#   Programmer: limodou
#   E-mail:     limodou@gmail.com
#
#   Copyleft 2006 limodou
#
#   Distributed under the terms of the GPL (GNU Public License)
#
#   NewEdit is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#   $Id$

import os
import sys
import shutil
import traceback
import csv
import StringIO

__version__ = 0.1
__author__ = 'limodou'

input_vars = {
    'root_path':'dir',
    'django_project_name':'string',
    'django_project_settings':'file',
}

scripts = [
    #action  parameters
#    ('edit settings INSTALLED_APPS %(project_name)s.test'),
#    ('edit urlconf  r"^admin/"  include("django.contrib.admin.urls")'),
    #action django-admin.py command
#    ('command init app'),
    #action sourcefile destfile/destdir
#    ('copy sourcefile destfile/destdir'),
    #action directory
#    ('cd path'),
    #action directory
#    ('mkdir path'),
    #action sourcedir, destdir
#    ('copytree sourcedir destdir'),
    #action filename
#    ('mkfile', 'filename'),
]

commands = {}

def register(name, func):
    commands[name] = func

def install(vars, scripts, func=None):
    """according to the dict parameters install a app
    you can defina a func to deal with the script, if the func reture Ture, install()
    will not continue current instruction, False will continue. The func process will be the first.
    """
    #set from path
    cd(vars)
    vars['from_path'] = os.path.dirname(os.path.abspath(sys.argv[0]))
    #first go into django_project_path
    for line in scripts:
        if isinstance(line, (list, tuple)):
            text = line[0]
            extra = list(line[1:])
        else:
            text = line
            extra = []
        buf = StringIO.StringIO(text)
        reader = csv.reader(buf, delimiter=' ', skipinitialspace=True)
        i = list(reader)[0] + extra
        def _e(i, vars=vars):
            return _expand(vars, i)
        i = map(_e, i)
        action, args = i[0], i[1:]
        if func:
            parameter = list(i)
            ret = func(parameter)
            if ret:
                continue
            else:
                action, args = parameter[0], parameter[1:]
        
        action = 'do_%s' % action
        if commands.has_key(action):
            function = commands[action]
            try:
                ret = function(vars, args)
                if not ret:
                    print "Error  : %s" % _repr(i)
                else:
                    print 'Success: %s' % _repr(i)
            except:
                traceback.print_exc()
                print 'There is something wrong as installing, process exit!'
                sys.exit(1)
        else:
            print 'Error: Can not found process handler [%s]' % action
            sys.exit(1)

def _repr(args):
    s = []
    for i in args:
        if isinstance(i, str):
            s.append(i)
        else:
            s.append(repr(i))
    return ' '.join(s)

def _expand(vars, value):
    if isinstance(value, (str, unicode)) and '%' in value:
        value = value % vars
    return value

def cd(vars):
    if os.path.exists(vars['project_path']):
        do_cd(vars, [vars['project_path']])

def do_edit(vars, args):
    parse_settings(vars)
    f, option_name, value = args
    if f == 'settings':
        vs = vars['ini'][option_name]
        if isinstance(vs, list):
            if not value in vs:
                vars['ini'][option_name].append(value)
                vars['ini'].save()
        else:
            vars['ini'][option_name] = value
            vars['ini'].save()
        return True
    elif f == 'urlconf':
        vars['urls'].add(option_name, value)
        vars['urls'].save()
        return True
    else:
        print ("Only 'settings' and 'urlconf' can be used for action 'modify', \n"
            "cannot recogonize [%s]" % f)
        return False
register('do_edit', do_edit)
    
def do_do(vars, args):
    if args[0] == 'startproject':
        cmd = 'django-admin.py '
    else:
        cmd = 'manage.py '
    os.system(cmd + ' '.join(args))
    return True
register('do_do', do_do)

def do_copy(vars, args):
    spath, dpath = args
    import glob
    
    filelist = glob.glob(spath)
    for f in filelist:
        shutil.copy(f, dpath)
    return True
register('do_copy', do_copy)

def do_cd(vars, args):
    os.chdir(args[0])
    return True
register('do_cd', do_cd)
    
def do_mkdir(vars, args):
    if not os.path.exists(args[0]):
        os.makedirs(args[0])
    return True
register('do_mkdir', do_mkdir)

def do_copytree(vars, args):
    my_copytree(args[0], args[1])
    return True    
register('do_copytree', do_copytree)
        
def do_mkfile(vars, args):
    file(args[0], 'w').close()
    return True    
register('do_mkfile', do_mkfile)

def parse_command_line(args):
    from optparse import OptionParser
    
    usage = "usage: %prog [options]"
    parser = OptionParser(usage, version="%prog " + str(__version__))
    parser.add_option("-c", "--config", dest="config", metavar="CONFIG FILE",
                      help="Django project configure file")
    parser.add_option("-r", "--root", dest="rootpath", metavar="ROOT PATH",
                      help="Django project root path")
    parser.add_option("-s", "--settings", dest="settings", metavar="SETTINGS FILE",
                      help="Django project settings file")
    parser.add_option("-n", "--name", dest="name", metavar="PROJECT NAME",
                      help="Django project name")

    (options, args) = parser.parse_args()
    
    input_vars['root_path'] = ''
    input_vars['django_project_name'] = ''
    input_vars['django_project_settings'] = ''
    
    #first using config file
    if not options.config:
        config = 'config.ini'
    else:
        config = options.config
    if config:
        if not os.path.exists(config):
            print 'Config file [%s] is not existed, please check again!' % config
            sys.exit(1)
            
        for line in file(config):
            try:
                k, v = line.strip().split('=')
                k, v = k.strip(), v.strip()
                input_vars[k] = v
            except:
                raise
          
    #then using command line parameter
    if input_vars['django_project_name'] and options.name:
        input_vars['django_project_name'] = options.name
    if input_vars['root_path'] and options.name:
        input_vars['root_path'] = options.rootpath
    if input_vars['django_project_settings'] and options.settings:
        input_vars['django_project_settings'] = options.settings
        
    #using default
    if not input_vars['root_path'] and not input_vars['django_project_name'] \
        and not input_vars['django_project_settings']:
        path = os.path.dirname(sys.argv[0])
        input_vars['root_path'] = os.path.dirname(os.path.dirname(path)) 
        input_vars['django_project_name'] = os.path.basename(os.path.dirname(path)) 
        input_vars['django_project_settings'] = 'settings.py'

    input_vars['project_name'] = os.path.basename(input_vars['django_project_name'])
    input_vars['project_path'] = _get_path('')
        
    return input_vars
    
def parse_settings(vars):
    settings = _get_path(input_vars['django_project_settings'])
    if not os.path.exists(settings):
        print 'Warning: Settings file [%s] is not existed!' % settings
    else:
        from DjangoIni import DjangoIni
        x = DjangoIni(settings)
        input_vars['ini'] = x
        input_vars['MEDIA_ROOT'] = x['MEDIA_ROOT']
        input_vars['TEMPLATE_DIRS'] = x['TEMPLATE_DIRS']
        
        urls = _get_path(x['ROOT_URLCONF'].split('.', 1)[1].replace('.', '/') + '.py')
        if not os.path.exists(urls):
            print 'Warning: URLConf file [%s] is not existed!' % urls
        from DjangoUrlConf import URLConf
        input_vars['urls'] = URLConf(urls)

def _get_path(path):
    if path:
        return os.path.join(input_vars['root_path'], input_vars['django_project_name'], path)
    else:
        return os.path.join(input_vars['root_path'], input_vars['django_project_name'])

def _get_from_commandline(message):
    if __name__ == '__main__':
        s = raw_input(message)

def my_copytree(src, dst, symlinks=False):
    """Recursively copy a directory tree using copy2().

    Modified from shutil.copytree

    """
    names = os.listdir(src)
    if not os.path.exists(dst):
        os.mkdir(dst)
    errors = []
    for name in names:
        if name and (name[0] == '.' or name == 'CVS'):
            continue
        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        try:
            if symlinks and os.path.islink(srcname):
                linkto = os.readlink(srcname)
                os.symlink(linkto, dstname)
            elif os.path.isdir(srcname):
                my_copytree(srcname, dstname, symlinks)
            else:
                shutil.copy2(srcname, dstname)
            # XXX What about devices, sockets etc.?
        except (IOError, os.error), why:
            errors.append((srcname, dstname, why))
    if errors:
        raise shutil.Error, errors

def main(scripts):
    vars = parse_command_line(sys.argv)
    install(vars, scripts=scripts)   
    
if __name__ == '__main__':
    main(scripts)
    
    