import os
path = os.path
import unittest
from unittest import TestCase
import random
from random import randrange
random.seed(2)

from myhdl import (block, Signal, delay, always,
                   instance, StopSimulation)
from myhdl._Simulation import Simulation

from .util import setupCosimulation

ACTIVE_LOW, INACTIVE_HIGH = 0, 1


@block
def edge1(flag, sig, clock):

    sig_Z1 = Signal(bool(0))

    @always(clock.posedge)
    def detect():
        sig_Z1.next = sig
        flag.next = False
        if sig and not sig_Z1:
            flag.next = True

    return detect


@block
def edge2(flag, sig, clock):

    sig_Z1 = Signal(bool(0))

    @always(clock.posedge)
    def detect():
        sig_Z1.next = sig
        flag.next = sig and not sig_Z1

    return detect


@block
def edge3(flag, sig, clock):

    @instance
    def detect():
        sig_Z1 = False
        while True:
            yield clock.posedge
            flag.next = sig and not sig_Z1
            sig_Z1 = sig.val

    return detect


@block
def edge4(flag, sig, clock):

    @instance
    def detect():
        sig_Z1 = False
        while True:
            yield clock.posedge
            flag.next = sig and not sig_Z1
            sig_Z1 = bool(sig)

    return detect


@block
def edge_v(name, flag, sig, clock):
    return setupCosimulation(**locals())


class TestEdge(TestCase):

    def bench(self, edge):

        clock = Signal(bool(0))
        sig = Signal(bool(0))
        flag = Signal(bool(0))

        sig_Z1 = Signal(bool(0))
        sig_Z2 = Signal(bool(0))

        @always(delay(10))
        def clockgen():
            clock.next = not clock

        @instance
        def stimulus():
            yield clock.negedge
            for dummy in range(100):
                sig.next = randrange(2)
                yield clock.negedge
            raise StopSimulation

        @always(clock.posedge)
        def delayline():
            sig_Z1.next = sig
            sig_Z2.next = sig_Z1

        @always(clock.negedge)
        def check():
            expected = sig_Z1 and not sig_Z2
            self.assertEqual(flag, expected)

        edge_inst = edge(flag, sig, clock).convert(hdl='Verilog')
        edge_inst_v = edge_v(edge.__name__, flag, sig, clock)

        return clockgen, stimulus, delayline, check, edge_inst_v

    def testEdge1(self):
        sim = Simulation(self.bench(edge1))
        sim.run(quiet=1)

    def testEdge2(self):
        sim = Simulation(self.bench(edge2))
        sim.run(quiet=1)

    def testEdge3(self):
        sim = Simulation(self.bench(edge3))
        sim.run(quiet=1)

    def testEdge4(self):
        sim = Simulation(self.bench(edge4))
        sim.run(quiet=1)


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

