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

This file is part of On the Grid.

On the Grid 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.

On the Grid 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 On the Grid. If not, see <https://www.gnu.org/licenses/>.
*/

// Game review consists of the board, some information about the players, the game tree, and any
// commentary on the current move. This requires four major components, some of which are easier
// than others. The game board has to be kept in sync with the game tree, so there's a
// communication channel there.
//
// I'll get all of the information about the game from the core, and then render everything in the
// UI. So this will be a heavy lift on the UI side.

use std::{cell::RefCell, rc::Rc};

use crate::{
    components::{Goban, PlayerCard, ReviewTree},
    ResourceManager,
};
use glib::Propagation;
use gtk::{gdk::Key, prelude::*, EventControllerKey};
use otg_core::{Color, GameReviewViewModel};

/*
#[derive(Default)]
pub struct GameReviewPrivate {
    model: Rc<RefCell<Option<GameReviewViewModel>>>,
}

#[glib::object_subclass]
impl ObjectSubclass for GameReviewPrivate {
    const NAME: &'static str = "GameReview";
    type Type = GameReview;
    type ParentType = gtk::Box;
}

impl ObjectImpl for GameReviewPrivate {}
impl WidgetImpl for GameReviewPrivate {}
impl BoxImpl for GameReviewPrivate {}

glib::wrapper! {
    pub struct GameReview(ObjectSubclass<GameReviewPrivate>) @extends gtk::Box, gtk::Widget, @implements gtk::Accessible;
}

impl GameReview {
    pub fn new(_api: CoreApi, record: GameRecord, resources: ResourceManager) -> Self {
        let s: Self = Object::builder().build();


        s
    }
}
*/

#[derive(Clone)]
pub struct GameReview {
    widget: gtk::Box,
    goban: Rc<RefCell<Option<Goban>>>,
    review_tree: Rc<RefCell<Option<ReviewTree>>>,

    resources: ResourceManager,
    view: Rc<RefCell<GameReviewViewModel>>,
}

impl GameReview {
    pub fn new(view: GameReviewViewModel, resources: ResourceManager) -> Self {
        let widget = gtk::Box::builder().build();

        let view = Rc::new(RefCell::new(view));

        let s = Self {
            widget,
            goban: Default::default(),
            review_tree: Default::default(),
            resources,
            view,
        };

        let keypress_controller = EventControllerKey::new();
        keypress_controller.connect_key_pressed({
            let s = s.clone();
            move |_, key, _, _| {
                let mut view = s.view.borrow_mut();
                match key {
                    Key::Down => view.next_move(),
                    Key::Up => view.previous_move(),
                    Key::Left => view.previous_variant(),
                    Key::Right => view.next_variant(),
                    _ => {
                        return Propagation::Proceed;
                    }
                }

                match *s.goban.borrow_mut() {
                    Some(ref mut goban) => goban.set_board_state(view.game_view()),
                    None => {}
                };
                match *s.review_tree.borrow() {
                    Some(ref tree) => tree.queue_draw(),
                    None => {}
                }
                Propagation::Stop
            }
        });

        s.widget.add_controller(keypress_controller);

        s.render();

        s
    }

    fn render(&self) {
        // It's actually really bad to be just throwing away errors. Panics make everyone unhappy.
        // This is not a fatal error, so I'll replace this `unwrap` call with something that
        // renders the board and notifies the user of a problem that cannot be resolved.
        let board_repr = self.view.borrow().game_view();
        let board = Goban::new(board_repr, self.resources.clone());

        /*
        s.attach(&board, 0, 0, 2, 2);
        s.attach(&gtk::Label::new(Some("white player")), 0, 2, 1, 1);
        s.attach(&gtk::Label::new(Some("black player")), 0, 2, 1, 2);
        s.attach(&gtk::Label::new(Some("chat")), 1, 2, 2, 2);
        */

        let sidebar = gtk::Box::builder()
            .orientation(gtk::Orientation::Vertical)
            .spacing(4)
            .build();

        // The review tree needs to know the record for being able to render all of the nodes. Once
        // keyboard input is being handled, the tree will have to be updated on each keystroke in
        // order to show the user where they are within the game record.
        let review_tree = ReviewTree::new(self.view.borrow().clone());

        // I think most keyboard focus is going to end up being handled here in GameReview, as
        // keystrokes need to affect both the goban and the review tree simultanesouly. Possibly
        // also the player information, seeing as moving to a new position may mean that the score
        // and time remaining can be updated.

        let player_information_section = gtk::Box::builder()
            .orientation(gtk::Orientation::Horizontal)
            .spacing(4)
            .build();

        player_information_section
            .append(&PlayerCard::new(Color::Black, &self.view.borrow().black_player()));
        player_information_section
            .append(&PlayerCard::new(Color::White, &self.view.borrow().white_player()));

        self.widget.append(&board);
        sidebar.append(&player_information_section);
        sidebar.append(&review_tree.widget());
        self.widget.append(&sidebar);

        *self.goban.borrow_mut() = Some(board);
        *self.review_tree.borrow_mut() = Some(review_tree);
    }

    fn redraw(&self) {
    }

    pub fn widget(&self) -> gtk::Widget {
        self.widget.clone().upcast::<gtk::Widget>()
    }
}
