from saleae.analyzers import AnalyzerFrame
from SecondLayerHLAs.SecondLayerAnalyzerHandler import SecondLayerAnalyzerHandler
from I3CDictionaries import I3CDictionaries, DecodingModes
from GenericDecoder import GenericDecoder
import logging
import sys


class BTDecoder(GenericDecoder):

    I3C_logger = logging.getLogger("I3C")

    result_types = I3CDictionaries.result_types

    BT_HEADER_BYTES        = 7
    BT_CRC_BYTES           = 6
    BT_DATA_BYTES_PER_BLOCK = 32

    def __init__(self, second_layer_HLA_option, i3c_version_option):
        logging.basicConfig(level=logging.INFO, stream=sys.stdout)

        self.ccc_dict = I3CDictionaries.getCCCDict(i3c_version_option)
        handler = SecondLayerAnalyzerHandler()
        self.second_layer_analyzer = handler.get(second_layer_HLA_option, DecodingModes.BT)
        self.second_layer_analyzer.update_result_types(self.result_types)

        self._reset_state()

    # ------------------------------------------------------------------ state

    def _reset_state(self):
        # Header phase
        self.bt_header_count     = 0
        self.bt_header_start     = None
        self.bt_header_address   = ""
        self.bt_header_direction = ""
        self.bt_header_cmd       = []
        self.bt_header_crc_type  = "CRC-16"
        self.bt_header_scl_source  = "Controller"
        self.bt_header_ccc_cont    = ""
        self.bt_header_new_framing = ""
        self.bt_header_ccc_name    = ""
        self.bt_header_error     = ""

        # Data block phase
        self.bt_block_index    = 0
        self.bt_tc_is_last     = False
        self.bt_tc_valid_bytes = 32
        self.bt_tc_byte        = "0x00"
        self.bt_tc_flags       = "Normal"
        self.bt_data_bytes     = []
        self.bt_data_count     = 0
        self.bt_data_start     = None
        self.bt_data_error     = ""

        # CRC phase
        self.bt_crc_count      = 0
        self.bt_crc_start      = None
        self.bt_crc_type       = "CRC-16"
        self.bt_crc_bytes      = []
        self.bt_crc_terminated = False
        self.bt_crc_error      = ""

    # ------------------------------------------------------------------ helpers

    def _append_header_error(self, new_error: str):
        if new_error:
            if self.bt_header_error:
                self.bt_header_error += "-"
            self.bt_header_error += new_error

    def _append_data_error(self, new_error: str):
        if new_error:
            if self.bt_data_error:
                self.bt_data_error += "-"
            self.bt_data_error += new_error

    def _append_crc_error(self, new_error: str):
        if new_error:
            if self.bt_crc_error:
                self.bt_crc_error += "-"
            self.bt_crc_error += new_error

    def _emit(self, frame: AnalyzerFrame):
        s = self.result_types[frame.type]["console"]
        try:
            self.I3C_logger.info(s.format(**frame.data))
        except Exception:
            pass
        return self.second_layer_analyzer.instruction_decoder(frame)

    def _emit_hdr_pattern(self, frame: AnalyzerFrame):
        info = frame.data.get("BT Info", "")
        new_frame = AnalyzerFrame(
            "I3C HDR Pattern",
            frame.start_time,
            frame.end_time,
            {"Info": info, "Length": 0},
        )
        s = self.result_types[new_frame.type]["console"]
        self.I3C_logger.info(s.format(**new_frame.data))
        new_frame.data["Length"] = str(new_frame.data["Length"])
        return self.second_layer_analyzer.instruction_decoder(new_frame)

    # ------------------------------------------------------------------ main

    def processFrame(self, frame: AnalyzerFrame):

        # ---- Header Block (7 bytes) ----
        if frame.type == "BT HEADER":
            self.bt_header_count += 1
            b = int(frame.data["BT Data"], 16)

            if self.bt_header_count == 1:
                # Byte 0: Address bits[7:1] + R/W bit[0]
                self.bt_header_start     = frame.start_time
                self.bt_header_address   = f"0x{b >> 1:02X}"
                self.bt_header_direction = "R" if (b & 0x01) else "W"
                self.bt_header_error     = frame.data.get("BT Error", "")
                self.bt_header_cmd       = []

            elif 2 <= self.bt_header_count <= 5:
                # Bytes 1–4: Cmd0–Cmd3
                self.bt_header_cmd.append(f"0x{b:02X}")
                self._append_header_error(frame.data.get("BT Error", ""))

            elif self.bt_header_count == 6:
                # Byte 5: Control — bit[0]=direction, bit[1]=CRC type, bit[2]=SCL source, bit[3]=CCC-Cont, bit[4]=new framing
                
                self.bt_header_ctr_direction = "R" if (b & 0x01) else "W"
                self.bt_header_crc_type    = "CRC-32" if (b & 0x02) else "CRC-16"       # Bit 1: Low (0) = CRC-16, High (1) = CRC-32
                
                if self.bt_header_direction == "R":
                    self.bt_header_scl_source  = "Target" if (b & 0x04) else "Controller"   # Bit 2: Low (0) = Controller drives SCL, High (1) = Target drives SCL in a READ.
                else:
                    self.bt_header_scl_source  = ""
                
                self.bt_header_ccc_cont    = "CCC-Cont " if (b & 0x08) else "Normal Msg"
                self.bt_header_new_framing = "v1.2-Framing " if (b & 0x10) else "Old Framing"
                self._append_header_error(frame.data.get("BT Error", ""))

            elif self.bt_header_count == self.BT_HEADER_BYTES:
                # Byte 6: Transition — bit[1] Low=ACK (Target drives SDA Low), High-Z=NACK
                self._append_header_error(frame.data.get("BT Error", ""))
                ack = "ACK" if (b & 0x02) == 0 else "NACK"
                
                if self.bt_header_direction == "R":
                    if (b & 0x04) == 0:
                        delay = "Delay Allowed"
                    else:
                        delay = "Delay Not Allowed"
                else:
                    delay = ""

                # Check if it's a CCC command to get the name for display
                if self.bt_header_address == "0x7E" and self.bt_header_cmd:
                    ccc_code = int(self.bt_header_cmd[0], 16)
                    name = self.ccc_dict.get(ccc_code, "")
                    self.bt_header_ccc_name = f" ({name})" if name else ""
                else:
                    self.bt_header_ccc_name = ""
                
                hdr = AnalyzerFrame(
                    "BT Header",
                    self.bt_header_start,
                    frame.end_time,
                    {   
                        # Byte 0: Address bits[7:1] + R/W bit[0]
                        "Address":    self.bt_header_address,
                        "Direction":  self.bt_header_direction,
                        # Bytes 1–4: Cmd0–Cmdr3
                        "Cmd0":       self.bt_header_cmd[0] if len(self.bt_header_cmd) > 0 else "",
                        "Cmd1":       self.bt_header_cmd[1] if len(self.bt_header_cmd) > 1 else "",
                        "Cmd2":       self.bt_header_cmd[2] if len(self.bt_header_cmd) > 2 else "",
                        "Cmd3":       self.bt_header_cmd[3] if len(self.bt_header_cmd) > 3 else "",
                        "CCCName":    self.bt_header_ccc_name,
                        # Byte 5: Control — bit[0]=direction, bit[1]=CRC type, bit[2]=SCL source, bit[3]=CCC-Cont, bit[4]=new framing
                        "CTRLDirection": self.bt_header_ctr_direction,
                        "CRCType":    self.bt_header_crc_type,
                        "SCLSource":  self.bt_header_scl_source,
                        "CCCCont":    self.bt_header_ccc_cont,
                        "NewFraming": self.bt_header_new_framing,
                        # Byte 6: Transition — bit[1] Low=ACK (Target drives SDA Low), High-Z=NACK
                        "ACK":        ack,
                        "Delay":      delay,
                        "Error":      f" - Error: [{self.bt_header_error}]" if self.bt_header_error else "",
                    },
                )
                self.bt_header_count = 0
                return self._emit(hdr), DecodingModes.BT

        # ---- Data Blocks (33 bytes each: 1 TC + 32 data) ----
        elif frame.type == "BT DATA":
            if "[TC]" in frame.data.get("BT Info", ""):
                # Transition_Control byte — starts a new Data Block.
                # A second TC before 32 data bytes means the previous was a Delay byte; discard it.
                
                # Full Byte
                tc = int(frame.data["BT Data"], 16)
                self.bt_tc_byte        = f"0x{tc:02X}"
                
                # Bit 1: Low (0) = Terminated (receiver drives SDA Low), High-Z (1) = Not terminated
                terminated = "Terminated " if (tc & 0x02) == 0 else ""                                  
                
                # Bit 2: High (1) = Last DB (Data Block).
                self.bt_tc_is_last     = bool(tc & 0x04)                                                
                is_last    = "Last DB" if self.bt_tc_is_last else ""
                

                # Bit 4: If Last=1, bits 7:4 indicate valid byte count in last block; if Last=0, all 32 bytes are valid.
                target_tx = ""
                if self.bt_tc_is_last:
                    self.bt_tc_valid_bytes = ((tc >> 4) + 1) * 2 if self.bt_tc_is_last else 32
                else:
                    if self.bt_header_direction == "R":
                        if (tc & 0x10) == 0:
                            target_tx = "Valid DB"
                        else:
                            target_tx = "DB Delay"
                    
                flags = (terminated + is_last + target_tx).strip()
                self.bt_tc_flags = flags if flags else "Normal"
                
                self.bt_data_bytes     = []
                self.bt_data_count     = 0
                self.bt_data_start     = frame.start_time
                self.bt_data_error     = frame.data.get("BT Error", "")
            else:
                self.bt_data_bytes.append(f"0x{int(frame.data['BT Data'], 16):02X}")
                self.bt_data_count += 1
                self._append_data_error(frame.data.get("BT Error", ""))

                if self.bt_data_count == self.BT_DATA_BYTES_PER_BLOCK:
                    display_data = self.bt_data_bytes[:self.bt_tc_valid_bytes]
                    blk = AnalyzerFrame(
                        "BT Data Block",
                        self.bt_data_start,
                        frame.end_time,
                        {
                            "BlockIndex": str(self.bt_block_index),
                            "TCByte":     self.bt_tc_byte,
                            "TCFlags":    self.bt_tc_flags,
                            "ValidBytes": str(self.bt_tc_valid_bytes),
                            "Data":       ", ".join(display_data),
                            "IsLast":     "Last" if self.bt_tc_is_last else "",
                            "Error":      f" - Error: [{self.bt_data_error}]" if self.bt_data_error else "",
                        },
                    )
                    self.bt_block_index += 1
                    self.bt_data_bytes   = []
                    self.bt_data_count   = 0
                    return self._emit(blk), DecodingModes.BT

        # ---- CRC Block (6 bytes) ----
        elif frame.type == "BT CRC":
            self.bt_crc_count += 1
            b = int(frame.data["BT Data"], 16)

            if self.bt_crc_count == 1:
                # Byte 0: Control — bit[5]=CRC type, bit[6]=terminated
                self.bt_crc_start      = frame.start_time
                self.bt_crc_type       = "CRC-32" if (b & 0x20) else "CRC-16"
                self.bt_crc_terminated = bool(b & 0x40)
                self.bt_crc_bytes      = []
                self.bt_crc_error      = frame.data.get("BT Error", "")

            elif 2 <= self.bt_crc_count <= 5:
                # Bytes 1–4: CRC0–CRC3
                self.bt_crc_bytes.append(b)
                self._append_crc_error(frame.data.get("BT Error", ""))

            elif self.bt_crc_count == self.BT_CRC_BYTES:
                # Byte 5: Transition_Verify
                self._append_crc_error(frame.data.get("BT Error", ""))

                if self.bt_crc_type == "CRC-16" and len(self.bt_crc_bytes) >= 2:
                    val = (self.bt_crc_bytes[1] << 8) | self.bt_crc_bytes[0]
                    crc_str = f"0x{val:04X}"
                elif len(self.bt_crc_bytes) >= 4:
                    val = ((self.bt_crc_bytes[3] << 24) | (self.bt_crc_bytes[2] << 16)
                           | (self.bt_crc_bytes[1] << 8)  |  self.bt_crc_bytes[0])
                    crc_str = f"0x{val:08X}"
                else:
                    crc_str = "?"

                # bit[1] Low (0) = CRC PASS (receiver acknowledges), High-Z (1) = FAIL
                verify = "PASS" if (b & 0x02) == 0 else "FAIL"
                crc_frame = AnalyzerFrame(
                    "BT CRC Block",
                    self.bt_crc_start,
                    frame.end_time,
                    {
                        "CRCType":      self.bt_crc_type,
                        "CRCValue":     crc_str,
                        "Terminated":   " Terminated" if self.bt_crc_terminated else "",
                        "VerifyResult": verify,
                        "Error":        f" - Error: [{self.bt_crc_error}]" if self.bt_crc_error else "",
                    },
                )
                self.bt_crc_count   = 0
                self.bt_block_index = 0
                return self._emit(crc_frame), DecodingModes.BT

        # ---- Mode transitions ----
        elif frame.type == "BT HDR R-START":
            self._reset_state()
            return self._emit_hdr_pattern(frame), DecodingModes.BT

        elif frame.type == "BT HDR EXIT":
            self._reset_state()
            return self._emit_hdr_pattern(frame), DecodingModes.SDR

        elif frame.type == "BT STOP":
            self._reset_state()
            return None, DecodingModes.SDR

        elif frame.type == "BT ERROR":
            self._reset_state()
            return AnalyzerFrame("BT Unknown Transaction", frame.start_time, frame.end_time), DecodingModes.BT

        elif frame.type == "UNSUPPORTED":
            self._reset_state()
            return AnalyzerFrame("UNSUPPORTED", frame.start_time, frame.end_time), DecodingModes.BT

        return None, DecodingModes.BT
