import unittest
import os
path = os.path
from random import randrange

from myhdl import (block, Signal, intbv, delay, instances,
                   instance)
from myhdl._Simulation import Simulation

from .util import setupCosimulation

### test of constant wire support ###


# example from Frank Palazollo
@block
def or_gate(a, b, c):

    @instance
    def comb():
        while 1:
            c.next = a | b
            yield a, b

    return comb


@block
def my_bundle(p, q):
        r = Signal(bool(0))
        gen_or = or_gate(p, r, q)
        return instances()


# additional level of hierarchy
@block
def ConstWire2(p, q):
    r = Signal(bool(1))
    s = Signal(bool(1))
    inst1 = my_bundle(p, r)
    inst2 = or_gate(r, s, q)
    return inst1, inst2


@block
def adder(a, b, c):

    @instance
    def comb():
        while 1:
            yield a, b
            c.next = a + b

    return comb


@block
def ConstWire3(p, q):
    t = Signal(intbv(17)[5:])
    adder_inst = adder(p, t, q)
    return instances()


@block
def ConstWire_v(name, p, q):
    return setupCosimulation(**locals())


class TestConstWires(unittest.TestCase):

    def benchBool(self, ConstWire):

        p = Signal(bool(0))
        q = Signal(bool(0))
        q_v = Signal(bool(0))

        constwire_inst = ConstWire(p, q).convert(hdl='Verilog')
        constwire_v_inst = ConstWire_v(ConstWire.__name__, p, q_v)

        def stimulus():
            for dummy in range(100):
                p.next = randrange(2)
                yield delay(10)
                self.assertEqual(q, q_v)

        return stimulus(), constwire_inst, constwire_v_inst

    def testConstWire1(self):
        sim = self.benchBool(my_bundle)
        Simulation(sim).run()

    def testConstWire2(self):
        sim = self.benchBool(ConstWire2)
        Simulation(sim).run()

    def benchIntbv(self, ConstWire):

        p = Signal(intbv(0)[8:])
        q = Signal(intbv(0)[8:])
        q_v = Signal(intbv(0)[8:])

        constwire_inst = ConstWire(p, q).convert(hdl='Verilog')
        constwire_v_inst = ConstWire_v(ConstWire.__name__, p, q_v)

        def stimulus():
            for i in range(100):
                p.next = i
                yield delay(10)
                self.assertEqual(q, q_v)

        return stimulus(), constwire_inst, constwire_v_inst

    def testConstWire3(self):
        sim = self.benchIntbv(ConstWire3)
        Simulation(sim).run()

### tests of code ignore facility during translation ###


@block
def adderRef(a, b, c):

    @instance
    def comb():
        while 1:
            yield a, b
            c.next = a + b

    return comb


@block
def adderDebug(a, b, c):

    @instance
    def comb():
        while 1:
            yield a, b
            if __debug__:
                import string
            c.next = a + b

    return comb


def Ignorecode_v(name, a, b, c):
    return setupCosimulation(**locals())


class TestIgnoreCode(unittest.TestCase):

    def bench(self, adder):

        a = Signal(intbv(0)[8:])
        b = Signal(intbv(0)[8:])
        c = Signal(intbv(0)[9:])
        c_v = Signal(intbv(0)[9:])

        ignorecode_inst = adder(a, b, c).convert(hdl='Verilog')
        ignorecode_v_inst = Ignorecode_v(adder.__name__, a, b, c_v)

        def stimulus():
            for dummy in range(100):
                a.next = randrange(2 ** 8)
                b.next = randrange(2 ** 8)
                yield delay(10)
                self.assertEqual(c, c_v)

        return stimulus(), ignorecode_inst, ignorecode_v_inst

    def testAdderRef(self):
        sim = self.bench(adderRef)
        Simulation(sim).run()

    def testAdderDebug(self):
        sim = self.bench(adderDebug)
        Simulation(sim).run()


if __name__ == '__main__':
    unittest.main()
