import clr
clr.AddReference('Soti.Connect.SemanticEngine.Conversions')
from System.Collections.Generic import List

from Soti.Connect.SemanticEngine.Conversions.Interpreters import FirmwareInterpreter
from Soti.Connect.SemanticEngine.Conversions.Interpreters import FirmwareMetadata
import re

print("Loading EpsonFirmwareInterpreter")

# Matches a single key=value line, e.g.  3="TM-T88VI"  or  6.2.2="40.50"
_LINE_RE = re.compile(r'^([\d.]+)="([^"]*)"$')
_VERSION_RE = re.compile(r'^\d+\.\d+(\s.*)?$')

def _parse_rcx(file_bytes):
    try:
        text = bytes(file_bytes).decode('ascii', errors='ignore')
    except Exception as e:
        print(f"RCX decode failed: {type(e).__name__} - {e}", e)
        return {}

    fields = {}
    current_section = None

    for raw_line in text.splitlines():
        line = raw_line.strip()

        if line.startswith('[') and line.endswith(']'):
            current_section = line
            continue

        m = _LINE_RE.match(line)
        if not m:
            continue

        key, value = m.group(1), m.group(2)

        # [A]
        if current_section == '[A]':
            if key == '3':
                fields['model'] = value
            elif key == '5':
                fields['command'] = value

        # [E]
        elif current_section == '[E]':
            if key == '6.240.2':
                fields['version'] = value  
            elif key == '6.2.2':
                fields['version'] = value            

    return fields

class EpsonFirmwareInterpreter(FirmwareInterpreter):   
    def GetMetadata(self, fileName, fileBytes):
        models  = List[str]()
        version = ""

        if len(fileBytes) < 3 or bytes(fileBytes[:3]) != b'RCX':
            return FirmwareMetadata(version, models)

        fields = _parse_rcx(fileBytes)

        models = List[str]()

        model_raw   = fields.get('model', '').strip()
        command_raw  = fields.get('command', '').strip()
        version_raw = fields.get('version', '').strip()
        
        if not model_raw or not version_raw or not command_raw:
            return FirmwareMetadata("", models)
        
        if command_raw.upper() != "ESC/POS":
            return FirmwareMetadata("", models)
        
        if not self.ValidateVersion(version_raw):
            return FirmwareMetadata("", models)
        
        version = f"{version_raw} {command_raw}"

        if model_raw == "TM-T88VII":
            models.Add("TM-T88VII")
        elif model_raw == "TM-T88VI":
            models.Add("TM-T88VI")
            models.Add("TM-T88VI-iHub")
        elif model_raw == "TM-T88VI-iHUB":
            models.Add("TM-T88VI")
            models.Add("TM-T88VI-iHub")

        return FirmwareMetadata(version, models)

    def ValidateVersion(self, rawVersion):        
        return bool(_VERSION_RE.match(rawVersion))

    def CompareVersions(self, deviceStateVersionRaw, firmwareFileVersionRaw):

        def split_version(v):
            try:
                parts = v.strip().split(' ')
                version = parts[0]
                command = " ".join(parts[1:]) if len(parts) > 1 else ""
                return version, command
            except:
                return "0.0", ""

        def parse(v):
            try:
                major, minor = split_version(v)[0].split('.')[:2]
                return int(major), int(minor)
            except:
                return 0, 0

        def get_command(v):
            return split_version(v)[1].upper().strip()

        d_major, d_minor = parse(deviceStateVersionRaw)
        f_major, f_minor = parse(firmwareFileVersionRaw)

        d_cmd = get_command(deviceStateVersionRaw)
        f_cmd = get_command(firmwareFileVersionRaw)

        # Command mismatch 
        if d_cmd != f_cmd:
            return 1 if d_cmd > f_cmd else -1

        # Version compare
        if d_major != f_major:
            return 1 if d_major > f_major else -1

        if d_minor != f_minor:
            return 1 if d_minor > f_minor else -1

        return 0
    

plugin = EpsonFirmwareInterpreter("EpsonFirmwareInterpreter")

store.Add(plugin)