# This file is part of the Coral project.
# Please read the copyright notice in the LICENSE file.

from coral.lgen.codeartifact import CodeArtifact
import os

class FileObject:
    """One fileobject per file. Note that you can have multiple streams
    in the file."""
    def __init__(self, filename):
        self.filename = filename
        # Hash of streamname => CodeArtifact
        # Must be streamed out in the order given by
        # CodeGenerator.getCodeArtifactOrder()
        # BUG only one Language possible with this...
        self.codeartifacts = {}
        # stream => hash of purposes already done for this stream
        self.purposes = {}

    def hasPurpose(self, stream, purpose):
        try:
            s = self.codeartifacts[stream]
        except:
            return 0

        try:
            p = self.purposes[s]
        except:
            return 0

        return p.has_key(purpose)

    def addPurpose(self, stream, purpose):
        s = self.getStream(stream)

        try:
            p = self.purposes[s]
        except:
            p = {}
            self.purposes[s] = p
        
        p[purpose] = 1
        
    def setStream(self, name, ca):
        assert not self.codeartifacts.has_key(name)
        self.codeartifacts[name] = ca
        
    def getStream(self, stream, purpose=None):
        """Returns a (possibly old) CodeArtifact for the stream.
        @purpose is e.g. a string which tells what this addition is for.
        NOTE that if the same addition has already been done, getStream
        returns None! This is useful so you can add some stuff _once_."""
        if purpose:
            if self.hasPurpose(stream, purpose):
                return None
            self.addPurpose(stream, purpose)
        try:
            return self.codeartifacts[stream]
        except:
            pass
        ca = CodeArtifact()
        self.codeartifacts[stream] = ca
        return ca

class CodeGenerator:
    def __init__(self, topleveldir):
        # Top-level directory of project
        self.topleveldir = topleveldir
        # Hash of filename => FileObject
        # Filename is relative to topleveldir
        self.codes = {}

    def getFilename(self, filename, relativeFileObject = None):
        """Returns a (possibly old) FileObject for filename, relative
        to the top of the project. If relativeFileObject is nonnull
        then the filename is interpreted relative to that FO."""
        if relativeFileObject != None:
            filename = self.topleveldir + os.sep + relativeFileObject.filename + os.sep + filename
        else:
            filename = self.topleveldir + os.sep + filename
        return filename

    def getFileObject(self, filename):
        try:
            return self.codes[filename]
        except:
            pass

        fo = FileObject(filename)
        self.codes[filename] = fo
        
        return fo

    def getFile(self, name, relativeObject = None):
        return self.getFileObject(self.getFilename(name, relativeObject))

    def streamOut(self):
        for filename in self.codes.keys():
            assert filename == self.codes[filename].filename
            parts = filename.split(os.sep)
            parts = [self.normStrFile(i) for i in parts ]

            if len(parts) > 1:
                try:
                    os.makedirs(os.sep.join(parts[:-1]))
                except OSError:
                    # Ignore, since they could already exist...
                    pass
            fileobject = self.codes[filename]
            cas = fileobject.codeartifacts
            order = self.getCodeArtifactOrder(fileobject)
            # BUG could be more elaborate
            # with replacing old files with new ones...
            fd = file(os.sep.join(parts), "wb")
            try:
                for i in order:
                    if cas.has_key(i):
                        try:
                            cas[i].beforeDump()
                        except:
                            pass
                        cas[i].dump_fd(fd)
            finally:
                fd.close()

    # Only required at the latest stage.

    def getCodeArtifactOrder(self, fileobject):
        """Returns list of strings of the order of streams.
        Can be overridden."""
        return [
            "prelude", \
            "imports", \
            "globals", \
            "private", \
            "declarations", \
            "inline", \
            "definitions", \
            "main", \
            "postlude", \
            ]

    # Generator mechanisms
    
    def generateCode(self, root_set):
        """Our humorous interface."""
        assert 0


    # Slave routines

    def normStrFile(self, s):
        """Normalize string to filename."""
        assert 0

    def normStrVariable(self, s):
        """Normalize string to variable name."""
        assert 0

