#![cfg_attr(
    all(not(debug_assertions), target_os = "windows"),
    windows_subsystem = "windows"
)]

use std::collections::HashMap;
use std::process::Child;
use std::thread::spawn;
use std::{path::PathBuf, process::Command};

use egui::Sense;
use egui::{self, Label};
use indexmap::IndexSet;
use macroquad::prelude::*;
use miniquad::window::{dropped_file_count, dropped_file_path};
use rocket::futures::StreamExt;
use workspace::{egui_mq, LogFile, WorkspaceClientMessage, WorkspaceServerMessage};

fn window_conf() -> Conf {
    Conf {
        window_title: "Log History".to_owned(),
        high_dpi: true,
        ..Default::default()
    }
}

#[macroquad::main(window_conf)]
async fn main() {
    let log_viewer_path = std::env::current_exe()
        .ok()
        .and_then(|exe| exe.parent().map(|p| p.to_path_buf()))
        .map(|exe| exe.join("fast_log_viewer.exe"))
        .unwrap_or_else(|| PathBuf::from("fast_log_viewer.exe"));

    let mut log_history: IndexSet<LogFile> = IndexSet::new();
    let mut open_files = HashMap::<PathBuf, Child>::new();

    let (incoming_tx, incoming_rx) = flume::bounded(1024);
    let (outgoing_tx, outgoing_rx) = flume::bounded::<(&str, Vec<u8>)>(1024);

    spawn(move || {
        tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .build()
            .unwrap()
            .block_on(async {
                let nats = async_nats::connect("nats://localhost:42666").await.unwrap();
                nats.publish(
                    "workspace.server",
                    bincode::serialize(&WorkspaceClientMessage::GetLogHistory)
                        .unwrap()
                        .into(),
                )
                .await
                .unwrap();
                let mut sub = nats.subscribe("workspace.log_history").await.unwrap();
                tokio::spawn(async move {
                    while let Some(msg) = sub.next().await {
                        if let Ok(msg) =
                            bincode::deserialize::<WorkspaceServerMessage>(&msg.payload)
                        {
                            match msg {
                                WorkspaceServerMessage::LogHistory(history) => {
                                    incoming_tx.send_async(history).await.unwrap();
                                }
                                #[allow(unreachable_patterns)]
                                _ => (),
                            }
                        }
                    }
                });

                while let Ok((topic, msg)) = outgoing_rx.recv_async().await {
                    nats.publish(topic, msg.into()).await.unwrap();
                }
            });
    });

    loop {
        clear_background(WHITE);

        egui_mq::ui(|ctx| {
            egui::SidePanel::left("side_panel")
                .exact_width(500.0)
                .show(ctx, |ui| {
                    ui.heading("Log History");
                    ui.separator();
                    egui::ScrollArea::vertical().show(ui, |ui| {
                        // let mut to_swap = None;
                        let mut to_delete = None;
                        if let Ok(history) = incoming_rx.try_recv() {
                            log_history = history;
                        }
                        let history_len = log_history.len();
                        for (i, log) in log_history.iter().enumerate() {
                            let path_text = log.path.to_string_lossy().to_string();
                            let label = ui
                                .add(Label::new(&log.name).sense(Sense::click()))
                                .on_hover_text(&path_text);
                            label.context_menu(|ui| {
                                if ui.button("Open in Notepad").clicked() {
                                    _ = Command::new("notepad.exe").arg(&path_text).spawn();
                                    ui.close_menu();
                                }
                                if ui.button("Remove").clicked() {
                                    to_delete = Some(i);
                                    ui.close_menu();
                                }
                            });
                            if label.clicked() {
                                if let Some(child) = open_files.get_mut(&log.path) {
                                    if let Ok(Some(_)) = child.try_wait() {
                                        *child = Command::new(&log_viewer_path)
                                            .arg(&path_text)
                                            .spawn()
                                            .unwrap();
                                    }
                                } else {
                                    open_files.insert(
                                        log.path.clone(),
                                        Command::new(&log_viewer_path)
                                            .arg(&path_text)
                                            .spawn()
                                            .unwrap(),
                                    );
                                }
                                // to_swap = Some(i);
                            }
                            if i < history_len - 1 {
                                ui.separator();
                            }
                        }
                        // if let Some(i) = to_swap {
                        //     if let Some(value) = log_history.swap_remove_index(i) {
                        //         log_history.shift_insert(0, value);
                        //     }
                        // }
                        // if let Some(i) = to_delete {
                        //     _ = log_history.shift_remove_index(i);
                        // }
                    })
                });
        });

        let dropped_file_count = dropped_file_count();
        if dropped_file_count > 0 {
            for i in 0..dropped_file_count {
                if let Some(file) = dropped_file_path(i) {
                    outgoing_tx
                        .send((
                            "workspace.server",
                            bincode::serialize(&LogFile {
                                name: file.file_name().unwrap().to_string_lossy().to_string(),
                                path: file,
                            })
                            .unwrap()
                            .into(),
                        ))
                        .unwrap();
                }
            }
        }

        egui_mq::draw();

        next_frame().await
    }
}
