#!/usr/bin/env python3

import sys
import Cfg
from   Hurricane3 import DbU, Point, Box, DataBase, Technology, LayerMask, \
                         BasicLayer, ViaLayer, RegularLayer
from   overlay    import CfgCache
from   technology import createBL

def flush ():
    sys.stdout.flush()
    sys.stderr.flush()

#def u ( value ): return value
#def l ( value ): return value
def l ( value ): return DbU.fromLambda( value )
def u ( value ): return DbU.fromPhysical( value, DbU.UnitPowerMicro )
def n ( value ): return DbU.fromPhysical( value, DbU.UnitPowerNano  )

def cfg_setup():
    print( "" )
    print( "Test Cfg module" )
    print( "========================================" )
    # Place & Route setup
    with CfgCache(priority=Cfg.Parameter.Priority.ConfigurationFile) as cfg:
        cfg.lefImport.minTerminalWidth = 0.0
        cfg.crlcore.groundName = 'vss'
        cfg.crlcore.powerName = 'vdd'
        cfg.etesian.aspectRatio = 1.00
        cfg.etesian.aspectRatio = [10, 1000]
        cfg.etesian.spaceMargin = 0.10
        cfg.etesian.uniformDensity = False
        cfg.etesian.routingDriven = False
        #cfg.etesian.latchUpDistance = u(30.0 - 1.0)
        cfg.etesian.latchUpDistance = 0
        cfg.etesian.antennaInsertThreshold = 0.50
        cfg.etesian.antennaMaxWL = u(250.0)
        cfg.etesian.antennaGateMaxWL = u(250.0)
        cfg.etesian.antennaDiodeMaxWL = u(550.0)
        cfg.etesian.tieName = 'tiepoly_x0'
        cfg.etesian.feedNames = 'tiepoly_x0'
        cfg.etesian.cell.zero = 'zero_x0'
        cfg.etesian.cell.one = 'one_x0'
        cfg.etesian.bloat = 'disabled'
        cfg.etesian.effort = 2
        cfg.etesian.effort = (
            ('Fast', 1),
            ('Standard', 2),
            ('High', 3 ),
            ('Extreme', 4 ),
        )
        cfg.etesian.graphics = 2
        cfg.etesian.graphics = (
            ('Show every step', 1),
            ('Show lower bound', 2),
            ('Show result only', 3),
        )
        cfg.anabatic.routingGauge = 'FlexLib'
        cfg.anabatic.globalLengthThreshold = 1450
        cfg.anabatic.saturateRatio = 0.90
        cfg.anabatic.saturateRp = 10
        cfg.anabatic.topRoutingLayer = 'METAL6'
        cfg.anabatic.edgeLength = 48
        cfg.anabatic.edgeWidth = 8
        cfg.anabatic.edgeCostH = 9.0
        cfg.anabatic.edgeCostK = -10.0
        cfg.anabatic.edgeHInc = 1.0
        cfg.anabatic.edgeHScaling = 1.0
        cfg.anabatic.globalIterations = 10
        cfg.anabatic.globalIterations = [ 1, 100 ]
        cfg.anabatic.gcell.displayMode = 1
        cfg.anabatic.gcell.displayMode = (("Boundary", 1), ("Density", 2))
        cfg.katana.hTracksReservedLocal = 4
        cfg.katana.hTracksReservedLocal = [0, 20]
        cfg.katana.vTracksReservedLocal = 3
        cfg.katana.vTracksReservedLocal = [0, 20]
        cfg.katana.termSatReservedLocal = 8
        cfg.katana.termSatThreshold = 9
        cfg.katana.eventsLimit = 4000002
        cfg.katana.ripupCost = 3
        cfg.katana.ripupCost = [0, None]
        cfg.katana.strapRipupLimit = 16
        cfg.katana.strapRipupLimit = [1, None]
        cfg.katana.localRipupLimit = 9
        cfg.katana.localRipupLimit = [1, None]
        cfg.katana.globalRipupLimit = 5
        cfg.katana.globalRipupLimit = [1, None]
        cfg.katana.longGlobalRipupLimit = 5
        cfg.chip.padCoreSide = 'South'

    # Plugins setup
    with CfgCache(priority=Cfg.Parameter.Priority.ConfigurationFile) as cfg:
        cfg.viewer.minimumSize = 500
        cfg.viewer.pixelThreshold = 20
        cfg.chip.block.rails.count = 0
        cfg.chip.block.rails.hWidth = u(2.68)
        cfg.chip.block.rails.vWidth = u(2.68)
        cfg.chip.block.rails.hSpacing = 0 #u(0.7)
        cfg.chip.block.rails.vSpacing = 0 #u(0.7)
        cfg.clockTree.minimumSide = l(600)
        cfg.clockTree.buffer = 'buf_x2'
        cfg.clockTree.placerEngine = 'Etesian'
        cfg.block.spareSide = 10
        cfg.spares.buffer = 'buf_x8'
        cfg.spares.maxSinks = 31


def testDbU ():
    print( "" )
    print( "Test Hurricane::DbU" )
    print( "========================================" )
    DbU.setPrecision( 2 )
    DbU.setPhysicalsPerGrid( 0.5, DbU.UnitPowerMicro )
    DbU.setGridsPerLambda( 2.0 )
    DbU.setSymbolicSnapGridStep( DbU.fromLambda(1.0) )
    DbU.setPolygonStep(DbU.fromGrid( 2.0) )
    DbU.setStringMode( DbU.StringModeSymbolic, DbU.UnitPowerMicro )
    value = DbU.fromPhysical( 10.0, DbU.UnitPowerMicro )
    print( "value={}".format( DbU.getValueString(value) ))


def testPoint ():
    print( "" )
    print( "Test Hurricane::Point" )
    print( "========================================" )
    p1 = Point( 1000, 2000 )
    print( p1 )
    p2 = Point( 1000, 2000 )
    print( p1 == p2 )
    p3 = Point( 2000, 2000 )
    print( p1 == p3 )
    p4 = Point( p3 )
    print( p4 )


def testBox ():
    print( "" )
    print( "Test Hurricane::Box" )
    print( "========================================" )
    b1 = Box()
    print( b1 )
    b1.merge( Point(1000,1000) )
    print( 'b1.merge(Point(1000,1000)={}'.format(b1) )
    b1.merge( Box(Point(2000,2000)) )
    print( 'b1.merge(Box(Point(1000,1000))={}'.format(b1) )
    print( 'b1.getCenter()={}'.format(b1.getCenter()) )
    p1 = Point( 1000, 1000 )
    b2 = Box( p1 )
    print( b2 )


def testDB ():
    print( "" )
    print( "Test Hurricane::DataBase" )
    print( "========================================" )
    db = DataBase.create()
    print( 'DataBase.getDB()={}'.format(DataBase.getDB()) )
    print( 'DataBase.Flags.CreateLib={}'.format(DataBase.Flags.CreateLib) )


def testTechnology ():
    print( "" )
    print( "Test Hurricane::Technology" )
    print( "========================================" )
    tech = Technology.create( DataBase.getDB(), 'test_techno' )
    print( 'tech={}'.format(tech) )


def testLayerMask ():
    print( "" )
    print( "Test Hurricane::Layer::Mask" )
    print( "========================================" )
    mask16 = LayerMask( 16 )
    print( 'mask16= {}'.format(mask16) )
    mask32 = LayerMask( 16+32 )
    print( 'mask16= {}'.format(mask32) )
    maskAnd = mask16 & mask32
    print( 'maskAnd={}'.format(maskAnd) )
    try:
        maskAnd = mask16 & 4
        print( 'maskAnd={}'.format(maskAnd) )
    except Exception as e:
        print( 'Catched and exception...' )
        print( e )
    maskShift = maskAnd << 1
    print( 'maskShift={}'.format(maskShift) )
    maskNot = ~maskAnd
    print( 'maskNot={}'.format(maskNot) )
    print( 'maskAnd={}'.format(maskAnd) )
    mask64 = LayerMask( 64+16 )
    print( 'mask64={}'.format(mask64) )
    mask64 &= LayerMask( 64 )
    print( 'mask64={}'.format(mask64) )
    mask64 |= LayerMask( 16 )
    print( 'mask64={}'.format(mask64) )
    mask64.fromString( '0x0000ffff' )
    print( 'mask64={}'.format(mask64) )


def testBasicLayer ():
    print( "" )
    print( "Test Hurricane::BasicLayer" )
    print( "========================================" )
    db     = DataBase.getDB()
    tech   = db.getTechnology()
    nWell  = createBL( tech, 'nWell' , BasicLayer.Material.nWell )
    pWell  = createBL( tech, 'pWell' , BasicLayer.Material.pWell )
    cut1   = createBL( tech, 'cut1'  , BasicLayer.Material.cut )
    metal1 = createBL( tech, 'metal1', BasicLayer.Material.metal )
    metal2 = createBL( tech, 'metal2', BasicLayer.Material.metal )
    METAL1 = RegularLayer.create( tech, 'METAL1', metal1 )
    METAL2 = RegularLayer.create( tech, 'METAL2', metal2 )
    VIA12  = ViaLayer    .create( tech, 'VIA12' , metal1, cut1, metal2  )
    print( 'nWell={}'.format(nWell) )
    print( 'pWell={}'.format(pWell) )
    print( dir(nWell) )
    print( 'Technology.getLayers():' )
    flush()
    for layer in tech.getLayers():
        flush()
        print( '| basicLayer={}'.format(layer) )
    flush()
 

if __name__ == '__main__':
    testDbU()
    cfg_setup()
    testPoint()
    testBox()
    testDB()
    testTechnology()
    testLayerMask()
    testBasicLayer()
    sys.exit( 0 )