/*
Copyright 2024, Savanni D'Gerinel <savanni@luminescent-dreams.com>

This file is part of Kifu.

Kifu is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Kifu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License along with Kifu. If not, see <https://www.gnu.org/licenses/>.
*/

use crate::CoreApi;
use adw::prelude::*;
use async_std::task::{block_on, spawn};
use kifu_core::settings::SettingsResponse;
use kifu_core::CoreResponse;
use kifu_core::{settings::SettingsRequest, Config, CoreRequest};
use std::sync::{Arc, RwLock};

use crate::views::{SettingsView, HomeView};

#[derive(Clone)]
enum AppView {
    Home,
}

// An application window should generally contain
// - an overlay widget
// - the main content in a stack on the bottom panel of the overlay
// - the settings and the about page in bins atop the overlay
#[derive(Clone)]
pub struct AppWindow {
    pub window: adw::ApplicationWindow,
    header: adw::HeaderBar,

    // content is a stack which contains the view models for the application. These are the main
    // elements that users want to interact with: the home page, the game library, a review, a game
    // itself, perhaps also chat rooms and player lists on other networks. stack contains the
    // widgets that need to be rendered. The two of these work together in order to ensure that
    // we can maintain the state of previous views. Since the two of these work together, they are
    // a candidate for extraction into a new widget or a new struct.
    stack: adw::NavigationView,
    content: Vec<AppView>,

    // Overlays are for transient content, such as about and settings, which can be accessed from
    // anywhere but shouldn't be part of the main application flow.
    panel_overlay: gtk::Overlay,
    core: CoreApi,

    // Not liking this, but I have to keep track of the settings view model separately from
    // anything else. I'll have to look into this later.
    settings_view_model: Arc<RwLock<Option<SettingsView>>>,
}

impl AppWindow {
    pub fn new(app: &adw::Application, core: CoreApi) -> Self {
        let window = Self::setup_window(app);
        let header = Self::setup_header();
        let panel_overlay = Self::setup_panel_overlay();
        let (stack, content) = Self::setup_content(core.clone());

        let layout = gtk::Box::builder()
            .orientation(gtk::Orientation::Vertical)
            .build();
        layout.append(&header);
        layout.append(&panel_overlay);
        panel_overlay.set_child(Some(&stack));

        window.set_content(Some(&layout));

        Self {
            window,
            header,
            stack,
            content,
            panel_overlay,
            core,
            settings_view_model: Default::default(),
        }
    }

    pub fn open_settings(&self) {
        // This should return instantly and allow the UI to continue being functional. However,
        // some tests indicate that this may not actually be working correctly, and that a
        // long-running background thread may delay things.
        glib::spawn_future_local({
            let s = self.clone();
            async move {
                if let CoreResponse::Settings(SettingsResponse(settings)) = s
                    .core
                    .dispatch(CoreRequest::Settings(SettingsRequest::Get))
                    .await
                {
                    let view_model = SettingsView::new(
                        &s.window,
                        settings,
                        {
                            let s = s.clone();
                            move |config| {
                                glib::spawn_future_local({
                                    let s = s.clone();
                                    async move {
                                        s.core
                                            .dispatch(CoreRequest::Settings(SettingsRequest::Set(
                                                config,
                                            )))
                                            .await
                                    }
                                });
                                s.close_overlay();
                            }
                        },
                        {
                            let s = s.clone();
                            move || {
                                s.close_overlay();
                            }
                        },
                    );
                    s.panel_overlay.add_overlay(&view_model);
                    *s.settings_view_model.write().unwrap() = Some(view_model);
                }
            }
        });
    }

    pub fn close_overlay(&self) {
        let mut view = self.settings_view_model.write().unwrap();
        match *view {
            Some(ref mut settings) => {
                self.panel_overlay.remove_overlay(settings);
                *view = None;
            }
            None => {}
        }
    }

    fn setup_window(app: &adw::Application) -> adw::ApplicationWindow {
        let window = adw::ApplicationWindow::builder()
            .application(app)
            .width_request(800)
            .height_request(500)
            .build();

        window
    }

    fn setup_header() -> adw::HeaderBar {
        let header = adw::HeaderBar::builder()
            .title_widget(&gtk::Label::new(Some("Kifu")))
            .build();

        let app_menu = gio::Menu::new();
        let menu_item = gio::MenuItem::new(Some("Configuration"), Some("app.show_settings"));
        app_menu.append_item(&menu_item);

        let hamburger = gtk::MenuButton::builder()
            .icon_name("open-menu-symbolic")
            .build();
        hamburger.set_menu_model(Some(&app_menu));

        header.pack_end(&hamburger);
        header
    }

    fn setup_panel_overlay() -> gtk::Overlay {
        gtk::Overlay::new()
    }

    fn setup_content(core: CoreApi) -> (adw::NavigationView, Vec<AppView>) {
        let stack = adw::NavigationView::new();
        let content = Vec::new();

        let home = HomeView::new(core.clone());
        let _ = stack.push(
            &adw::NavigationPage::builder()
                .can_pop(false)
                .title("Kifu")
                .child(&home)
                .build(),
        );
        // content.push(AppView::Home(HomeViewModel::new(core)));

        (stack, content)
    }

    // pub fn set_content(content: &impl IsA<gtk::Widget>) -> adw::ViewStack {
    // self.content.set_child(Some(content));
    // }
}