from hwt.hdl.assignment import Assignment
from hwt.hdl.operator import Operator
from hwt.hdl.operatorDefs import AllOps, isEventDependentOp
from hwt.hdl.portItem import PortItem
from hwt.synthesizer.interfaceLevel.mainBases import UnitBase
from hwtLib.abstract.busEndpoint import BusEndpoint


def getEpSignal(sig, op):
    """
    :param sig: main signal
    :param op: operator on this signal

    :return: signal modified by this operator or none if this operator
        is creating new datapath
    """
    # we do not follow results of indexing like something[sig]
    if op.operator == AllOps.INDEX:
        if op.operands[0] is not sig:
            return
    if op.operator not in [AllOps.INDEX,
                           AllOps.ADD,
                           AllOps.SUB,
                           AllOps.MUL,
                           AllOps.DIV,
                           AllOps.CONCAT]:
        return

    if sig in op.operands:
        return op.result


def getParentUnit(sig):
    try:
        intf = sig._interface
    except AttributeError:
        return  # if there is no interface it cant have parent unit

    while not isinstance(intf._parent, UnitBase):
        intf = intf._parent
    return intf._parent


class AddressSpaceProbe(object):
    def __init__(self, topIntf, getMainSigFn, offset=0):
        """
        :param topIntf: interface on which should discovery start
        :param getMainSigFn: function which gets the main signal
            form interface which should this code care about
            usually address
        """
        self.topIntf = topIntf
        self.getMainSigFn = getMainSigFn
        self.offset = offset
        self.seen = set([None, ])
        self.discovered = self._discoverAddressSpace(self.topIntf,
                                                     self.offset)

    def _extractStruct(self, converter, offset):
        t = converter.STRUCT_TEMPLATE

        for transTmpl in converter._bramPortMapped:
            # some arrays can have items with internal structure
            memberT = self._discoverAddressSpace(
                converter.getPort(transTmpl), offset)
            if memberT is not None:
                raise NotImplementedError("Nested address space")
        return t

    def walkToConverter(self, mainSig):
        """
        walk mainSig down to endpoints and search for any bus converter
        instance
        """
        if mainSig in self.seen:
            return

        self.seen.add(mainSig)

        parent = getParentUnit(mainSig)

        if isinstance(parent, BusEndpoint) and parent not in self.seen:
            self.seen.add(parent)
            yield parent

        for e in mainSig.endpoints:
            if isinstance(e, Operator) and not isEventDependentOp(e):
                ep = getEpSignal(mainSig, e)
                yield from self.walkToConverter(ep)
            elif isinstance(e, (Assignment, PortItem)):
                yield from self.walkToConverter(e.dst)
            else:
                for outp in e._outputs:
                    yield from self.walkToConverter(outp)

    def _discoverAddressSpace(self, topIntf, offset):
        _mainSig = self.getMainSigFn(topIntf)
        try:
            mainSig = _mainSig._sig
        except AttributeError:
            mainSig = _mainSig._sigInside

        t = None
        for converter in self.walkToConverter(mainSig):
            # addrMap = self._extractAddressMap(converter, offset)
            if t is not None:
                raise NotImplementedError("Hierarchical endpoints")
            t = self._extractStruct(converter, offset)

        return t

@@ -285,16 +285,124 @@
 ndpoint%0A
+from hwtLib.abstract.busBridge import BusBridge%0Afrom hwtLib.abstract.busInterconnect import BusInterconnect%0A
 %0A%0Adef ge
@@ -1367,24 +1367,25 @@
 be(object):%0A
+%0A
     def __in
@@ -2404,24 +2404,32 @@
 elf, mainSig
+, offset
 ):%0A        %22
@@ -2519,16 +2519,84 @@
 instance
+s%0A%0A        :return: generator of typles (offset, converter instance)
 %0A       
@@ -2741,127 +2741,781 @@
  if 
-isinstance(parent, BusEndpoint) and parent not in self.seen:%0A            self.seen.add(parent)%0A            yield parent
+parent not in self.seen:%0A            self.seen.add(parent)%0A            if isinstance(parent, BusEndpoint):%0A                yield offset, parent%0A                return%0A            elif isinstance(parent, BusBridge):%0A                i = parent.m%0A                yield from self.walkToConverter(self._getMainSigFn(i), offset)%0A                return%0A            elif isinstance(parent, BusInterconnect):%0A                if len(parent._masters) != 1:%0A                    raise NotImplementedError()%0A                for intf, addrRec in zip(parent.m, parent._slaves):%0A                    _offset = addrRec%5B0%5D%0A                    yield from self.walkToConverter(self._getMainSigFn(intf),%0A                                                    offset + _offset)%0A                return
 %0A%0A  
@@ -3713,16 +3713,24 @@
 erter(ep
+, offset
 )%0A      
@@ -3832,16 +3832,24 @@
 er(e.dst
+, offset
 )%0A      
@@ -3956,16 +3956,24 @@
 ter(outp
+, offset
 )%0A%0A    d
@@ -3980,50 +3980,31 @@
 ef _
-discoverAddressSpace(self, topIntf, offset
+getMainSigFn(self, intf
 ):%0A 
@@ -4039,20 +4039,17 @@
 inSigFn(
-topI
+i
 ntf)%0A   
@@ -4066,33 +4066,30 @@
             
-mainSig =
+return
  _mainSig._s
@@ -4138,38 +4138,135 @@
     
-mainSig = _mainSig._sigInside%0A
+return _mainSig._sigInside%0A%0A    def _discoverAddressSpace(self, topIntf, offset):%0A        mainSig = self._getMainSigFn(topIntf)
 %0A   
@@ -4290,16 +4290,25 @@
      for
+ _offset,
  convert
@@ -4341,16 +4341,24 @@
 (mainSig
+, offset
 ):%0A     
@@ -4556,32 +4556,33 @@
 ruct(converter, 
+_
 offset)%0A%0A       

support for BusBridge, BusInterconnect