from math import inf
import unittest

from hwt.hdl.types.bits import Bits
from hwt.hdl.types.stream import HStream
from hwtLib.abstract.frame_utils.alignment_utils import FrameAlignmentUtils
from hwtLib.abstract.frame_utils.join.fsm import input_B_dst_to_fsm
from hwtLib.abstract.frame_utils.join.state_trans_item import StateTransItem


class FrameJoinUtilsTC(unittest.TestCase):

    def test_fsm0(self):
        word_bytes = 2
        f_len = (1, 1)
        streams = [
            HStream(Bits(8), frame_len=f_len, start_offsets=[1]),
        ]
        out_offset = 0
        sju = FrameAlignmentUtils(word_bytes, out_offset)
        input_B_dst = sju.resolve_input_bytes_destinations(streams)
        tt = input_B_dst_to_fsm(word_bytes, len(streams), input_B_dst, sju.can_produce_zero_len_frame(streams))

        def st(d):
            return StateTransItem.from_dict(tt, d)

        ref = [[
            st({'st': '0->0', 'in': [[{'keep': [0, 1], 'relict': 0, 'last': 1}]],
                 'in.keep_mask':[[[0, 0]]], 'in.rd':[1],
                 'out.keep':[1, 0], 'out.mux':[(0, 0, 1), None], 'out.last':1}
                )],
        ]

        self.assertSequenceEqual(tt.state_trans, ref)

    def test_fsm0_w2_0B(self):
        word_bytes = 2
        streams = [
            HStream(Bits(8), frame_len=(0, 1)),
        ]
        out_offset = 0
        sju = FrameAlignmentUtils(word_bytes, out_offset)
        input_B_dst = sju.resolve_input_bytes_destinations(streams)
        tt = input_B_dst_to_fsm(word_bytes, len(streams), input_B_dst, sju.can_produce_zero_len_frame(streams))

        def st(d):
            return StateTransItem.from_dict(tt, d)

        ref = [[
            st({'st':'0->0', 'in':[[{'keep':[0, 0], 'relict':1, 'last':1}]],
                'in.keep_mask':[[[0, 0]]], 'in.rd':[1],
                'out.keep':[0, 0], 'out.mux':[None, None], 'out.last':1}),
            st({'st':'0->0', 'in':[[{'keep':[1, 0], 'relict':0, 'last':1}]],
                'in.keep_mask':[[[0, 0]]], 'in.rd':[1],
                'out.keep':[1, 0], 'out.mux':[(0, 0, 0), None], 'out.last':1})
        ]]
        self.assertSequenceEqual(tt.state_trans, ref)

    def test_fsm0_0B(self):
        word_bytes = 1
        streams = [
            HStream(Bits(8), frame_len=(0, 1)),
        ]
        out_offset = 0
        sju = FrameAlignmentUtils(word_bytes, out_offset)
        input_B_dst = sju.resolve_input_bytes_destinations(streams)
        tt = input_B_dst_to_fsm(word_bytes, len(streams), input_B_dst, sju.can_produce_zero_len_frame(streams))

        def st(d):
            return StateTransItem.from_dict(tt, d)

        ref = [[
            st({'st':'0->0', 'in':[[{'keep':[0], 'relict':1, 'last':1}]],
                'in.keep_mask':[[[0]]], 'in.rd':[1],
                'out.keep':[0], 'out.mux':[None], 'out.last':1}),
            st({'st':'0->0', 'in':[[{'keep':[1], 'relict':0, 'last':1}]],
                'in.keep_mask':[[[0]]], 'in.rd':[1],
                'out.keep':[1], 'out.mux':[(0, 0, 0)], 'out.last':1})
        ]]

        self.assertSequenceEqual(tt.state_trans, ref)

    def test_fsm0_arbitrary_len(self):
        word_bytes = 2
        f_len = (1, inf)
        streams = [
            HStream(Bits(8), frame_len=f_len),
        ]
        out_offset = 0
        sju = FrameAlignmentUtils(word_bytes, out_offset)
        input_B_dst = sju.resolve_input_bytes_destinations(streams)