#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
VWED Fins 模块
提供 Fins 协议通信相关的内置函数
"""

from typing import Optional
from utils.logger import get_logger
import socket
import struct

logger = get_logger("services.script_fins_module")


class VWEDFinsModule:
    """VWED Fins 模块类"""
    
    def __init__(self, script_id: str):
        self.script_id = script_id
        logger.debug(f"初始化 Fins 模块，脚本ID: {script_id}")
    
    def read_fins_string(self, ip: str, port: int, area: int, fins_io_addr: int, bit_offset: int, word_length: int) -> Optional[str]:
        """
        Fins 读取 string
        
        Args:
            ip: 从机 IP
            port: 从机端口  
            area: 读取的存储区域代码，16进制值如：0x82
            fins_io_addr: 读取的地址位
            bit_offset: 读取的偏移量
            word_length: 读取的长度
            
        Returns:
            Optional[str]: 读取成功的返回值，失败返回 None
        """
        try:
            logger.info(f"Fins 读取 string - IP: {ip}, Port: {port}, Area: 0x{area:02X}, Addr: {fins_io_addr}, Offset: {bit_offset}, Length: {word_length}")
            
            # 建立 TCP 连接
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5.0)  # 5秒超时
            sock.connect((ip, port))
            
            try:
                # 构造 Fins 读取命令
                command = self._build_fins_read_command(area, fins_io_addr, bit_offset, word_length)
                
                # 发送命令
                sock.send(command)
                
                # 接收响应
                response = sock.recv(1024)
                
                # 解析响应并提取字符串数据
                result = self._parse_fins_string_response(response, word_length)
                
                logger.info(f"Fins 读取 string 成功: {result}")
                return result
                
            finally:
                sock.close()
                
        except Exception as e:
            logger.error(f"Fins 读取 string 失败: {str(e)}")
            return None
    
    def read_fins_word(self, ip: str, port: int, area: int, fins_io_addr: int, bit_offset: int) -> Optional[int]:
        """
        Fins 读取 word
        
        Args:
            ip: 从机 IP
            port: 从机端口
            area: 读取的存储区域代码，16进制值如：0x82
            fins_io_addr: 读取的地址位
            bit_offset: 读取的偏移量
            
        Returns:
            Optional[int]: 读取成功的返回值，失败返回 None
        """
        try:
            logger.info(f"Fins 读取 word - IP: {ip}, Port: {port}, Area: 0x{area:02X}, Addr: {fins_io_addr}, Offset: {bit_offset}")
            
            # 建立 TCP 连接
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5.0)
            sock.connect((ip, port))
            
            try:
                # 构造 Fins 读取命令（读取 1 个 word）
                command = self._build_fins_read_command(area, fins_io_addr, bit_offset, 1)
                
                # 发送命令
                sock.send(command)
                
                # 接收响应
                response = sock.recv(1024)
                
                # 解析响应并提取 word 数据
                result = self._parse_fins_word_response(response)
                
                logger.info(f"Fins 读取 word 成功: {result}")
                return result
                
            finally:
                sock.close()
                
        except Exception as e:
            logger.error(f"Fins 读取 word 失败: {str(e)}")
            return None
    
    def read_fins_bit(self, ip: str, port: int, area: int, fins_io_addr: int, bit_offset: int) -> Optional[int]:
        """
        Fins 读取 bit
        
        Args:
            ip: 从机 IP
            port: 从机端口
            area: 读取的存储区域代码，16进制值如：0x82
            fins_io_addr: 读取的地址位
            bit_offset: 读取的偏移量
            
        Returns:
            Optional[int]: 读取成功的返回值，失败返回 None
        """
        try:
            logger.info(f"Fins 读取 bit - IP: {ip}, Port: {port}, Area: 0x{area:02X}, Addr: {fins_io_addr}, Offset: {bit_offset}")
            
            # 建立 TCP 连接
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5.0)
            sock.connect((ip, port))
            
            try:
                # 构造 Fins 位读取命令
                command = self._build_fins_bit_read_command(area, fins_io_addr, bit_offset)
                
                # 发送命令
                sock.send(command)
                
                # 接收响应
                response = sock.recv(1024)
                
                # 解析响应并提取 bit 数据
                result = self._parse_fins_bit_response(response)
                
                logger.info(f"Fins 读取 bit 成功: {result}")
                return result
                
            finally:
                sock.close()
                
        except Exception as e:
            logger.error(f"Fins 读取 bit 失败: {str(e)}")
            return None
    
    def write_fins_word(self, ip: str, port: int, area: int, fins_io_addr: int, bit_offset: int, value: int) -> None:
        """
        Fins 写入 word
        
        Args:
            ip: 从机 IP
            port: 从机端口
            area: 写入的存储区域代码，16进制值如：0x82
            fins_io_addr: 写入的地址位
            bit_offset: 写入的偏移量
            value: 写入的值
        """
        try:
            logger.info(f"Fins 写入 word - IP: {ip}, Port: {port}, Area: 0x{area:02X}, Addr: {fins_io_addr}, Offset: {bit_offset}, Value: {value}")
            
            # 建立 TCP 连接
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5.0)
            sock.connect((ip, port))
            
            try:
                # 构造 Fins 写入命令
                command = self._build_fins_write_command(area, fins_io_addr, bit_offset, value)
                
                # 发送命令
                sock.send(command)
                
                # 接收响应
                response = sock.recv(1024)
                
                # 检查响应状态
                if self._check_fins_write_response(response):
                    logger.info(f"Fins 写入 word 成功")
                else:
                    logger.error(f"Fins 写入 word 失败: 响应状态错误")
                
            finally:
                sock.close()
                
        except Exception as e:
            logger.error(f"Fins 写入 word 失败: {str(e)}")
    
    def write_fins_bit(self, ip: str, port: int, area: int, fins_io_addr: int, bit_offset: int, value: bool) -> None:
        """
        Fins 写入 bit
        
        Args:
            ip: 从机 IP
            port: 从机端口
            area: 写入的存储区域代码，16进制值如：0x82
            fins_io_addr: 写入的地址位
            bit_offset: 写入的偏移量
            value: 写入的值
        """
        try:
            logger.info(f"Fins 写入 bit - IP: {ip}, Port: {port}, Area: 0x{area:02X}, Addr: {fins_io_addr}, Offset: {bit_offset}, Value: {value}")
            
            # 建立 TCP 连接
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5.0)
            sock.connect((ip, port))
            
            try:
                # 构造 Fins 位写入命令
                command = self._build_fins_bit_write_command(area, fins_io_addr, bit_offset, value)
                
                # 发送命令
                sock.send(command)
                
                # 接收响应
                response = sock.recv(1024)
                
                # 检查响应状态
                if self._check_fins_write_response(response):
                    logger.info(f"Fins 写入 bit 成功")
                else:
                    logger.error(f"Fins 写入 bit 失败: 响应状态错误")
                
            finally:
                sock.close()
                
        except Exception as e:
            logger.error(f"Fins 写入 bit 失败: {str(e)}")
    
    def _build_fins_read_command(self, area: int, address: int, bit_offset: int, word_count: int) -> bytes:
        """构造 Fins 读取命令"""
        # Fins TCP 头部 (16 字节)
        header = b'FINS' + b'\x00' * 12
        
        # Fins 命令头部
        icf = 0x80  # Information Control Field
        rsv = 0x00  # Reserved
        gct = 0x02  # Gateway Count
        dna = 0x00  # Destination Network Address
        da1 = 0x00  # Destination Unit Address
        da2 = 0x00  # Destination Node Address
        sna = 0x00  # Source Network Address
        sa1 = 0x00  # Source Unit Address
        sa2 = 0x00  # Source Node Address
        sid = 0x00  # Service ID
        
        # 读取命令
        cmd = 0x0101  # Memory Area Read
        
        # 内存区域指定
        area_code = area & 0xFF
        addr_high = (address >> 8) & 0xFF
        addr_low = address & 0xFF
        bit_addr = bit_offset & 0x0F
        count_high = (word_count >> 8) & 0xFF
        count_low = word_count & 0xFF
        
        # 组装命令
        command = struct.pack('>BBBBBBBBBBHBBBBBB',
                             icf, rsv, gct, dna, da1, da2, sna, sa1, sa2, sid,
                             cmd, area_code, addr_high, addr_low, bit_addr, count_high, count_low)
        
        # 添加长度到头部
        length = len(command)
        header = header[:8] + struct.pack('>I', length) + header[12:]
        
        return header + command
    
    def _build_fins_bit_read_command(self, area: int, address: int, bit_offset: int) -> bytes:
        """构造 Fins 位读取命令"""
        # 使用读取命令，但指定位地址
        return self._build_fins_read_command(area, address, bit_offset, 1)
    
    def _build_fins_write_command(self, area: int, address: int, bit_offset: int, value: int) -> bytes:
        """构造 Fins 写入命令"""
        # Fins TCP 头部
        header = b'FINS' + b'\x00' * 12
        
        # Fins 命令头部
        icf = 0x80
        rsv = 0x00
        gct = 0x02
        dna = 0x00
        da1 = 0x00
        da2 = 0x00
        sna = 0x00
        sa1 = 0x00
        sa2 = 0x00
        sid = 0x00
        
        # 写入命令
        cmd = 0x0102  # Memory Area Write
        
        # 内存区域指定
        area_code = area & 0xFF
        addr_high = (address >> 8) & 0xFF
        addr_low = address & 0xFF
        bit_addr = bit_offset & 0x0F
        count_high = 0x00
        count_low = 0x01  # 写入 1 个 word
        
        # 数据部分
        data_high = (value >> 8) & 0xFF
        data_low = value & 0xFF
        
        # 组装命令
        command = struct.pack('>BBBBBBBBBBHBBBBBBB',
                             icf, rsv, gct, dna, da1, da2, sna, sa1, sa2, sid,
                             cmd, area_code, addr_high, addr_low, bit_addr, count_high, count_low,
                             data_high, data_low)
        
        # 添加长度到头部
        length = len(command)
        header = header[:8] + struct.pack('>I', length) + header[12:]
        
        return header + command
    
    def _build_fins_bit_write_command(self, area: int, address: int, bit_offset: int, value: bool) -> bytes:
        """构造 Fins 位写入命令"""
        # 使用位写入命令
        header = b'FINS' + b'\x00' * 12
        
        # Fins 命令头部
        icf = 0x80
        rsv = 0x00
        gct = 0x02
        dna = 0x00
        da1 = 0x00
        da2 = 0x00
        sna = 0x00
        sa1 = 0x00
        sa2 = 0x00
        sid = 0x00
        
        # 位写入命令
        cmd = 0x0102  # Memory Area Write (bit)
        
        # 内存区域指定
        area_code = area & 0xFF
        addr_high = (address >> 8) & 0xFF
        addr_low = address & 0xFF
        bit_addr = bit_offset & 0x0F
        count_high = 0x00
        count_low = 0x01
        
        # 位数据
        bit_data = 0x01 if value else 0x00
        
        # 组装命令
        command = struct.pack('>BBBBBBBBBBHBBBBBB',
                             icf, rsv, gct, dna, da1, da2, sna, sa1, sa2, sid,
                             cmd, area_code, addr_high, addr_low, bit_addr, count_high, count_low,
                             bit_data)
        
        # 添加长度到头部
        length = len(command)
        header = header[:8] + struct.pack('>I', length) + header[12:]
        
        return header + command
    
    def _parse_fins_string_response(self, response: bytes, word_length: int) -> Optional[str]:
        """解析 Fins 字符串响应"""
        try:
            # 跳过头部，获取数据部分
            if len(response) < 20:
                return None
            
            # 检查响应码
            if len(response) > 28 and response[27] != 0x00:
                logger.error(f"Fins 响应错误码: 0x{response[27]:02X}")
                return None
            
            # 提取字符串数据
            data_start = 28  # 跳过头部和响应码
            data_bytes = response[data_start:data_start + word_length * 2]
            
            # 转换为字符串（移除空字符）
            result = data_bytes.decode('ascii', errors='ignore').rstrip('\x00')
            return result
            
        except Exception as e:
            logger.error(f"解析 Fins 字符串响应失败: {str(e)}")
            return None
    
    def _parse_fins_word_response(self, response: bytes) -> Optional[int]:
        """解析 Fins word 响应"""
        try:
            # 跳过头部，获取数据部分
            if len(response) < 30:
                return None
            
            # 检查响应码
            if response[27] != 0x00:
                logger.error(f"Fins 响应错误码: 0x{response[27]:02X}")
                return None
            
            # 提取 word 数据
            data_start = 28
            word_data = struct.unpack('>H', response[data_start:data_start + 2])[0]
            return word_data
            
        except Exception as e:
            logger.error(f"解析 Fins word 响应失败: {str(e)}")
            return None
    
    def _parse_fins_bit_response(self, response: bytes) -> Optional[int]:
        """解析 Fins bit 响应"""
        try:
            # 跳过头部，获取数据部分
            if len(response) < 29:
                return None
            
            # 检查响应码
            if response[27] != 0x00:
                logger.error(f"Fins 响应错误码: 0x{response[27]:02X}")
                return None
            
            # 提取 bit 数据
            bit_data = response[28] & 0x01
            return bit_data
            
        except Exception as e:
            logger.error(f"解析 Fins bit 响应失败: {str(e)}")
            return None
    
    def _check_fins_write_response(self, response: bytes) -> bool:
        """检查 Fins 写入响应"""
        try:
            if len(response) < 28:
                return False
            
            # 检查响应码
            return response[27] == 0x00
            
        except Exception as e:
            logger.error(f"检查 Fins 写入响应失败: {str(e)}")
            return False