//! module for displaying current game state

use std::collections::HashMap;

use crate::app::GameReader;
use heck::ToTitleCase;
use leptos::{html::Canvas, *};
use magister::color::Color;
use plotters::{
    chart::{ChartBuilder, LabelAreaPosition},
    coord::ranged1d::{IntoSegmentedCoord, SegmentValue},
    drawing::IntoDrawingArea,
    series::Histogram,
    style::{Color as _, RED, WHITE},
};
use plotters_canvas::CanvasBackend;
use web_sys::HtmlCanvasElement;

#[component]
pub fn Player(#[prop(into)] index: MaybeSignal<usize>) -> impl IntoView {
    // Display the manapool and balance
    const COLORS: &[Color] = &Color::all();
    let game = expect_context::<GameReader>();
    let player_data = move || {
        game.0.with(|gs| {
            let gs = gs.as_ref().ok()?;
            Some(gs.players()[index.get()].clone())
        })
    };

    view! {
        {move || {
            player_data()
                .map(|pd| {
                    let id = pd.id();
                    let balance = pd.balance;
                    let pool = pd.mana_pool;
                    let (gen_count, set_gen_count) = create_signal(1usize);
                    let (has_error, set_has_error) = create_signal(false);
                    let (gen_mana, set_gen_mana) = create_signal(None);
                    let (all_gen_mana, set_all_gen_mana) = create_signal(HashMap::new());
                    let plot_ref = create_node_ref::<Canvas>();
                    let plotted = move |plot_ref: &HtmlElement<Canvas>, data: &[(Color, usize)]| {
                        let backend = CanvasBackend::with_canvas_object(
                                Clone::clone(HtmlCanvasElement::as_ref(plot_ref)),
                            )
                            .expect("plotters canvas failed to initialize")
                            .into_drawing_area();
                        backend.fill(&WHITE.mix(1.0)).expect("failed to clear canvas");
                        let mut chart = ChartBuilder::on(&backend)
                            .set_label_area_size(LabelAreaPosition::Left, 40)
                            .set_label_area_size(LabelAreaPosition::Bottom, 40)
                            .build_cartesian_2d(
                                (0usize..6).into_segmented(),
                                0..all_gen_mana
                                    .get()
                                    .values()
                                    .copied()
                                    .max()
                                    .map(|x| x + 3)
                                    .unwrap_or(3),
                            )
                            .expect("Failed to create chart");
                        const COLORS: &[Color] = &Color::all();
                        chart
                            .configure_mesh()
                            .disable_x_mesh()
                            .x_desc("Color")
                            .y_desc("Count")
                            .y_labels(5)
                            .x_labels(7)
                            .x_label_formatter(
                                &|idx| {
                                    match idx {
                                        SegmentValue::Exact(idx) => format!("{}?", COLORS[*idx]),
                                        SegmentValue::CenterOf(idx) => {
                                            COLORS
                                                .get(*idx)
                                                .map(ToString::to_string)
                                                .unwrap_or(String::new())
                                        }
                                        SegmentValue::Last => String::new(),
                                    }
                                },
                            )
                            .draw()
                            .expect("Failed to draw axises");
                        chart
                            .draw_series(
                                Histogram::vertical(&chart)
                                    .style(RED.mix(0.5).filled())
                                    .data(
                                        data
                                            .iter()
                                            .map(|(c, i)| (
                                                COLORS.iter().position(|oc| oc == c).unwrap(),
                                                *i,
                                            )),
                                    ),
                            )
                            .expect("Failed to draw data");
                        backend.present().expect("failed to present chart");
                    };
                    create_effect(move |_| {
                        let data = all_gen_mana.get();
                        if let Some(plot_ref) = plot_ref.get().as_ref() {
                            plotted(plot_ref, &data.into_iter().collect::<Vec<_>>())
                        }
                    });
                    view! {
                        <div>
                            <h2 class="italic text-xl">
                                "Player " {move || index.get() + 1} " : "
                                <span class="font-mono not-italic bg-base-200 rounded p-1">
                                    {move || format!("{id}")}
                                </span>
                            </h2>
                            <ul>
                                {COLORS
                                    .iter()
                                    .map(|c| {
                                        view! {
                                            <li>
                                                {c.color().to_title_case()} " Mana: "
                                                {pool.get(c).copied().unwrap_or(0)} " / (" {balance.get(*c)}
                                                ")"
                                            </li>
                                        }
                                    })
                                    .collect_view()}
                            </ul>
                            <div class="join">
                                <input
                                    class="input input-bordered join-item"
                                    type="number"
                                    prop:value=move || gen_count.get()
                                    class:has-error=has_error
                                    on:input=move |ev| {
                                        set_gen_count
                                            .set(
                                                match event_target_value(&ev).parse() {
                                                    Ok(val) => {
                                                        set_has_error.set(false);
                                                        val
                                                    }
                                                    Err(_e) => {
                                                        set_has_error.set(true);
                                                        return;
                                                    }
                                                },
                                            );
                                    }
                                />

                                <button
                                    class="btn join-item"
                                    on:click=move |_| {
                                        let generated_mana: Vec<_> = balance
                                            .gen_mana(&mut rand::thread_rng())
                                            .take(gen_count.get())
                                            .collect();
                                        let counts = Color::all()
                                            .into_iter()
                                            .map(|c| (
                                                c,
                                                generated_mana.iter().filter(|col| col == &&c).count(),
                                            ))
                                            .collect::<HashMap<_, _>>();
                                        set_all_gen_mana
                                            .update(|all_gen_mana| {
                                                for color in Color::all() {
                                                    *all_gen_mana.entry(color).or_insert(0)
                                                        += counts.get(&color).copied().unwrap_or(0);
                                                }
                                            });
                                        set_gen_mana.set(Some(generated_mana));
                                    }
                                >

                                    "Generate Mana"
                                </button>
                            </div>
                            <div class="font-mono">
                                {move || {
                                    if let Some(gen_mana) = gen_mana.get() {
                                        let mapped = Color::all()
                                            .into_iter()
                                            .map(|c| (
                                                c,
                                                gen_mana.iter().filter(|col| col == &&c).count(),
                                            ))
                                            .collect::<HashMap<_, _>>();
                                        format!("{mapped:?}")
                                    } else {
                                        "".to_string()
                                    }
                                }}

                            </div>
                            <Show when=move || {
                                all_gen_mana.with(|agm| agm.values().copied().sum::<usize>() > 0)
                            }>
                                <div>
                                    "Total Mana: "
                                    {move || all_gen_mana.with(|agm| agm.values().sum::<usize>())}
                                </div>
                                <canvas width="600" height="200" _ref=plot_ref></canvas>
                            </Show>
                        </div>
                    }
                })
        }}
    }
}