# Copyright 2024-2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
from dataclasses import dataclass
from enum import Enum
from audio_dsp.tuning import ConfigSender
from typing import Callable
from queue import Empty
from pathlib import Path
from .usb import USBTransport
from audio_dsp.design.parse_json import DspJson, update_pipeline
from audio_dsp.design.pipeline import generate_dsp_main
import platform
import shutil


@dataclass
class StateUpdated:
    params: DspJson


@dataclass
class SendTuning:
    return_cb: Callable


@dataclass
class Exit:
    pass


class _Device:
    def __init__(self, pipeline, params: DspJson, logger):
        self._last_params = None
        self.pipeline = pipeline
        self.running = True
        self.params = params
        self._log = logger

        system = platform.system()
        if system == "Windows":
            host_app_name = "dsp_host.exe"
        elif system == "Linux":
            host_app_name = "dsp_host_linux"
        elif system == "Darwin":
            host_app_name = "dsp_host_macos"
        else:
            raise RuntimeError(f"Unsupported platform: {system}")

        bin_path = Path(__file__).parent / "host_bin"
        host_app_path = shutil.which(host_app_name, path=bin_path)
        assert host_app_path is not None, f"Host application {host_app_name} not found in {bin_path}"

        self.transport = ConfigSender(USBTransport(host_app_path=host_app_path), "once")

    def run(self, queue):
        while self.running:
            try:
                command = queue.get(timeout=1)
            except Empty:
                continue

            if isinstance(command, StateUpdated):
                self.state_updated(command)
            elif isinstance(command, SendTuning):
                self.send_tuning(command)
            elif isinstance(command, Exit):
                self.running = False
            else:
                self._log.log(f"Unknown command {type(command)}")

    def send_tuning(self, command: SendTuning):
        self._log.log("Sending Tuning")
        try:
            self.transport(self.pipeline)
        except Exception as e:
            self._log.log("Failed!")
            self._log.log(f"{type(e)} {e}")
        else:
            self._log.log("Success!")
        command.return_cb(True)

    def state_updated(self, command: StateUpdated):
        self._log.log("State updated")
        self.params = command.params
        try:
            update_pipeline(self.pipeline, self.params)
        except (ValueError, TypeError, ZeroDivisionError) as e:
            self._log.log("Tuning error: " + str(e))


def device_thread(queue, pipeline, init_params, logger):
    logger.log("On device thread")
    _Device(pipeline, init_params, logger).run(queue)
