use gtk::prelude::*;
use kifu_core::{CoreApp, Request, Response};
use kifu_gtk::ui::PlayingField;
use std::{
    sync::{Arc, Mutex},
    time::Duration,
};
use tokio::{
    runtime::Runtime,
    sync::mpsc::{Receiver, Sender},
};

#[derive(Clone)]
pub struct CoreApi {
    gtk_tx: gtk::glib::Sender<Response>,
    rt: Arc<Runtime>,
    core: CoreApp,
}

impl CoreApi {
    pub fn dispatch(&self, request: Request) {
        self.rt.spawn({
            let gtk_tx = self.gtk_tx.clone();
            let core = self.core.clone();
            async move { gtk_tx.send(core.dispatch(request).await) }
        });
    }
}

fn main() {
    let runtime = Arc::new(
        tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .build()
            .unwrap(),
    );

    let core = CoreApp::new();

    let core_handle = runtime.spawn({
        let core = core.clone();
        async move {
            core.run().await;
        }
    });

    let app = gtk::Application::builder()
        .application_id("com.luminescent-dreams.kifu-gtk")
        .build();

    app.connect_activate({
        let runtime = runtime.clone();
        move |app| {
            let (gtk_tx, gtk_rx) =
                gtk::glib::MainContext::channel::<Response>(gtk::glib::PRIORITY_DEFAULT);

            let api = CoreApi {
                gtk_tx,
                rt: runtime.clone(),
                core: core.clone(),
            };

            let window = gtk::ApplicationWindow::new(app);
            window.present();

            gtk_rx.attach(None, move |message| {
                println!("message: {:?}", message);
                match message {
                    Response::PlayingFieldView(view) => {
                        let playing_field = PlayingField::new(view);
                        window.set_child(Some(&playing_field));
                    }
                }
                Continue(true)
            });

            api.dispatch(Request::PlayingField);
        }
    });

    println!("running the gtk loop");
    app.run();

    let _ = runtime.block_on(async { core_handle.await });
}