#!/usr/bin/env python
"""
HCI Gateway - HCI protocol communication gateway

This module provides low-level HCI protocol operations.
The gateway handles basic protocol communication and command/response patterns.
"""
from __future__ import annotations
from typing import Optional
from logging import getLogger

from packet.hci_module import HciModule
from packet.hci.hci_packet_implement import (
    HciEvent, HciEventCommandComplete, HciProgInfoRsp, HciProgBlkClean,
    HciProgBaudrateSet, HciProgWriteData, HciProgXorCheck, HciProgApplyBlk,
    HciProgSha256Check, HciProgDump, HciDiagnoTput, HciDiagnoLatency
)
from error.atmosic_error import HciStatusError, HciGatewayError
from error.errorcodes import HciStatusErrorCode, HciGatewayErrorCode

logger = getLogger(__name__)

class HciGateway:
    """HCI protocol communication gateway.
    
    Responsible for basic protocol operations and command/response handling.
    This is the low-level layer that directly communicates with HCI device.
    """
    
    def __init__(self, hci_module: HciModule):
        """Initialize gateway with HCI module."""
        assert isinstance(hci_module, HciModule), "HCI module must be available"
        self.hci = hci_module
    
    def prog_info_request(self) -> HciProgInfoRsp.EventCC:
        """Send ProgInfo request and return response."""
        cmd = HciProgInfoRsp.Command()
        
        def wait_func() -> HciProgInfoRsp.EventCC:
            def check_func(evt: HciEvent) -> bool:
                return isinstance(evt, HciProgInfoRsp.EventCC)
            
            evt = self.hci.read_until(check_func, timeout_s=5.0)
            assert isinstance(evt, HciProgInfoRsp.EventCC)
            return evt
        
        evt = self.hci.write_and_wait(cmd, wait_function=wait_func)
        self._validate_command_complete_status(evt, "ProgInfo request")
        
        logger.debug(f"[Gateway] ProgInfo response:"
                     f" app_ver=0x{evt.app_ver or 0:04X},"
                     f" protocol_ver=0x{evt.protocol_ver or 0:04X},"
                     f" ram_buffer=0x{evt.ram_buffer_start or 0:08X}"
                     f"+0x{evt.ram_buffer_size or 0:08X}")
        
        return evt
    
    def prog_blk_clean_request(self, start_addr: int, length: int,
                               timeout_s: float = 5) -> HciProgBlkClean.EventCC:
        """Send ProgBlkClean request and return response."""
        cmd = HciProgBlkClean.Command()
        cmd.start_addr = start_addr
        cmd.length = length
        
        def wait_func() -> HciProgBlkClean.EventCC:
            def check_func(evt: HciEvent) -> bool:
                return isinstance(evt, HciProgBlkClean.EventCC)
            
            evt = self.hci.read_until(check_func, timeout_s=timeout_s)
            assert isinstance(evt, HciProgBlkClean.EventCC)
            return evt
        
        evt = self.hci.write_and_wait(cmd, wait_function=wait_func)
        self._validate_command_complete_status(evt, "Block clean request")
        
        logger.debug(f"[Gateway] Block cleaned:"
                     f" 0x{start_addr:08X}+0x{length:08X}")
        return evt
    
    def prog_baudrate_set_request(self, baudrate: int, delay_us: int) -> HciProgBaudrateSet.EventCC:
        """Send ProgBaudrateSet request and return response."""
        cmd = HciProgBaudrateSet.Command()
        cmd.baudrate = baudrate
        cmd.delay_time_us = delay_us
        
        def wait_func() -> HciProgBaudrateSet.EventCC:
            def check_func(evt: HciEvent) -> bool:
                return isinstance(evt, HciProgBaudrateSet.EventCC)
            
            evt = self.hci.read_until(check_func, timeout_s=5.0)
            assert isinstance(evt, HciProgBaudrateSet.EventCC)
            return evt
        
        evt = self.hci.write_and_wait(cmd, wait_function=wait_func)
        self._validate_command_complete_status(evt, "Baudrate set request")
        
        logger.debug(f"[Gateway] Baudrate set: {baudrate} baud,"
                     f" {delay_us}us delay")
        return evt
    
    def prog_write_data_send_command(self, address: int, seq_no: int,
                                     data: bytes, is_need_rsp: bool = True
                                    ) -> None:
        """Send ProgWriteData command without waiting for response."""
        cmd = HciProgWriteData.Command()
        cmd.mode = 2 if is_need_rsp else 1  # Mode 2 as specified
        cmd.address = address
        cmd.seq_no = seq_no
        cmd.data = data
        cmd.data_length = len(data)

        self.hci.write(cmd, is_clean_read_buffer=False)

        logger.debug(f"[Gateway] Data packet {seq_no} sent:"
                     f" address=0x{address:08X}, size={len(data)}")

    def prog_write_data_wait_event(self, timeout: float = 5.0
                                  ) -> HciProgWriteData.EventCC:
        """Wait for ProgWriteData response event."""
        def check_func(evt: HciEvent) -> bool:
            return isinstance(evt, HciProgWriteData.EventCC)

        evt = self.hci.read_until(check_func, timeout_s=timeout)
        assert isinstance(evt, HciProgWriteData.EventCC)
        self._validate_command_complete_status(evt, "Write data response")
        return evt

    def prog_write_data_request(self, address: int, seq_no: int, data: bytes
                               ) -> HciProgWriteData.EventCC:
        """Send ProgWriteData request and return response (original method with write lock)."""
        cmd = HciProgWriteData.Command()
        cmd.mode = 2  # Mode 2 as specified
        cmd.address = address
        cmd.seq_no = seq_no
        cmd.data = data
        cmd.data_length = len(data)

        def wait_func() -> HciProgWriteData.EventCC:
            def check_func(evt: HciEvent) -> bool:
                return isinstance(evt, HciProgWriteData.EventCC)

            evt = self.hci.read_until(check_func, timeout_s=5.0)
            assert isinstance(evt, HciProgWriteData.EventCC)
            return evt

        evt = self.hci.write_and_wait(cmd, wait_function=wait_func)
        self._validate_command_complete_status(evt, "Write data request")

        logger.debug(f"[Gateway] Data packet {seq_no} written:"
                     f" address=0x{address:08X}, size={len(data)}")
        return evt
    
    def prog_xor_check_request(self, address: int, length: int
                              ) -> HciProgXorCheck.EventCC:
        """Send ProgXorCheck request and return response."""
        cmd = HciProgXorCheck.Command()
        cmd.address = address
        cmd.length = length
        
        def wait_func() -> HciProgXorCheck.EventCC:
            def check_func(evt: HciEvent) -> bool:
                return isinstance(evt, HciProgXorCheck.EventCC)
            
            evt = self.hci.read_until(check_func, timeout_s=10.0)
            assert isinstance(evt, HciProgXorCheck.EventCC)
            return evt
        
        evt = self.hci.write_and_wait(cmd, wait_function=wait_func)
        self._validate_command_complete_status(evt, "XOR check request")
        
        logger.debug(f"[Gateway] XOR check: address=0x{address:08X},"
                     f" length={length}, xor=0x{evt.xor:02X}")
        return evt
    
    def prog_apply_blk_request(self, source_addr: int, target_addr: int,
                               length: int) -> HciProgApplyBlk.EventCC:
        """Send ProgApplyBlk request and return response."""
        cmd = HciProgApplyBlk.Command()
        cmd.source_address = source_addr
        cmd.target_address = target_addr
        cmd.length = length
        
        def wait_func() -> HciProgApplyBlk.EventCC:
            def check_func(evt: HciEvent) -> bool:
                return isinstance(evt, HciProgApplyBlk.EventCC)
            
            # Longer timeout for storage operation
            evt = self.hci.read_until(check_func, timeout_s=30.0)
            assert isinstance(evt, HciProgApplyBlk.EventCC)
            return evt
        
        evt = self.hci.write_and_wait(cmd, wait_function=wait_func)
        self._validate_command_complete_status(evt, "Apply block request")
        
        logger.debug(f"[Gateway] Block applied: 0x{source_addr:08X} ->"
                     f" 0x{target_addr:08X} ({length} bytes)")
        return evt
    
    def prog_sha256_check_request(self, address: int, length: int
                                 ) -> HciProgSha256Check.EventCC:
        """Send ProgSha256Check request and return response."""
        cmd = HciProgSha256Check.Command()
        cmd.address = address
        cmd.length = length
        
        def wait_func() -> HciProgSha256Check.EventCC:
            def check_func(evt: HciEvent) -> bool:
                return isinstance(evt, HciProgSha256Check.EventCC)
            
            # Longer timeout for SHA256 calculation
            evt = self.hci.read_until(check_func, timeout_s=15.0)
            assert isinstance(evt, HciProgSha256Check.EventCC)
            return evt
        
        evt = self.hci.write_and_wait(cmd, wait_function=wait_func)
        self._validate_command_complete_status(evt, "SHA256 check request")
        
        logger.debug(f"[Gateway] SHA256 check: address=0x{address:08X},"
                     f" length={length},"
                     f" hash={evt.sha256_first_16_bytes.hex()}")
        return evt

    def prog_dump_send_command(self, absolute_addr: int, data_length: int
                              ) -> None:
        """Send ProgDump request and return response."""
        cmd = HciProgDump.Command()
        cmd.start_address = absolute_addr
        cmd.length = data_length
        
        self.hci.write(cmd)
        
        logger.debug(f"[Gateway] Data dump: address=0x{absolute_addr:08X},"
                     f" length={data_length}")
        
    def prog_dump_wait_event(self, timeout_s: float = 15.0
                            ) -> Optional[HciProgDump.EventCC]:
        def check_func(evt: HciEvent) -> bool:
            return isinstance(evt, HciProgDump.EventCC)
        
        evt = self.hci.read_until(check_func, timeout_s=timeout_s)
        assert isinstance(evt, HciProgDump.EventCC)
        self._validate_command_complete_status(evt, "Data dump request")
        return evt

    def _validate_command_complete_status(self, evt: HciEventCommandComplete,
                                          hci_command_title: str) -> None:
        if evt.status is None:
            raise HciGatewayError(
                f"{hci_command_title} failed with no status: {evt}",
                HciGatewayErrorCode.EVENT_FORMAT_ERROR)
        elif evt.status != 0:
            raise HciStatusError(
                f"{hci_command_title} with status: {evt.status}",
                HciStatusErrorCode(evt.status))

    def diagno_tput_send_command(self, mode: int, data: bytes) -> None:
        """Send DiagnoTput request and return response."""
        cmd = HciDiagnoTput.Command()
        cmd.mode = mode
        cmd.data_len = len(data)
        cmd.data = data
        
        self.hci.write(cmd)
        
        logger.debug(f"[Gateway] DiagnoTput: mode={mode},"
                     f" data_len={cmd.data_len}")

    def diagno_tput_wait_event(self, timeout_s: float = 15.0
                              ) -> Optional[HciDiagnoTput.EventCC]:
        def check_func(evt: HciEvent) -> bool:
            return isinstance(evt, HciDiagnoTput.EventCC)
        
        evt = self.hci.read_until(check_func, timeout_s=timeout_s)
        assert isinstance(evt, HciDiagnoTput.EventCC)
        return evt
    
    def diagno_latency_request(self, test_data: int, start_address: int,
                               data_length: int) -> HciDiagnoLatency.EventCC:
        """Send DiagnoLatency request and return response."""
        
        logger.debug(f"[Gateway] DiagnoLatency: test_data={test_data},"
                     f" start_address=0x{start_address:08X},"
                     f" data_length={data_length}")

        cmd = HciDiagnoLatency.Command()
        cmd.test_data = test_data
        cmd.start_address = start_address
        cmd.data_length = data_length

        def wait_func() -> HciDiagnoLatency.EventCC:
            def check_func(evt: HciEvent) -> bool:
                return isinstance(evt, HciDiagnoLatency.EventCC)
            
            evt = self.hci.read_until(check_func, timeout_s=15.0)
            assert isinstance(evt, HciDiagnoLatency.EventCC)
            return evt
        
        evt = self.hci.write_and_wait(cmd, wait_function=wait_func)
        self._validate_command_complete_status(evt, "DiagnoLatency request")
        return evt