from protocol import I2CPCClient, time
from tef_102_patch import *

from typing import Callable, TypeVar, ParamSpec, Concatenate
from collections.abc import Callable as ABCCallable

P = ParamSpec("P")
T = TypeVar("T")

ADDRESS = 0x64
class BaseTEF668X:
    def __init__(self, p: I2CPCClient) -> None:
        self.p = p
        p.set_baudrate(1500000)
        self.p.set_clock(400_000)
    def close(self):
        self.p.set_baudrate(115200)
        self.p.close()
    def _reset(self):
        self.p.write_i2c(ADDRESS, b"\x1e\x5a\x01\x5a\x5a")
    def _write_firmware(self, patch: bytes | list[int], patch_lut: bytes | list[int]):
        self.p.write_i2c(ADDRESS, b"\x1c\x00\x00")
        self.p.write_i2c(ADDRESS, b"\x1c\x00\x74")
        def send_patch(_patch: bytes):
            for i in range(0, len(_patch), 24):
                data = _patch[i:i+24]
                if self.p.write_i2c(ADDRESS, b"\x1b" + data)[1] != 0: raise Exception
        send_patch(bytes(patch))
        self.p.write_i2c(ADDRESS, b"\x1c\x00\x00")
        self.p.write_i2c(ADDRESS, b"\x1c\x00\x75")
        send_patch(bytes(patch_lut))
        self.p.write_i2c(ADDRESS, b"\x1c\x00\x00")
    
    def APPL_Get_Operation_Status(self):
        data = self.p.write_read_i2c(ADDRESS, b"\x40\x80\x01", 2)
        while data[1] != 0:
            data = self.p.write_read_i2c(ADDRESS, b"\x40\x80\x01", 2)
            time.sleep(0.01)
        return data[-1]

    @staticmethod
    def _base_command_wrapper(func: Callable[Concatenate["BaseTEF668X", P], tuple[bytes, int | None, ABCCallable[[bytes], T] | None]]) -> Callable[Concatenate["BaseTEF668X", P], bytes | T]:
        def inner(self: "BaseTEF668X", *args: P.args, **kwargs: P.kwargs ) -> bytes | T:
            data, read_bytes, out_parser = func(self, *args, **kwargs)
            if read_bytes: data = self.p.write_read_i2c(ADDRESS, data, read_bytes)
            else: data = self.p.write_i2c(ADDRESS, data)
            if out_parser: return out_parser(data)
            return data
        return inner

    @_base_command_wrapper
    def APPL_Set_ReferenceClock(self, clock: int, type_clock: bool):
        return b"\x40\x04\x01" + bytes(((clock >> 16) & 0xffff).to_bytes(2) + (clock & 0xffff).to_bytes(2) + bytes([0, int(type_clock)])), None, None
    @_base_command_wrapper
    def APPL_Activate(self):
        return b"\x40\x05\x01\x00\x01", None, None

    def init(self):
        self._reset()
        while self.APPL_Get_Operation_Status() != 0: time.sleep(0.01)
        self._write_firmware(tef_102_patch, tef_102_patch_lut)
        self.p.write_i2c(ADDRESS, b"\x14\x00\x01")
        while self.APPL_Get_Operation_Status() != 1: time.sleep(0.01)
        self.APPL_Set_ReferenceClock(12000000, False)
        self.APPL_Activate()
        while self.APPL_Get_Operation_Status() != 2: time.sleep(0.01)