# -*- encoding: UTF-8 -*-

#    Pyjama is a python project bootstraper for Maemo5 development
#
#    Copyright (C) 2011  Thierry Bressure
#
#    This program 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 3 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, see <http://www.gnu.org/licenses/>

'''
Created on Jan 6, 2012

@author: maemo
'''

import os
import shutil
import logging
import string
import gtk

from ..common import version 

version.getInstance().submitRevision("$Revision: 131 $")


GPL_LICENCE = """This program 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 3 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, see
<http://www.gnu.org/licenses/>
"""


def not_yet_implemented():
    raise NotImplementedError()

class Settings():
    '''
    Represents the settings for Gnatirac
    '''
   
    def __init__(self):
        pass

class Project():
    '''
    Project informations
    '''
            
    
    def __init__(self, type="default",
                  name="pyjama",
                  author=os.environ['LOGNAME'],
                  summary="pyjama generated project",
                  description="This a project generated by Pyjama with default layout" ,
                  inception=None,
                  logo_uri=None,
                  version="0.1.0"):
        self.type = type
        self.name = name
        self.author = author
        self.summary = summary
        self.description = description
        self.version = version
        if not inception:
            from datetime import date
            inception = date.today().year
        self.inception = inception
        self.logi_uri=logo_uri        
        self.file_encoding="-*- encoding: UTF-8 -*-" # python syntax to define source file encoding
        self.copyright = None
        self.header=None
        self.licence=GPL_LICENCE
        from datetime import datetime
        self.date_time = datetime.today().strftime("%d %B %Y %H:%M:%S")        
        self._generate_copyright()        
        self._generate_header() # generate full pythoh source file header        

    def _generate_copyright(self):
        copyleft = Copyright()
        SourceTransformer().process(copyleft, self, None) # no output dir because in memory processing
        self.copyright = copyleft.get_content()
        
    def _generate_header(self):
        header = Header()
        SourceTransformer().process(header, self, None) # no output dir because in memory processing
        self.header = header.get_content()
        
    def generate(self, output):
        '''
        Generate this project description.
        Parameter:
            - ouptut: the path where to generate the project named directory which contains project source 
        '''
        DefaultGenerator(self).generate(os.path.realpath(os.path.join(output,self.name)))
    
        


class IVisitableTemplate():
    '''
    An object that can be visited
    '''
    def acceptTransformer(self, visitor):
        '''
        Call this method to receive a visitor 
        '''
        not_yet_implemented()


class ITemplate():
    '''
    A template that can be modify by Transformer to generate project skeleton 
    '''
    
    def set_output(self, output):
        '''
        Modify the output i.e the location of the generated file. 
        Parameter:
            - ouput is a pathname file
        '''
        not_yet_implemented()

    def get_input(self):
        '''
        Return the location of the template. This is a pathname file
        '''
        not_yet_implemented()
    
    def get_content(self):
        '''
        Return the current value of the template.
        This value may have been modify by some Transformer since the template instanciation
        '''
        not_yet_implemented()
    
    def set_content(self, content):
        '''
        Modify the content of the template.
        '''
        not_yet_implemented()
        
    def generate(self):
        '''
        Generate the template using current content and current output        
        '''
        not_yet_implemented()
    

class Template(ITemplate, IVisitableTemplate):
    '''
    Base implementation of any template
    '''
    def __init__(self, id):
        logging.debug("instanciating %s id=%s" % ("Template", str(id)))
        self.id = id
        self.output = None        
        self.content = None

    def get_content(self):
        return self.content


    def set_content(self, content):
        self.content = content


    def set_output(self, output):
        self.output = output

    def _ensure_destination(self):
        dir_output = os.path.dirname(self.output)
        if not os.path.isdir(dir_output):
            os.makedirs(dir_output)
                
        
    
    
class MemoryTemplate(Template):
    '''
    A template based on memory value
    '''
    
    def __init__(self, id, value):
        Template.__init__(self, id)
        self.content = value

    def generate(self):
        # copy the content
        logging.debug("generating %s" % (self.output))
        self._ensure_destination()
        f = open(self.output,"wb")
        f.write(self.content)     
    
    def set_relative_output_file(self, root_output, rel_output_file):
         self.set_output(os.path.join(root_output, rel_output_file))   


class TextMemoryTemplate(MemoryTemplate):
    '''
    A memory template based on a text
    '''
    def __init__(self, id, value):
        MemoryTemplate.__init__(self, id, value)

    def acceptTransformer(self, visitor):
        return visitor.visit_TextMemoryTemplate(self)


         
class FileTemplate(Template):
    '''
    A template which is based on a file  
    '''
    
    def __init__(self, root, file):
        Template.__init__(self, file)            
        self.relative_input_file = "." + os.sep + file.partition(root)[2]      
        
    def _load_file_content(self):
        # load the text file content
        f =  open(self.get_input())
        self.set_content(f.read())
    
    def get_input(self):
        '''
        Return the template file input
        '''
        return self.id    
    
    def get_relative_input_file(self):
        return self.relative_input_file
    
    def set_relative_input_file(self, rel_input_file):
        self.relative_input_file = rel_input_file
            
    def set_relative_output_file(self, root_output, rel_output_file=None):
        '''
        Défine the output file of this template.
        Parameters:
            - root_output : the output dir
            - rel_output (optional) : the relative ouput file. Default: relative input file
        '''
        if not rel_output_file:
            rel_output_file = self.relative_input_file
        self.set_output(os.path.join(root_output, rel_output_file))                

    def generate(self):
        '''
        Copy input file to output file
        '''
        logging.debug("generating %s" % (self.output))
        self._ensure_destination()
        if self.content:
            # copy the content
            f = open(self.output,"wb")
            f.write(self.content)                
        else:
            shutil.copy(self.get_input(), self.output)


    def acceptTransformer(self, visitor):
        '''
        Tell the visitor to handle a FileTemplate
        '''
        return visitor.visit_fileTemplate(self)
        
    
    

class TextFileTemplate(FileTemplate):
    '''
    A file template representing text file (python source file...)
    '''
    def __init__(self, root, file):
        FileTemplate.__init__(self, root, file)
        self._load_file_content()

    def acceptTransformer(self, visitor):
        return visitor.visit_textFileTemplate(self)

    


class ImageFileTemplate(FileTemplate):
    '''
    A file template representing an image file.
    content attribute is a pixbuf
    '''
    def __init__(self, root, file):
        FileTemplate.__init__(self, root, file)
        self._load_file_content()
    
    def _load_file_content(self):
        # load the text file content
        f = open(self.get_input())
        self.set_content(gtk.gdk.pixbuf_new_from_file(self.get_input()))
        
    
    def generate(self):
        '''
        Copy input file to output file
        '''
        self._ensure_destination()
        if self.content:
            # copy the content
            if self.output.endswith(".png"):
                type = "png"
            elif self.output.enswith(".jpg"):
                type = "jpeg"
            else:
                type = "jpeg"
            self.content.save(self.output, type)                
        else:
            shutil.copy(self.get_input(), self.output)

class TemplateFactory():
    '''
    Factory of template according to filename
    '''
    
    TEXT_FILE_EXTENSION = [".txt", ".py", ".cfg", ".in", ".desktop"]
    
    IMAGE_FILE_EXTENSION = [".png", ".jpg"]
    
    def get_template(self, root, file):
        '''
        Return a template instance
        '''
        
        for extension in self.TEXT_FILE_EXTENSION:
            if file.endswith(extension):
                return TextFileTemplate(root, file)
        
        for extension in self.IMAGE_FILE_EXTENSION:
            if file.endswith(extension):
                return ImageFileTemplate(root, file)

        return FileTemplate(root, file)

class Copyright(TextMemoryTemplate, IVisitableTemplate):
    '''
    Copyright information
    '''            
    def __init__(self):
        MemoryTemplate.__init__(self, "Copyright",
                                value="Copyright (C) $INCEPTION  $AUTHOR")

class Header(TextMemoryTemplate, IVisitableTemplate):    
    '''
    Header of each source file. This portion of text before
    file specific information
    '''                      
    def __init__(self):
        MemoryTemplate.__init__(self, "Header",
                                value="""$FILE_ENCODING

$DESCRIPTION

$COPYRIGHT

$LICENCE
""")

    def acceptTransformer(self, visitor):
        return visitor.visit_HeaderTemplate(self)

    
        
          
class Licence(TextMemoryTemplate, IVisitableTemplate):
    '''
    The GNU Licence template
    '''
    
    def __init__(self):
        MemoryTemplate.__init__(self, "GNU Licence",
                                value=GPL_LICENCE)
       
        
        
class IVisitorTransformer():
    '''
    Visitor interface for Transfomer class
    '''        
    def visit_fileTemplate(self, template):
        '''
        handle visited FileTemplate
        '''
        not_yet_implemented()
        
    def visit_imageFileTemplate(self, template):
        '''
        handle visited ImageFileTemplate
        '''
        not_yet_implemented()
        
    def visit_textFileTemplate(self, template):
        '''
        handle visited textFileTemplate
        '''
        not_yet_implemented()
        
    def visit_HeaderTemplate(self, template):
        '''
        Handle visited HeaderTemplate
        '''
        not_yet_implemented()
        
    def visit_TextMemoryTemplate(self, template):
        '''
        Handle visited TextMemroyTemplate
        '''
        not_yet_implemented()
        
class ITransformer():
    '''
    Interface for Transformer class
    '''
    def process(self, template, project, root_output_dir):
        '''
        return a list of modified template
        ''' 
        not_yet_implemented()
        
class Transformer(ITransformer, IVisitorTransformer):
    
    
    def process(self, template, project, root_output_dir=None):
        self.project = project
        self.root_output_dir = root_output_dir
        return template.acceptTransformer(self)
    
    def _return_no_template(self):
        return []


class SourceTransformer(Transformer):

   

    # KEYWORD CONSTANTS
    HEADER = 'HEADER'
    COPYRIGHT = 'COPYRIGHT'
    DESCRIPTION = 'DESCRIPTION'
    SUMMARY = 'SUMMARY'
    AUTHOR = 'AUTHOR'
    LICENCE = 'LICENCE'
    FILE_ENCODING = 'FILE_ENCODING'
    NAME = 'NAME'
    INCEPTION = 'INCEPTION'
    VERSION = 'VERSION'
    DATE_TIME = 'DATE_TIME'
    
    
        
       
    def get_project_dico(self):
        dico = dict()
        dico[self.HEADER] = self.project.header
        dico[self.COPYRIGHT] = self.project.copyright
        dico[self.DESCRIPTION] = self.project.description
        dico[self.SUMMARY] = self.project.summary
        dico[self.AUTHOR] = self.project.author
        dico[self.LICENCE] = self.project.licence
        dico[self.FILE_ENCODING] = self.project.file_encoding
        dico[self.NAME] = self.project.name
        dico[self.NAME + "_CAP"] = self.project.name.capitalize()
        dico[self.INCEPTION] = self.project.inception
        dico[self.VERSION] = self.project.version
        dico[self.DATE_TIME] = self.project.date_time
        
        return dico       
    
    def _substitute_source(self, template, *argv, **kwarg):
        '''
        Transform the template with given keywords values
        '''
        template.set_content(string.Template(template.get_content()).safe_substitute(**kwarg))
        
    def _apply_project_defintion(self, template):
        '''
        Apply project definition on the template        
        '''
        d = self.get_project_dico()
        
        self._substitute_source(template, **d)

    def visit_fileTemplate(self, template):
        if template.get_relative_input_file().find("scripts/pyjama") > -1:
            # this file i snot detected as a text file so we manualy load its content 
            template._load_file_content()
            self._apply_project_defintion(template)          
            return [template]
        
        return self._return_no_template()


    def visit_imageFileTemplate(self, template):
        return self._return_no_template()


    def visit_textFileTemplate(self, template):
        self._apply_project_defintion(template)
        return [template]

    def visit_HeaderTemplate(self, template):
        self._apply_project_defintion(template)
        # header must be commented
        CommentOutTransformer().process(template, self.project, self.root_output_dir)
        return [template]        

    def visit_TextMemoryTemplate(self, template):
        self._apply_project_defintion(template)
        return [template]




 
    
class FilenameTransformer(Transformer):
    '''
    Apply transformation on file name
    '''

    def visit_fileTemplate(self, template):
        if template.get_relative_input_file().find("scripts/pyjama") > -1:
            template.set_relative_input_file(template.get_relative_input_file().replace("pyjama",self.project.name))            
            return [template]
        
        return self._return_no_template()


    def visit_imageFileTemplate(self, template):
        if template.get_relative_input_file().find("media/pyjama.png") > -1 or \
            template.get_relative_input_file().find("hildon/icons/48x48/pyjama.png") > -1 or \
            template.get_relative_input_file().find("hildon/icons/64x64/pyjama.png") > -1 or \
            template.get_relative_input_file().find("src/pyjama-logo.png") > -1:      
                template.set_relative_input_file(template.get_relative_input_file().replace("pyjama",self.project.name))
                return [template]
        return self._return_no_template()


    def visit_textFileTemplate(self, template):
        if template.get_relative_input_file().find("hildon/pyjama.desktop") > -1:
            template.set_relative_input_file(template.get_relative_input_file().replace("pyjama",self.project.name))            
            return [template]
        if template.get_relative_input_file().find("pyjama") > -1:
            template.set_relative_input_file(template.get_relative_input_file().replace("pyjama",self.project.name))            
            return [template]
        return self._return_no_template()

    def visit_HeaderTemplate(self, template):
        return self._return_no_template()


    def visit_TextMemoryTemplate(self, template):
        return self._return_no_template()





class CommentOutTransformer(Transformer):
    '''
    Comment out the processed template
    '''

    def visit_HeaderTemplate(self, template):
        result = ""        
        # read each line
        for line in template.get_content().splitlines():
            commented = "# " + line + "\n"
            result = result + commented
        template.set_content(result)
        return [template]
   

    

class LogoTransformer(Transformer):
    '''
    Apply transforamtion on image logo.
    '''

    def visit_fileTemplate(self, template):
        return self._return_no_template()


    def visit_imageFileTemplate(self, template):
        return self._return_no_template()


    def visit_textFileTemplate(self, template):
        return self._return_no_template()


    def visit_HeaderTemplate(self, template):
        return self._return_no_template()


    def visit_TextMemoryTemplate(self, template):
        return self._return_no_template()


class CopyTransformer(Transformer):
    '''
    Simply copy the template to the output path preserving input relative path
    '''

    def visit_fileTemplate(self, template):
        return self._copy_to_output_dir(template)


    def visit_imageFileTemplate(self, template):
        return self._copy_to_output_dir(template)


    def visit_textFileTemplate(self, template):
        return self._copy_to_output_dir(template)


    def visit_HeaderTemplate(self, template):
        return self._return_no_template()


    def visit_TextMemoryTemplate(self, template):
        return self._return_no_template()

    def _copy_to_output_dir(self, template):
        template.set_relative_output_file(self.root_output_dir)
        return [template]

   
   

TRANSFORMERS_PIPELINE = [SourceTransformer(), FilenameTransformer(), LogoTransformer(), CopyTransformer()]

class Generator():
    
    def __init__(self, project):
        self.project = project
        self.templateFactory = TemplateFactory()

    def generateTemplatePool(self, output,  aTemplatePool ):
        '''
        Process and generate template
        Parameter:
            - output: the directory root output
            - aTemplatePool : list of Template
        '''
       
        for transformer in TRANSFORMERS_PIPELINE:
            processedByTransformer = map(lambda aTemplate: transformer.process(aTemplate, self.project, output), aTemplatePool)
            # make a flat list of Template
            map(lambda x: aTemplatePool.extend(x), processedByTransformer)
            # remove duplicate template
            aTemplatePool = [template for template in set(aTemplatePool)]
        
        map(lambda x: x.generate(), aTemplatePool)


    def generate(self, output):
        '''
        Common generation 
        '''
        licence = Licence()
        licence.set_relative_output_file(output, "LICENCE")
        aTemplatePool = [licence]
        self.generateTemplatePool(output, aTemplatePool)
        

class DefaultGenerator(Generator):
    '''
    This class can generate a project skeleton given a project définition
    '''
    
    def __init__(self,project):
        Generator.__init__(self, project)
                    
    
    def generate(self, output):
        logging.info("start generation into %s" % (output,))
                
        Generator.generate(self, output)
        
        from pkg_resources import resource_filename
        template_dir = resource_filename(__name__, '/template/default')
        logging.info("found template dir " + template_dir)
        import os
        from os.path import join
        for root, dirs, files in os.walk(template_dir):
            # ignoring .svn directory
            svn_dir = ".svn"
            if svn_dir in dirs:
                dirs.remove(svn_dir)
            
            # apply transformation on each file
            for aFile in files:
                aTemplatePool = [self.templateFactory.get_template(template_dir, os.path.join(root,aFile))]
                self.generateTemplatePool(output, aTemplatePool)
            
            