
# -*- mode:Python -*-

import sys
import os.path
import string
import traceback
import Hurricane
from   Hurricane import DbU
from   Hurricane import DataBase
from   Hurricane import Layer
from   Hurricane import BasicLayer
from   Hurricane import DiffusionLayer
from   Hurricane import TransistorLayer
from   Hurricane import RegularLayer
from   Hurricane import ContactLayer
from   Hurricane import ViaLayer
from   CRL       import AllianceFramework
from   helpers   import ErrorMessage


symbolicTechno = None
symbolicFile   = '<No symbolic file specified>'


class SymbolicLayerType ( object ):

    Regular     = 1
    Diffusion   = 2
    Transistor  = 3
    Contact     = 4
    Via         = 5

    def __init__ ( self, code ):
        self._code = code
        return

    def __int__ ( self ):
        return self._code

    def __str__ ( self ):
        if self._code == SymbolicLayerType.Regular:    return 'TypeRegular'
        if self._code == SymbolicLayerType.Diffusion:  return 'TypeDiffusion'
        if self._code == SymbolicLayerType.Transistor: return 'TypeTransistor'
        if self._code == SymbolicLayerType.Contact:    return 'TypeContact'
        if self._code == SymbolicLayerType.Via:        return 'TypeVia'
        return 'TypeUnknown (%d)', self._code

    def __repr__ ( self ):
        return str(self)

    def realLayerLength ( self ):
        if self._code == SymbolicLayerType.Regular:    return 1
        if self._code == SymbolicLayerType.Diffusion:  return 3
        if self._code == SymbolicLayerType.Transistor: return 4
        if self._code == SymbolicLayerType.Contact:    return 5
        if self._code == SymbolicLayerType.Via:        return 3
        return 0


TypeRegular     = SymbolicLayerType(SymbolicLayerType.Regular)
TypeDiffusion   = SymbolicLayerType(SymbolicLayerType.Diffusion)
TypeTransistor  = SymbolicLayerType(SymbolicLayerType.Transistor)
TypeContact     = SymbolicLayerType(SymbolicLayerType.Contact)
TypeVia         = SymbolicLayerType(SymbolicLayerType.Via)


class LayersLUT ( object ):

    Real         = 0x1
    Symbolic     = 0x2
    MissingError = 0x8

    def __init__ ( self ):
        self._realLayers     = {}
        self._symbolicLayers = {}
        return

    def add ( self, layer ):
        if isinstance(layer,BasicLayer): self._realLayers    [ layer.getName() ] = layer
        else:                            self._symbolicLayers[ layer.getName() ] = layer
        return

    def lookup ( self, name, flags=Real|Symbolic ):
        layer = None
        if flags & LayersLUT.Real and self._realLayers.has_key(name):
            layer = self._realLayers[name]
        if flags & LayersLUT.Symbolic and self._symbolicLayers.has_key(name):
            layer = self._symbolicLayers[name]

        if not layer and flags&LayersLUT.MissingError:
            raise ErrorMessage(1,['Layer <%s> is not defined (yet?).'%name])

        return layer


layersLUT = LayersLUT()


def loadRealLayers ( technology, basicLayersData ):
    entryNo = 0
    for entry in realLayersTable:
        entryNo += 1

        try:
            if len(entry) < 2:
                raise ErrorMessage(1,['Malformed entry in <realLayersTable>.'
                                     ,'Less than two fields: missing name and/or material.'
                                     ,str(entry)
                                     ])
            if len(entry) > 2:
                if entry[1] != BasicLayer.Material.blockage:
                    raise ErrorMessage(1,['Invalid entry in <realLayersTable>.'
                                         ,'Only blockage material can have a third field.'
                                         ,str(entry)
                                         ])
                routingLayer = technology.getBasicLayer(entry[2])
                if not routingLayer:
                    raise ErrorMessage(1,['Incoherency in <realLayersTable> entry at.'
                                         ,'The metal <%s> associated to the blockage doesn\'t exist (yet?).' % entry[2]
                                         ,str(entry)
                                         ])

            basicLayer = BasicLayer.create( technology
                                          , entry[0]
                                          , BasicLayer.Material(entry[1]) )
            layersLUT.add ( basicLayer )
            if len(entry) > 2:
                routingLayer.setBlockageLayer(basicLayer)

        except Exception, e:
            ErrorMessage.wrapPrint(e,'In %s:<symbolicLayersTable> at index %d.' % (symbolicFile,entryNo))
    return


def loadSymbolicLayers ( technology, symbolicLayersData ):
    entryNo = 0
    for entry in symbolicLayersData:
        entryNo += 1

        try:
            if len(entry) != 3:
                raise ErrorMessage(1,['Malformed entry in <symbolicLayersTable>.'
                                     ,'Must contains exactly three fields: ( name, type, (real_layers,) ).'
                                     ,str(entry)
                                     ])
            name, layerType, realLayers = entry

            if not isinstance(layerType,SymbolicLayerType):
                raise ErrorMessage(1,['Invalid entry in <symbolicLayersTable>.'
                                     ,'The layer type code is not valid, should be any of:'
                                     ,'    * TypeRegular'
                                     ,'    * TypeDiffusion'
                                     ,'    * TypeTransistor'
                                     ,'    * TypeContact'
                                     ,'    * TypeVia'
                                     ,str(entry)
                                     ])
            if layerType.realLayerLength() != len(realLayers):
                raise ErrorMessage(1,['Invalid entry in <symbolicLayersTable>.'
                                     ,'Layer of type <%s> contains %d real layers instead of %d.' \
                                          % (layerType,len(realLayers),layerType.realLayerLength())
                                     ,str(entry)
                                     ])

            realLayersArgs = []
            for layerName in realLayers:
                if layerName:
                    realLayersArgs += [ layersLUT.lookup(layerName
                                                        ,LayersLUT.Real
                                                        |LayersLUT.Symbolic
                                                        |LayersLUT.MissingError) ]
                else:
                    realLayersArgs += [ None ]
    
            symbolicLayer = None
    
            if layerType == TypeRegular:
                symbolicLayer = RegularLayer.create(technology,entry[0])
                symbolicLayer.setBasicLayer( *realLayersArgs )
            elif layerType == TypeDiffusion:
                symbolicLayer = DiffusionLayer.create(technology ,entry[0], *realLayersArgs)
            elif layerType == TypeTransistor:
                symbolicLayer = TransistorLayer.create(technology ,entry[0], *realLayersArgs)
            elif layerType == TypeContact:
                symbolicLayer = ContactLayer.create(technology ,entry[0], *realLayersArgs)
            elif layerType == TypeVia:
                symbolicLayer = ViaLayer.create(technology ,entry[0], *realLayersArgs)
    
            layersLUT.add( symbolicLayer )

        except Exception, e:
            ErrorMessage.wrapPrint(e,'In %s:<symbolicLayersTable> at index %d.' % (symbolicFile,entryNo))
    return


def loadSymbolicRules ( technology, symbolicRulesTable ):
    entryNo = 0
    for rule in symbolicRulesTable:
        entryNo += 1

        try:
            if len(rule) != 2:
                raise ErrorMessage(1,['Malformed entry in <symbolicRulesTable>.'
                                     ,'Must contains exactly two fields: ( rule_path, value ).'
                                     ,str(rule)
                                     ])
            if not isinstance(rule[1],int) and not isinstance(rule[1],float):
                raise ErrorMessage(1,['Invalid entry in <symbolicRulesTable>.'
                                     ,'Rule value must be of integer or float type.'
                                     ,str(rule)
                                     ])

            value    = DbU.fromLambda(rule[1])
            elements = rule[0].split('.')
            if len(elements) < 3:
                raise ErrorMessage(1,['Invalid entry in <symbolicRulesTable>.'
                                     ,'Rule name must contains at least three components: \"LAYER.category.dimension\".'
                                     ,str(rule)
                                     ])

            ruleLayer = layersLUT.lookup( elements[0], LayersLUT.Symbolic|LayersLUT.MissingError  )
            subLayer  = layersLUT.lookup( elements[1], LayersLUT.Real )

            if subLayer: ruleTag  = string.join(elements[2:],'.')
            else:        ruleTag  = string.join(elements[1:],'.')

            if   ruleTag == 'extention.cap':   ruleLayer.setExtentionCap  ( subLayer, value )
            elif ruleTag == 'extention.width': ruleLayer.setExtentionWidth( subLayer, value )
            elif ruleTag == 'enclosure':       ruleLayer.setEnclosure     ( subLayer, value )
            elif ruleTag == 'minimum.width':   ruleLayer.setMinimalSize   ( value )
            elif ruleTag == 'minimum.side':    ruleLayer.setMinimalSize   ( value )
            else: 
                raise ErrorMessage(1,['Invalid entry in <symbolicRulesTable>.'
                                     ,'Unknown rule kind: \".%s\", should be any of:' % ruleTag
                                     ,'    * "RULE_HEAD.extention.cap"'
                                     ,'    * "RULE_HEAD.extention.width"'
                                     ,'    * "RULE_HEAD.enclosure"'
                                     ,'    * "RULE_HEAD.minimum.width"'
                                     ,'    * "RULE_HEAD.minimum.side"'
                                     ,str(rule)
                                     ])

        except Exception, e:
            ErrorMessage.wrapPrint(e,'In %s:<symbolicRulesTable> at index %d.' % (symbolicFile,entryNo))
    return


def loadWorkingLayers ( technology, workingLayersTable ):
    entryNo = 0
    for layerName in workingLayersTable:
        entryNo += 1
        try:
           # This call is just to generate an error if the layer is non-existent.
            layersLUT.lookup(layerName,LayersLUT.Real|LayersLUT.Symbolic|LayersLUT.MissingError)
            technology.setWorkingLayer(layerName)
        except Exception, e:
            ErrorMessage.wrapPrint(e,'In %s:<symbolicRulesTable> at index %d.' % (symbolicFile,entryNo))
    return


def loadViewerConfig ( technology, viewerConfig ):
    try:
        if viewerConfig.has_key('precision'): DbU.setPrecision(viewerConfig['precision'])
    except Exception, e:
        ErrorMessage.wrapPrint(e,'In %s:<viewerConfig>.')
    return


def load ( symbolicPath ):
    tables = ( ('viewerConfig'       , loadViewerConfig  )
             , ('realLayersTable'    , loadRealLayers    )
             , ('symbolicLayersTable', loadSymbolicLayers)
             , ('symbolicRulesTable' , loadSymbolicRules )
             , ('workingLayersTable' , loadWorkingLayers )
             )

    global symbolicTechno
    global symbolicFile
    symbolicFile   = os.path.basename(symbolicPath)
    symbolicTechno = AllianceFramework.get().getEnvironment().getSYMB_TECHNO_NAME()

    confGlobals = globals()
    execfile(symbolicPath,confGlobals)

    db         = DataBase.getDB()
    technology = db.getTechnology()

    for symbol, loader in tables:
        if not confGlobals.has_key(symbol):
            print '[ERROR] The <%s> table is missing in the configuration file.' % symbol
            print '        <%s>' % symbolicFile
            print '        Attempting to continue anyway.'
        else:
            loader( technology, confGlobals[ symbol ])

    return
