#!/usr/bin/env python

import sys
import optparse
import os
import tempfile
import tarfile
import urllib2
import shutil
import subprocess
#import zipfile


class DjenesisScript(object):
    VIRTUALENV_INSTALLATION_INSTRUCTIONS_URL="https://github.com/concentricsky/djenesis#virtualenv-installation"
    VIRTUALENVWRAPPER_PATH_DEFAULT="/usr/local/bin/virtualenvwrapper.sh"


    def main(self,*args):
        # Parse commandline arguments
        self._parse_commandline()

        if self.use_virtualenvwrapper:
            self._virtualenvwrapper("mkvirtualenv", self.virtualenvwrapper_name)
        else:
            #use --no-site-packages for old versions
            subprocess.call(["virtualenv", "--no-site-packages", "--distribute", self.virtualenv_directory])

        cached_template = None
        if self.template:
            # inflate template to temporary directory
            if any(self.template.startswith(scm) for scm in ('git+','svn+','hg+')):
                # template in a repo
                cached_template = self._inflate_template_from_repo()
            elif '://' in self.template:
                # template at a url
                cached_template = self._fetch_template_from_url()
                self._inflate_template_from_file(cached_template)
            else:
                # template at local path
                cached_template = self._inflate_template_from_file(self.template)


            if not self.dont_remove_scm:
                # remove .git, .hg or .svn files from template
                shutil.rmtree(os.path.join(self.output_directory, '.git'), ignore_errors=True)
                shutil.rmtree(os.path.join(self.output_directory, '.hg'), ignore_errors=True)
                for dirname, dirs, files in os.walk(self.output_directory):
                    if '.svn' in dirs:
                        shutil.rmtree(os.path.join(dirname, '.svn'), ignore_errors=True)

        # run django-admin.py startproject                
        self._env_pip("install", "Django")
        dirname = os.path.dirname(self.output_directory)
        projname = os.path.basename(self.output_directory)
        arguments = [
            os.path.join(self._bin_directory(), "django-admin.py"), 
            "startproject"
        ]
        if cached_template:
            arguments.append("--template=%s" %(cached_template,))
        arguments.append(projname)
        subprocess.call(arguments, cwd=dirname)

        # install requirements.txt into the virtualenv
        self._install_requirements()

        #cleanup 
        if cached_template and os.path.exists(cached_template):
            shutil.rmtree(cached_template)

    def _parse_commandline(self):
        parser = optparse.OptionParser(usage="Usage: %prog [options] <project_name> [template]")
        parser.add_option("-e", "--virtualenv", help="Specify the path to create the virtualenv")
        parser.add_option("-i", "--initialize", action='store_true', default=False, help="Initialize from an existing project (dont remove scm files)")
        parser.add_option("-w", "--use-virtualenvwrapper", action='store_true', default=False, help="use 'mkvirtualenv' and 'workon' from virtualenvwrapper")
        parser.add_option("--virtualenvwrapper-name", help="the name of the virtualenvwrapper environment to use (defaults to project_name)")
        parser.add_option("--virtualenvwrapper-path", default=DjenesisScript.VIRTUALENVWRAPPER_PATH_DEFAULT, help="the path to the virtualenvwrapper")

        (options, args) = parser.parse_args()
        if len(args) < 1:
            parser.print_help()
            sys.exit(1)

        self.template = None
        self.output_directory = self._real_path(args.pop(0))
        self.project_name = os.path.basename(self.output_directory)

        if len(args) > 0:
            self.template = args.pop(0)
        if self.template is None:
            self.template = os.environ.get('DJENESIS_DEFAULT_TEMPLATE', None)

        self.pip_packages = args

        self.dont_remove_scm = getattr(options, 'initialize', False)

        self.virtualenv_directory = getattr(options, 'virtualenv', None)

        self.use_virtualenvwrapper = ('DJENESIS_VIRTUALENVWRAPPER' in os.environ) or getattr(options, 'use_virtualenvwrapper', None)
        self.virtualenvwrapper_name = getattr(options, 'virtualenvwrapper_name', None)
        if self.virtualenvwrapper_name is None:
            self.virtualenvwrapper_name = self.project_name
        self.virtualenvwrapper_path = os.environ.get('DJENESIS_VIRTUALENVWRAPPER_PATH', DjenesisScript.VIRTUALENVWRAPPER_PATH_DEFAULT)
        options_wrapper_path = getattr(options, 'virtualenvwrapper_path', None)
        if options_wrapper_path != None:
            self.virtualenvwrapper_path = options_wrapper_path
        self.workon_home = os.environ.get('WORKON_HOME', None)

        if self.use_virtualenvwrapper:
            if not os.path.exists(self.virtualenvwrapper_path):
                self.use_virtualenvwrapper = False
                print("Could not find virtualenvwrapper.sh. Is mkvirtualenv installed?\nFalling back to virtualenv...")
            if self.workon_home is None:
                self.use_virtualenvwrapper = False
                print("WORKON_HOME environment variable not found. Is mkvirtualenv installed?\nFalling back to virtualenv...")
            elif not os.path.exists(self.workon_home):
                os.makedirs(self.workon_home)

        if self.virtualenv_directory is None:
            self.virtualenv_directory = self._real_path(os.path.join(os.path.dirname(self.output_directory), 'env-'+os.path.basename(self.output_directory)))
        if not self._has_virtualenv():
            print("Could not find virtualenv! Please install it before continuing.\nSee %s for installation instructions." % (VIRTUALENV_INSTALLATION_INSTRUCTIONS_URL))

    def _has_virtualenv(self):
        try:
            subprocess.call(["virtualenv", "--version"], stdout=subprocess.PIPE)
            return True
        except OSError as e:
            pass
        return False

    def _virtualenvwrapper(self, *args):
        if not os.path.exists(self.virtualenvwrapper_path) or not self.workon_home:
            return -1
        args = ["bash -c 'source %s; %s'" % (self.virtualenvwrapper_path, ' '.join(args))]
        return subprocess.call(args, shell=True)

    def _inflate_template_from_repo(self):
        temp_directory = tempfile.mkdtemp()
        scm, url = self.template.split('+')
        if scm == 'git':
            if '#' in url:
                url, branch = url.split('#',1)
                call_args = ["git", "clone", "-b", branch, url, self.output_directory]
            else:
                call_args = ["git", "clone", url, temp_directory]
        elif scm == 'svn':
            call_args = ["svn", "checkout", url, temp_directory]
        elif scm == 'hg':
            call_args = ["hg", "clone", url, temp_directory]
        subprocess.call(call_args)
        return temp_directory

    def _fetch_template_from_url(self):
        temp_file = tempfile.mktemp()

        # fetch URL and cache it locally
        uh,fh = None,None
        try:
            uh = urllib2.urlopen(self.template)
            fh = open(temp_file, 'w+')
            fh.write(uh.read())
        except:
            pass
        finally:
            if fh:
                fh.close()
            if uh:
                uh.close()

        if temp_file:
            temp_file = self._inflate_template_from_file(temp_file)

        return temp_file

    def _inflate_template_from_file(self, local_path):
        # try to extract to code directory
        if not os.path.exists(local_path):
            return False

        # try treating it like a directory
        if os.path.isdir(local_path):
            shutil.copytree(local_path, self.output_directory, symlinks=True)
            return True

        # try treating it like a *.tgz or *.tar
        try:
            tar = tarfile.open(local_path)
            tar.extractall(path=self.output_directory)
            tar.close()
            return True
        except tarfile.ReadError as e:
            pass

        # try treating it like a *.zip
        # THERE IS A BUG IN PYTHON RELATED to zipfile.ZipFile, see: http://bugs.python.org/issue4710
        # try:
        #     zf = zipfile.ZipFile(local_path)
        #     zf.extractall(path=self.output_directory)
        #     zf.close()
        #     return True
        # except zipfile.BadZipfile:
        #     pass



    def _install_requirements(self):
        template_requirements = os.path.join(self.output_directory, 'requirements.txt')
        if os.path.exists(template_requirements):
            self._env_pip("install", "-r", template_requirements)

        if len(self.pip_packages) > 0:
            self._env_pip("install", *self.pip_packages)

    def _real_path(self, s, cwd='.'):
        return os.path.abspath(os.path.join(cwd, s))

    def _bin_directory(self):
        directory = self.virtualenv_directory
        if self.use_virtualenvwrapper:
            directory = os.path.join(self.workon_home, self.virtualenvwrapper_name)
        return os.path.join(directory, 'bin',)

    def _env_pip(self, *args):
        subprocess.call([os.path.join(self._bin_directory(), 'pip')] + list(args))





if __name__ == '__main__':
    script = DjenesisScript()
    script.main(*sys.argv)
