import logging
import multiprocessing
import os
import shutil
import subprocess
import tomllib
from enum import Enum
from pathlib import Path

import click
import platformdirs

from flandre import C
from flandre import P
from flandre.BusClient import BusClient
from flandre.kde_pyqt6_mainui import kde_pyqt6_mainui
from flandre.nodes.Beamformer import Beamformer
from flandre.nodes.Broker import Broker
from flandre.nodes.Device import Device
from flandre.nodes.ImageCV import ImageCV
from flandre.nodes.ImageFFMPEG import ImageFFMPEG
from flandre.nodes.ImageQt import ImageQt
from flandre.nodes.Loader import Loader
from flandre.nodes.MainUI import MainUI
from flandre.nodes.Mi import Mi
from flandre.nodes.Midi import Midi
from flandre.nodes.Muxer import Muxer
from flandre.nodes.Recorder import Recorder
from flandre.nodes.Robot import Robot
from flandre.nodes.VideoQt import VideoQt
from flandre.nodes.Web import Web
from flandre.utils.Msg import KillMsg, NodeOnlineMsg, Msg1, Msg2


class LaunchComponent(Enum):
    MainUI = MainUI
    ImageCV = ImageCV
    Muxer = Muxer
    Robot = Robot
    Loader = Loader
    Device = Device
    Recorder = Recorder
    Beamformer = Beamformer
    ImageFFMPEG = ImageFFMPEG
    ImageQt = ImageQt
    Midi = Midi
    Mi = Mi
    Web = Web
    VideoQt = VideoQt


def launch(arg: dict[LaunchComponent, dict]):
    logging.basicConfig(level=logging.INFO)
    multiprocessing.set_start_method('spawn')
    bp = multiprocessing.Process(target=Broker(broker=True), kwargs=dict(software_config=C))
    bp.start()
    ps = []
    for k, v in arg.items():
        if k == LaunchComponent.MainUI and os.environ.get('XDG_CURRENT_DESKTOP', None) == 'KDE':
            ps.append(kde_pyqt6_mainui)
            continue
        ps.append(k.value(**v))

    pps = []
    for p in ps:
        pps.append(multiprocessing.Process(target=p, kwargs=dict(software_config=C)))
    for p in pps:
        p.start()

    c = BusClient(KillMsg, NodeOnlineMsg)
    cnt = 0
    ready = False
    dd = dict()
    while True:
        if ready:
            msg = c.recv()
        else:
            c.send(Msg2())
            msg = c.poll(100)
        if msg is None:
            continue
        if isinstance(msg, KillMsg):
            if msg.name == '':
                break
        if not ready:
            if isinstance(msg, NodeOnlineMsg):
                if msg.name not in dd:
                    logging.info(msg)
                dd[msg.name] = 1
                if dd.keys().__len__() == len(ps):
                    logging.info(f'launcher stand by ready')
                    c.send(Msg1())
                    ready = True
    for p in pps:
        p.kill()
    bp.kill()


def launch_from_file(file: Path):
    assert file.exists()
    r = tomllib.loads(file.read_text())
    arg_d = dict()
    for k, v in r.items():
        arg_d[LaunchComponent[k]] = v
    launch(arg_d)


@click.command()
@click.option('--data_folder', default=None)
@click.option('--generate_pyqt', default=True)
@click.option('--dev/--no-dev', default=True)
@click.option('-p', '--path', type=str,
              default=platformdirs.user_config_path('Flandre', 'Scarlet') / 'launch.toml',
              help='Path to launch.toml'
              )
def entrypoint(data_folder, generate_pyqt, dev, path):
    if dev:
        C.config_folder = P.DEV_PROJECT_FOLDER / 'config'
    else:
        C.read_config(C.software_config_file)

    if (pyuic6 := shutil.which('pyuic6')) is None:
        print('pyuic6 is not installed')
        return
    if generate_pyqt:
        subprocess.run([pyuic6, '-o', P.PYQT / 'Main.py', P.PYQT / 'Main.ui'])
        subprocess.run([pyuic6, '-o', P.PYQT / 'Image.py', P.PYQT / 'Image.ui'])
    if data_folder is not None:
        C.data_folder = Path(data_folder)

    path = Path(path)
    if not path.exists():
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text('[MainUI]\n')
    print('Use launch config', path)
    launch_from_file(path)


if __name__ == '__main__':
    entrypoint()
