#!/usr/bin/env python3
# -*- python -*-

from enum import IntEnum
from pconst import const

from sony_fpga_driver.dmac_if import PciBar


const.VCOMP_ADRS_OFFSET = 0xd100


class VCOMPRegAdrs(IntEnum):
    THROUGH_MODE_ADRS = 0x00
    MASK_MODE_ADRS = 0x04
    BLANK_MODE_ADRS = 0x08
    COUNT_MODE_ADRS = 0x0C
    ERR_COLOR_ADRS = 0x10
    BIT_MASK_ADRS = 0x14
    ERR_FLAG_RST_ADRS = 0x18
    ERR_FLAG_ADRS = 0x1C
    COUNT_START_ADRS = 0x20
    COUNT_RST_ADRS = 0x24
    ERR_COUNT_ADRS = 0x28
    STORE_MODE_ADRS = 0x58
    READ_FRAME_SEL_ADRS = 0x5C
    CMPIN_SEL_ADRS = 0x60


class RegAccess(IntEnum):
    READ_ONLY = 1
    WRITE = 2
    ABORT = 3


class VCOMP:
    def __init__(self, name, gif2=False):
        if gif2:
            self.__io = PciBar("GIF2")
        else:
            self.__io = PciBar("GIF")
        self._name = name
        self._reg_access = RegAccess.ABORT

    def acceptable(self, args):
        print("Don't Use")

    def acceptable_pair(self, args):
        if args.p is None:
            print("Not Found: -p")
            return False
        pair_list = args.p

        if len(pair_list) < 1 or 3 < len(pair_list):
            return False
        for pair in pair_list:
            if pair < 1 or 3 < pair:
                return False
        return True

    def acceptable_ch(self, args):
        if args.c is None:
            print("Not Found: -c")
            return False
        ch_list = args.c

        if len(ch_list) < 1 or 4 < len(ch_list):
            return False
        for ch in ch_list:
            if ch < 1 or 4 < ch:
                return False
        return True

    def write(self, adrs, val):
        byte_val = val.to_bytes(4, 'little')
        self.__io.write(adrs, byte_val)
        return 0

    def read(self, adrs):
        return self.__io.read(adrs, 4)

    def burst_read(self):
        return self.__io.burst_read(const.VCOMP_ADRS_OFFSET, 100)

    def display(self):
        return self

    def __del__(self):
        return 0


class THROUGH_MODE(VCOMP):
    def __init__(self, gif2=False):
        super().__init__('THROUGH_MODE', gif2)
        self.__ADRS = const.VCOMP_ADRS_OFFSET \
            + VCOMPRegAdrs.THROUGH_MODE_ADRS
        self.__val = 0

    def acceptable(self, args):
        if super().acceptable_pair(args) is not True:
            print(self._name + " illegal value: -p")
            self._reg_access = RegAccess.ABORT
            return self._reg_access
        pair_list = args.p

        if super().acceptable_ch(args) is not True:
            print(self._name + " illegal value: -c")
            self._reg_access = RegAccess.ABORT
            return self._reg_access
        ch_list = args.c

        if args.t is None:
            self._reg_access = RegAccess.READ_ONLY
            return self._reg_access
        through = args.t

        if through < 0x00 or 0x01 < through:
            print(self._name + " illegal value: -t")
            self._reg_access = RegAccess.ABORT
            return self._reg_access

        self.__val = 0
        for pair in pair_list:
            for ch in ch_list:
                self.__val |= through << ((pair - 1) * 4 + (ch - 1))
        self._reg_access = RegAccess.WRITE
        return self._reg_access

    def write(self):
        if self._reg_access == RegAccess.WRITE:
            super().write(self.__ADRS, self.__val)
        return self

    def read(self):
        return super().read(self.__ADRS)

    def display(self):
        print("%s(0x%02X): 0x%08X" % (self._name, self.__ADRS, self.read()))
        return self

    def __del__(self):
        return


class MASK_MODE(VCOMP):
    def __init__(self, gif2=False):
        super().__init__('MASK_MODE', gif2)
        self.__ADRS = const.VCOMP_ADRS_OFFSET \
            + VCOMPRegAdrs.MASK_MODE_ADRS
        self.__val = 0

    def acceptable(self, args):
        if super().acceptable_pair(args) is not True:
            print(self._name + " illegal value: -p")
            self._reg_access = RegAccess.ABORT
            return self._reg_access
        pair_list = args.p

        if super().acceptable_ch(args) is not True:
            print(self._name + " illegal value: -c")
            self._reg_access = RegAccess.ABORT
            return self._reg_access
        ch_list = args.c

        if args.m is None:
            self._reg_access = RegAccess.READ_ONLY
            return self._reg_access
        mask = args.m

        if mask < 0x00 or 0x01 < mask:
            print(self._name + " illegal value: -m")
            self._reg_access = RegAccess.ABORT
            return self._reg_access

        self.__val = 0
        for pair in pair_list:
            for ch in ch_list:
                self.__val |= mask << ((pair - 1) * 4 + (ch - 1))
        self._reg_access = RegAccess.WRITE
        return self._reg_access

    def write(self):
        if self._reg_access == RegAccess.WRITE:
            super().write(self.__ADRS, self.__val)
        return self

    def read(self):
        return super().read(self.__ADRS)

    def display(self):
        print("%s(0x%02X): 0x%08X" % (self._name, self.__ADRS, self.read()))
        return self

    def __del__(self):
        return 0


class BLANK_MODE(VCOMP):
    def __init__(self, gif2=False):
        super().__init__('BLANK_MODE', gif2)
        self.__ADRS = const.VCOMP_ADRS_OFFSET \
            + VCOMPRegAdrs.BLANK_MODE_ADRS
        self.__val = 0

    def acceptable(self, args):
        if super().acceptable_pair(args) is not True:
            print(self._name + " illegal value: -p")
            self._reg_access = RegAccess.ABORT
            return self._reg_access
        pair_list = args.p

        if super().acceptable_ch(args) is not True:
            print(self._name + " illegal value: -c")
            self._reg_access = RegAccess.ABORT
            return self._reg_access
        ch_list = args.c

        if args.b is None:
            self._reg_access = RegAccess.READ_ONLY
            return self._reg_access
        blank = args.b

        if blank < 0x00 or 0x01 < blank:
            print(self._name + " illegal value: -b")
            self._reg_access = RegAccess.ABORT
            return self._reg_access

        self.__val = 0
        for pair in pair_list:
            for ch in ch_list:
                self.__val |= blank << ((pair - 1) * 4 + (ch - 1))
        self._reg_access = RegAccess.WRITE
        return self._reg_access

    def write(self):
        if self._reg_access == RegAccess.WRITE:
            super().write(self.__ADRS, self.__val)
        return self

    def read(self):
        return super().read(self.__ADRS)

    def display(self):
        print("%s(0x%02X): 0x%08X" % (self._name, self.__ADRS, self.read()))
        return self

    def __del__(self):
        return 0


class COUNT_MODE(VCOMP):
    def __init__(self, gif2=False):
        super().__init__('COUNT_MODE', gif2)
        self.__ADRS = const.VCOMP_ADRS_OFFSET \
            + VCOMPRegAdrs.COUNT_MODE_ADRS
        self.__val = 0

    def acceptable(self, args):
        if super().acceptable_pair(args) is not True:
            print(self._name + " illegal value: -p")
            self._reg_access = RegAccess.ABORT
            return self._reg_access
        pair_list = args.p

        if super().acceptable_ch(args) is not True:
            print(self._name + " illegal value: -c")
            self._reg_access = RegAccess.ABORT
            return self._reg_access
        ch_list = args.c

        self.__val = 0
        if args.ca is not None:
            count = args.ca
            if count < 0x00 or 0x02 < count:
                print("COUNT_MODE illegal value: -b")
                self._reg_access = RegAccess.ABORT
                return self._reg_access
            for pair in pair_list:
                for ch in ch_list:
                    self.__val |= count << ((pair - 1) * 8 + (ch - 1) * 2)
        else:
            self.__val = self.read() & 0x00FFFFFF

        if args.ho is not None:
            self.__val |= args.ho << 28

        self._reg_access = RegAccess.WRITE
        return self._reg_access

    def write(self):
        if self._reg_access == RegAccess.WRITE:
            super().write(self.__ADRS, self.__val)
        return self

    def read(self):
        return super().read(self.__ADRS)

    def display(self):
        print("%s(0x%02X): 0x%08X" % (self._name, self.__ADRS, self.read()))
        return self

    def __del__(self):
        return 0


class ERR_COLOR(VCOMP):
    def __init__(self, gif2=False):
        super().__init__('ERR_COLOR', gif2)
        self.__ADRS = const.VCOMP_ADRS_OFFSET \
            + VCOMPRegAdrs.ERR_COLOR_ADRS
        self.__val = 0

    def acceptable(self, args):
        if args.ey is None and args.ec is None:
            self._reg_access = RegAccess.READ_ONLY
            return self._reg_access

        val = 0
        mask = 0

        if args.ey is not None:
            err_color_y = args.ey
            if err_color_y < 0 or 0x3FF < err_color_y:
                print(self._name + " illegal value: -ey")
                self._reg_access = RegAccess.ABORT
                return self._reg_access

            mask |= 0x3FF
            val |= err_color_y & 0x3FF

        if args.ec is not None:
            err_color_c = args.ec
            if err_color_c < 0 or 0x3FF < err_color_c:
                print(self._name + " illegal value: -ec")
                self._reg_access = RegAccess.ABORT
                return self._reg_access

            mask |= 0x3FF << 16
            val |= (err_color_c & 0x3FF) << 16

        mask = int(format(~mask & 0xFFFFFFFF, '08x'), 16)
        self.__val = (self.read() & mask) | val
        self._reg_access = RegAccess.WRITE
        return self._reg_access

    def write(self):
        if self._reg_access == RegAccess.WRITE:
            super().write(self.__ADRS, self.__val)
        return self

    def read(self):
        return super().read(self.__ADRS)

    def display(self):
        print("%s(0x%02X): 0x%08X" % (self._name, self.__ADRS, self.read()))
        return self

    def __del__(self):
        return 0


class BIT_MASK(VCOMP):
    def __init__(self, gif2=False):
        super().__init__('BIT_MASK', gif2)
        self.__ADRS = const.VCOMP_ADRS_OFFSET \
            + VCOMPRegAdrs.BIT_MASK_ADRS
        self.__val = 0

    def acceptable(self, args):
        if args.my is None and args.mc is None:
            self._reg_access = RegAccess.READ_ONLY
            return self._reg_access

        val = 0
        mask = 0

        if args.my is not None:
            bit_mask_y = args.my
            if bit_mask_y < 0 or 0x3FF < bit_mask_y:
                print(self._name + " illegal value: -my")
                self._reg_access = RegAccess.ABORT
                return self._reg_access

            mask |= 0x3FF
            val |= bit_mask_y & 0x3FF

        if args.mc is not None:
            bit_mask_c = args.mc
            if bit_mask_c < 0 or 0x3FF < bit_mask_c:
                print(self._name + " illegal value: -mc")
                self._reg_access = RegAccess.ABORT
                return self._reg_access

            mask |= 0x3FF << 16
            val |= (bit_mask_c & 0x3FF) << 16

        mask = int(format(~mask & 0xFFFFFFFF, '08x'), 16)
        self.__val = (self.read() & mask) | val
        self._reg_access = RegAccess.WRITE
        return self._reg_access

    def write(self):
        if self._reg_access == RegAccess.WRITE:
            super().write(self.__ADRS, self.__val)
        return self

    def read(self):
        return super().read(self.__ADRS)

    def display(self):
        print("%s(0x%02X): 0x%08X" % (self._name, self.__ADRS, self.read()))
        return self

    def __del__(self):
        return 0


class ERR_FLAG_RST(VCOMP):
    def __init__(self, args, gif2=False):
        super().__init__('ERR_FLAG_RST', gif2)
        self.__ADRS = const.VCOMP_ADRS_OFFSET \
            + VCOMPRegAdrs.ERR_FLAG_RST_ADRS

        if super().acceptable_pair(args) is not True:
            self._reg_access = RegAccess.ABORT
            print(self._name + " illegal value: -p")
            return
        self.__pair_list = args.p

        if super().acceptable_ch(args) is not True:
            self._reg_access = RegAccess.ABORT
            print(self._name + " illegal value: -c")
            return
        self.__ch_list = args.c
        self._reg_access = RegAccess.WRITE

    def start(self):
        if self._reg_access == RegAccess.WRITE:
            val = self.read()
            for pair in self.__pair_list:
                for ch in self.__ch_list:
                    val |= 1 << ((pair - 1) * 4 + (ch - 1))
            super().write(self.__ADRS, val)
        return self

    def read(self):
        return super().read(self.__ADRS)

    def display(self):
        print("%s(0x%02X): 0x%08X" % (self._name, self.__ADRS, self.read()))
        return self

    def __del__(self):
        return


class ERR_FLAG(VCOMP):
    def __init__(self, gif2=False):
        super().__init__('ERR_FLAG', gif2)
        self.__ADRS = const.VCOMP_ADRS_OFFSET \
            + VCOMPRegAdrs.ERR_FLAG_ADRS

    def get_err_list(self, pair_list, ch_list):
        # value = self.read()
        # err_list = []
        # for pair in pair_list:
        #     for ch in ch_list:
        #         check_bit = 1 << ((pair - 1) * 4 + (ch - 1))
        #         if value & check_bit:
        #             result = {"pair": pair, "ch": ch}
        #             err_list.append(result)
        return self.read()

    def write(self):
        pass

    def read(self):
        return super().read(self.__ADRS)

    def display(self):
        print("%s(0x%02X): 0x%08X" % (self._name, self.__ADRS, self.read()))
        return self

# debug start
    def debug_write(self, debug_pair_list, debug_ch_list):
        val = 0
        for pair in debug_pair_list:
            for ch in debug_ch_list:
                val |= (1 << ((pair - 1) * 4 + (ch - 1)))
        super().write(self.__ADRS, val)

    def debug_clean(self):
        super().write(self.__ADRS, 0)
# debug end

    def __del__(self):
        return 0


class COUNT_START(VCOMP):
    def __init__(self, args, gif2=False):
        super().__init__('COUNT_START', gif2)
        self.__ADRS = const.VCOMP_ADRS_OFFSET \
            + VCOMPRegAdrs.COUNT_START_ADRS

        if super().acceptable_pair(args) is not True:
            self._reg_access = RegAccess.ABORT
            print(self._name + " illegal value: -p")
            return
        self.__pair_list = args.p

        if super().acceptable_ch(args) is not True:
            self._reg_access = RegAccess.ABORT
            print(self._name + " illegal value: -c")
            return
        self.__ch_list = args.c

        self._reg_access = RegAccess.WRITE

    def stop(self):
        if self._reg_access == RegAccess.WRITE:
            val = self.read()
            for pair in self.__pair_list:
                for ch in self.__ch_list:
                    val &= ~(1 << ((pair - 1) * 4 + (ch - 1)))
            super().write(self.__ADRS, val)
        return self

    def start(self):
        if self._reg_access == RegAccess.WRITE:
            val = self.read()
            for pair in self.__pair_list:
                for ch in self.__ch_list:
                    val |= 1 << ((pair - 1) * 4 + (ch - 1))
            super().write(self.__ADRS, val)
        return self

    def read(self):
        return super().read(self.__ADRS)

    def display(self):
        print("%s(0x%02X): 0x%08X" % (self._name, self.__ADRS, self.read()))
        return self

    def __del__(self):
        return


class COUNT_RST(VCOMP):
    def __init__(self, args, gif2=False):
        super().__init__('COUNT_RST', gif2)
        self.__ADRS = const.VCOMP_ADRS_OFFSET \
            + VCOMPRegAdrs.COUNT_RST_ADRS

        if super().acceptable_pair(args) is not True:
            self._reg_access = RegAccess.ABORT
            print(self._name + " illegal value: -p")
            return
        self.__pair_list = args.p

        if super().acceptable_ch(args) is not True:
            self._reg_access = RegAccess.ABORT
            print(self._name + " illegal value: -c")
            return
        self.__ch_list = args.c

        self._reg_access = RegAccess.WRITE

    def start(self):
        if self._reg_access == RegAccess.WRITE:
            val = self.read()
            for pair in self.__pair_list:
                for ch in self.__ch_list:
                    val |= 1 << ((pair - 1) * 4 + (ch - 1))
            super().write(self.__ADRS, val)
        return self

    def read(self):
        return super().read(self.__ADRS)

    def display(self):
        print("%s(0x%02X): 0x%08X" % (self._name, self.__ADRS, self.read()))
        return self

    def __del__(self):
        return


class ERR_COUNT(VCOMP):
    def __init__(self, gif2=False):
        super().__init__('ERR_COUNT', gif2)
        self.__ADRS = const.VCOMP_ADRS_OFFSET \
            + VCOMPRegAdrs.ERR_COUNT_ADRS

    def acceptable(self, args):
        self._reg_access = RegAccess.READ_ONLY
        return self._reg_access

    def write(self):
        pass

    def read(self, pair, ch):
        adrs = self.__ADRS + ((pair - 1) * 4 + (ch - 1)) * 4
        return super().read(adrs)

    def display(self):
        for pair in range(1, 4):
            for ch in range(1, 5):
                adrs = self.__ADRS + ((pair - 1) * 4 + (ch - 1)) * 4
                print("%s(0x%02X): 0x%08X" %
                      (self._name, adrs, self.read(pair, ch)))
        return self

    def debug_write(self, debug_value):
        super().write(self.__ADRS, debug_value)

    def __del__(self):
        return 0


class STORE_MODE(VCOMP):
    def __init__(self, gif2=False):
        super().__init__('STORE_MODE', gif2)
        self.__ADRS = const.VCOMP_ADRS_OFFSET \
            + VCOMPRegAdrs.STORE_MODE_ADRS
        self.__val = 0

    def acceptable(self, args):
        if args.sm is None and args.sa is None:
            self._reg_access = RegAccess.READ_ONLY
            return self._reg_access

        elif args.sm is not None and args.sa is not None:
            store = args.sm
            stop = args.sa

            if store < 1 or 3 < store:
                print("STORE_MODE illegal value: -sm")
                self._reg_access = RegAccess.ABORT
                return self._reg_access

            if stop < 0 or 1 < stop:
                print(self._name + " illegal value: -sa")
                self._reg_access = RegAccess.ABORT
                return self._reg_access

            self.__val |= store & 0b11
            self.__val |= stop << 4
            self._reg_access = RegAccess.WRITE
            return self._reg_access

        else:
            self._reg_access = RegAccess.ABORT
            return self._reg_access

    def write(self):
        if self._reg_access == RegAccess.WRITE:
            super().write(self.__ADRS, self.__val)
        return self

    def read(self):
        return super().read(self.__ADRS)

    def read_store_mode(self):
        return self.read() & 0b11

    def display(self):
        print("%s(0x%02X): 0x%08X" % (self._name, self.__ADRS, self.read()))
        return self

    def __del__(self):
        return 0


class READ_FRAME_SEL(VCOMP):
    def __init__(self, gif2=False):
        super().__init__('READ_FRAME_SEL', gif2)
        self.__ADRS = const.VCOMP_ADRS_OFFSET \
            + VCOMPRegAdrs.READ_FRAME_SEL_ADRS
        self.__val = 0

    def acceptable(self, args):
        if super().acceptable_pair(args) is not True:
            print(self._name + " illegal value: -p")
            self._reg_access = RegAccess.ABORT
            return self._reg_access
        pair_list = args.p

        if args.r is None:
            self._reg_access = RegAccess.READ_ONLY
            return self._reg_access
        read_frame = args.r

        val = 0
        mask = 0

        if read_frame < 0:
            read_frame = ~(read_frame & 0b1111)
            read_frame += 1
            read_frame |= 0b10000

        for pair in pair_list:
            mask |= 0b11111 << ((pair - 1) * 8)
            val |= (read_frame & 0b11111) << ((pair - 1) * 8)
        mask = int(format(~mask & 0xFFFFFFFF, '08x'), 16)
        self.__val = (self.read() & mask) | val
        self._reg_access = RegAccess.WRITE
        return self._reg_access

    def write(self):
        if self._reg_access == RegAccess.WRITE:
            super().write(self.__ADRS, self.__val)
        return self

    def read(self):
        return super().read(self.__ADRS)

    def read_pair(self, pair):
        val = (self.read() >> (pair - 1) * 8) & 0b11111
        if val & 0b10000:
            val = val & 0b1111
            val *= -1
        return val

    def display(self):
        print("%s(0x%02X): 0x%08X" % (self._name, self.__ADRS, self.read()))
        return self

    def __del__(self):
        return 0


class CMPIN_SEL(VCOMP):
    def __init__(self, gif2=False):
        super().__init__('CMPIN_SEL', gif2)
        self.__ADRS = const.VCOMP_ADRS_OFFSET \
            + VCOMPRegAdrs.CMPIN_SEL_ADRS
        self.__val = 0

    def acceptable(self, args):
        if super().acceptable_pair(args) is not True:
            print(self._name + " illegal value: -p")
            self._reg_access = RegAccess.ABORT
            return self._reg_access
        pair_list = args.p

        if args.t is None:
            self._reg_access = RegAccess.READ_ONLY
            return self._reg_access
        through = args.t

        if through < 0x00 or 0x01 < through:
            print(self._name + " illegal value: -t")
            self._reg_access = RegAccess.ABORT
            return self._reg_access

        val = 0
        mask = 0

        for pair in pair_list:
            mask |= 1 << ((pair - 1) * 4)
            val |= through << ((pair - 1) * 4)
        mask = int(format(~mask & 0xFFFFFFFF, '08x'), 16)
        self.__val = (self.read() & mask) | val
        self._reg_access = RegAccess.WRITE
        return self._reg_access

    def write(self):
        if self._reg_access == RegAccess.WRITE:
            super().write(self.__ADRS, self.__val)
        return self

    def read(self):
        return super().read(self.__ADRS)

    def display(self):
        print("%s(0x%02X): 0x%08X" % (self._name, self.__ADRS, self.read()))
        return self

    def __del__(self):
        return 0
