#!/usr/bin/python
########################################################################
#
# File Name:            GenerateRepository.py
#
# Documentation:        http://docs.4suite.org/4ODS/GenerateRepository.py.html
#

#Desc.
#WWW: http://4suite.org/4ODS        e-mail: support@4suite.org

#Copyright (c) 1999 Fourthought, Inc., USA.   All Rights Reserved.
#See  http://4suite.org/COPYRIGHT  for license and copyright information

import os, string


from Ft.Ods.MetaData import MetaKind, DefiningScope, CollectionKind
import GenerateModule, GenerateInterface, GenerateStructure, GenerateEnumeration, GenerateConstant, GenerateUnion, GenerateCollection, GenerateException

from Ft.Ods.Exception import FtodsUnsupportedError, FtodsGenerationError, FtodsUnknownError


def GenerateStubs(params,repo):
    #Make sure the basedir exists
    if not os.path.exists(params.baseDir):
        raise FtodsGenerationError(msg="Base Dir %s must exist" % params.baseDir)

    if os.path.isfile('ODSGlobalDef.py'):
        os.unlink('ODSGlobalDef.py')


    #Go through the metamodel to get a list of things that need to be generated
    mods = repo._4ods_getGeneratedMetaClasses()


    modules = []
    klasses = []
    others = []
    for m in mods:
        if m.meta_kind == MetaKind.mk_module:
            modules.append(m)
        elif m.meta_kind in [MetaKind.mk_class,
                             MetaKind.mk_interface]:
            klasses.append(m)
        else:
            others.append(m)


    for m in modules:
        fileName = GetGeneratedFileName(params.baseDir,m)
        GenerateModule.GenerateStubs(m,params,fileName)

    for k in klasses:
        fileName = GetGeneratedFileName(params.baseDir,k)
        params.curPackage = GetCurPackage(params.basePackage,fileName)
        GenerateInterface.GenerateStubs(k,params,fileName)

    sortedOthers = []
    for o in others:
        fileName,cName,toSet = GetGeneratedFileName(params.baseDir,o)
        params.curPackage = GetCurPackage(params.basePackage,fileName)
        sortedOthers.append((o,fileName,cName,toSet))

    sortedOthers.sort(CompareOthers)

    for o,fileName,cName,toSet in sortedOthers:
        if o.meta_kind == MetaKind.mk_structure:
            GenerateStructure.GenerateStubs(o,params,fileName,cName,toSet)
        elif o.meta_kind == MetaKind.mk_enumeration:
            GenerateEnumeration.GenerateStubs(o,params,fileName,cName,toSet)
        elif o.meta_kind == MetaKind.mk_constant:
            GenerateConstant.GenerateStubs(o,params,fileName,cName,toSet)
        elif o.meta_kind == MetaKind.mk_union:
            GenerateUnion.GenerateStubs(o,params,fileName,cName,toSet)
        elif o.meta_kind == MetaKind.mk_collection:
            GenerateCollection.GenerateStubs(o,params,fileName,cName,toSet)
        elif o.meta_kind == MetaKind.mk_exception:
            GenerateException.GenerateStubs(o,params,fileName,cName,toSet)
        else:
            raise FtodsGenerationError(msg="Generating files for MetaKind: %s" % o.meta_kind)


    #params.curDir = params.baseDir
    #GenerateUtils.WalkDefinitions(repo,'GenerateStubs',params)

g_indexes = {MetaKind.mk_structure:0,
             MetaKind.mk_enumeration:1,
             MetaKind.mk_union:2,}

def CompareOthers(left,right):
    l = g_indexes.get(left[0].meta_kind,10)
    r = g_indexes.get(right[0].meta_kind,10)
    return cmp(l,r)


def GetGeneratedFileName(baseDir,repoObject):

    fields = string.split(repoObject.absolute_name(),'::')[1:]

    if repoObject.meta_kind in [MetaKind.mk_module,
                                ]:
        rel = string.join(fields,os.sep)
        fileName = os.path.join(baseDir,rel)
    elif repoObject.meta_kind in [MetaKind.mk_class,
                                  MetaKind.mk_interface
                                ]:
        fields[-1] = fields[-1] + '.py'
        rel = string.join(fields,os.sep)
        fileName =  os.path.join(baseDir,rel)
    elif repoObject.meta_kind in [MetaKind.mk_structure,
                                  MetaKind.mk_enumeration,
                                  MetaKind.mk_union,
                                  MetaKind.mk_exception,
                                ] \
                                or (repoObject.meta_kind == MetaKind.mk_collection and \
                                    repoObject.collection_kind == CollectionKind.ck_string and \
                                    not len(repoObject.type_defs)):
        if len(fields) == 0:
            #A private literal

            mo = repoObject._db.schema().resolve('ODLMetaObjects')
            path = mo._4ods_findTypePath(repoObject)

            #Get the last defining scope
            for ctr in range(len(path)):
                if not isinstance(path[ctr],DefiningScope.DefiningScope):
                    ctr = ctr - 1
                    break
            path = path[ctr:]

            #path[0] is the top level defining scope, use its file
            fName = GetGeneratedFileName('',path[0])

            #We have to set the private stuff as well!!!
            set = (string.join(map(lambda x:x.name,path[1:-1]),'.'),path[-1].name)
            if path[0].meta_kind == MetaKind.mk_module:
                fName = os.path.join(fName,'__init__.py')

            cName = '_' + string.join(map(lambda x:x.name,path[:-1]),'_') + '_' + path[-1].name
        elif len(fields) == 1:
            fName = 'ODSGlobalDef.py'
            cName = fields[0]
            set = None
        else:
            p = repoObject.parent()
            if p.meta_kind == MetaKind.mk_module:
                fName = string.join(fields[:-1] + ['__init__.py'],os.sep)
                cName = repoObject.name
                set = None
            else:
                #Must be a class or interface, only other defining scope that can define structures and enums
                fName = string.join(fields[:-2] + [fields[-2]+'.py'],os.sep)
                cName = repoObject.name
                set = None

        fileName =  os.path.join(baseDir,fName), cName,set
    elif repoObject.meta_kind in [MetaKind.mk_constant,
                                  ]:
        if len(fields) == 1:
            fName = 'ODSGlobalDef.py'
            cName = repoObject.name
            set = None
        else:
            p = repoObject.parent()
            if p.meta_kind == MetaKind.mk_module:
                fName = string.join(fields[:-1] + ['__init__.py'],os.sep)
                cName = repoObject.name
                set = None
            else:
                #Must be a class or interface, only other defining scope that can define structures and enums
                fName = string.join(fields[:-2] + [fields[-2]+'.py'],os.sep)
                cName = repoObject.name
                set = None

        fileName =  os.path.join(baseDir,fName), cName,set
    elif repoObject.meta_kind in [MetaKind.mk_collection,
                                  ]:

        #This will be a collection that is the subtype of another collection
        #it must be formed with a type definition so untraverse the type definition

        mo = repoObject._db.schema().resolve('ODLMetaObjects')
        path = mo._4ods_findTypePath(repoObject)

        #Get the last defining scope
        for ctr in range(len(path)):
            if not isinstance(path[ctr],DefiningScope.DefiningScope):
                ctr = ctr - 1
                break

        path = path[ctr:]

        fName = GetGeneratedFileName('',path[0])

        set = path[-2].name

        if path[0].meta_kind == MetaKind.mk_module:
            fName = os.path.join(fName,'__init__.py')

        cName = '_' + string.join(map(lambda x:x.name,path[:-2]),'_') + '_' + path[-2].name

        fileName =  os.path.join(baseDir,fName), cName,set


    else:
        raise FtodsGenerationError("Generating files for MetaKind %s not supported" % repoObject.meta_kind)

    if type(fileName) == type(()):
        useName = fileName[0]
    else:
        useName = fileName
    
    if os.path.exists(useName + 'c'):
        os.unlink(useName + 'c')
    if os.path.exists(useName + 'o'):
        os.unlink(useName + 'o')
    return fileName

def GetCurPackage(basePackage,fileName):

    fields = string.split(fileName,os.sep)

    p = ''
    for f in fields[:-1]:
        if f == '.':
            continue
        p = p + f + '.'
    p = p [:-1]
    if basePackage:
        return basePackage + '.' + p
    else:
        return p
    

def GetPrivateLiteralPath(obj):
    
    if obj.parent():
        return [obj.parent(),obj]
    #This should only be 1 because it is private
    if len(obj.specifiers):
        #At a member or union case
        spec = obj.specifiers[0]
        if spec.meta_kind == MetaKind.mk_member:
            return GetPrivateLiteralPath(spec.structure_type) + [obj]
        else:
            raise FtodsUnknownError(msg="Not fully implemented")
    elif len(obj.constants):
        #At a constant type
        spec = obj.constants[0]
        return GetPrivateLiteralPath(spec) + [obj]
    elif len(obj.type_defs):
        #At a type def
        td = obj.type_defs[0]
        return GetPrivateLiteralPath(spec) + [obj]
    else:
        print obj.collections.value
        print obj.dictionaries.value
        print obj.unions.value
        print obj.operations.value
        print obj.properties.value
        print obj.type_defs.value
        raise FtodsUnknownError(msg="Not fully implemented")
        

