import subprocess

import myhdl
from myhdl import *
from myhdl.conversion import analyze

DESCENDING, ASCENDING = False, True


@block
def comp(a1, a2, z1, z2, dir):

    @always_comb
    def comb():
        z1.next = a1
        z2.next = a2
        if dir == (a1 > a2):
            z1.next = a2
            z2.next = a1

    return comb


@block
def feedthru(a, z):

    @always_comb
    def comb():
        z.next = a

    return comb


@block
def bitonicMerge(a, z, dir):

    n = len(a)
    k = n // 2
    w = len(a[0])

    if n > 1:
        t = [Signal(intbv(0)[w:]) for i in range(n)]
        comps = [comp(a[i], a[i + k], t[i], t[i + k], dir) for i in range(k)]
        lomerge = bitonicMerge(t[:k], z[:k], dir)
        himerge = bitonicMerge(t[k:], z[k:], dir)
        lomerge.name = "lomerge"
        himerge.name = "hiMerge"
        return comps, lomerge, himerge
    else:
        return feedthru(a[0], z[0])


@block
def bitonicSort(a, z, dir):

    n = len(a)
    k = n // 2
    w = len(a[0])

    if n > 1:
        t = [Signal(intbv(0)[w:]) for i in range(n)]
        losort = bitonicSort(a[:k], t[:k], ASCENDING)
        hisort = bitonicSort(a[k:], t[k:], DESCENDING)
        merge = bitonicMerge(t, z, dir)
        losort.name = "losort"
        hisort.name = "hisort"
        merge.name = "merge"
        return losort, hisort, merge
    else:
        return feedthru(a[0], z[0])


@block
def Array8Sorter(a0, a1, a2, a3, a4, a5, a6, a7,
                 z0, z1, z2, z3, z4, z5, z6, z7):

    a = [a0, a1, a2, a3, a4, a5, a6, a7]
    z = [z0, z1, z2, z3, z4, z5, z6, z7]
    sort = bitonicSort(a, z, ASCENDING)
    sort.name = "sort"
    return sort


def Array8Sorter_v(a0, a1, a2, a3, a4, a5, a6, a7,
                   z0, z1, z2, z3, z4, z5, z6, z7):

    analyze.simulator = 'iverilog'
    toVerilog(Array8Sorter(a0, a1, a2, a3, a4, a5, a6, a7,
                           z0, z1, z2, z3, z4, z5, z6, z7))
    analyze(Array8Sorter(a0, a1, a2, a3, a4, a5, a6, a7,
                         z0, z1, z2, z3, z4, z5, z6, z7))
    # cmd = "cver -q +loadvpi=../../../cosimulation/cver/myhdl_vpi:vpi_compat_bootstrap " + \
    #      "Array8Sorter.v tb_Array8Sorter.v"
    subprocess.call("iverilog -o Array8Sorter.o Array8Sorter.v tb_Array8Sorter.v", shell=True)
    cmd = "vvp -m ../../../cosimulation/icarus/myhdl.vpi Array8Sorter.o"
    return Cosimulation(cmd, **locals())
