use bevy::input::keyboard::{Key, KeyboardInput};
use bevy::prelude::*;

use crate::consts::*;
use crate::gameplay::LevelSize;
use crate::resources::{AppState, Easy, TitleFont};

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Component)]
pub struct Title;

impl Title {

    pub fn load(
        mut commands: Commands,
        title_font: Res<TitleFont>,
        easy: Res<Easy>,
    ) {
        commands.spawn((
            Self,
            Text::new(TITLE),
            TextFont {
                font: title_font.clone_weak(),
                font_size: 48.0,
                ..default()
            },
            TextShadow::default(),
            TextColor(TITLE_COLOR),
            TextLayout::new_with_justify(JustifyText::Center),
            Node {
                position_type: PositionType::Absolute,
                top: Val::ZERO,
                width: Val::Percent(100.0),
                ..default()
            },
        ));

        commands.spawn((
            Self,
            Text::new(APP_VERSION.to_string()),
            TextFont {
                font: title_font.clone_weak(),
                font_size: 18.0,
                ..default()
            },
            TextColor(VERSION_COLOR),
            TextLayout::new_with_justify(JustifyText::Right),
            Node {
                position_type: PositionType::Absolute,
                top: Val::Px(48.0),
                width: Val::Percent(74.6),
                ..default()
            },
        ));

        let slices = 15.0;
        let mut shadow = TextShadow::default();
        shadow.offset = Vec2::ZERO;
        commands
            .spawn((
                Self,
                LevelSize::Size5x5,
                Text::new("5×5"),
                TextFont {
                    font: title_font.clone_weak(),
                    font_size: 84.0,
                    ..default()
                },
                shadow,
                TextColor(TITLE_COLOR),
                TextLayout::new_with_justify(JustifyText::Center),
                Node {
                    position_type: PositionType::Relative,
                    top: Val::Percent(slices),
                    width: Val::Percent(100.0),
                    ..default()
                },
            ))
            .observe(Self::pointer_over_observer)
            .observe(Self::pointer_out_observer)
            .observe(Self::pointer_click_observer)
        ;
        let mut shadow = TextShadow::default();
        shadow.offset = Vec2::ZERO;
        commands
            .spawn((
                Self,
                LevelSize::Size10x5,
                Text::new("10×5"),
                TextFont {
                    font: title_font.clone_weak(),
                    font_size: 84.0,
                    ..default()
                },
                shadow,
                TextColor(TITLE_COLOR),
                TextLayout::new_with_justify(JustifyText::Center),
                Node {
                    position_type: PositionType::Relative,
                    top: Val::Percent(slices * 2.0),
                    width: Val::Percent(100.0),
                    ..default()
                },
            ))
            .observe(Self::pointer_over_observer)
            .observe(Self::pointer_out_observer)
            .observe(Self::pointer_click_observer)
        ;
        let mut shadow = TextShadow::default();
        shadow.offset = Vec2::ZERO;
        commands
            .spawn((
                Self,
                LevelSize::Size10x10,
                Text::new("10×10"),
                TextFont {
                    font: title_font.clone_weak(),
                    font_size: 84.0,
                    ..default()
                },
                shadow,
                TextColor(TITLE_COLOR),
                TextLayout::new_with_justify(JustifyText::Center),
                Node {
                    position_type: PositionType::Relative,
                    top: Val::Percent(slices * 3.0),
                    width: Val::Percent(100.0),
                    ..default()
                },
            ))
            .observe(Self::pointer_over_observer)
            .observe(Self::pointer_out_observer)
            .observe(Self::pointer_click_observer)
        ;
        let mut shadow = TextShadow::default();
        shadow.offset = Vec2::ZERO;
        commands
            .spawn((
                Self,
                LevelSize::Size15x10,
                Text::new("15×10"),
                TextFont {
                    font: title_font.clone_weak(),
                    font_size: 84.0,
                    ..default()
                },
                shadow,
                TextColor(TITLE_COLOR),
                TextLayout::new_with_justify(JustifyText::Center),
                Node {
                    position_type: PositionType::Relative,
                    top: Val::Percent(slices * 4.0),
                    width: Val::Percent(100.0),
                    ..default()
                },
            ))
            .observe(Self::pointer_over_observer)
            .observe(Self::pointer_out_observer)
            .observe(Self::pointer_click_observer)
        ;
        let mut shadow = TextShadow::default();
        shadow.offset = Vec2::ZERO;
        commands
            .spawn((
                Self,
                LevelSize::Size15x15,
                Text::new("15×15"),
                TextFont {
                    font: title_font.clone_weak(),
                    font_size: 84.0,
                    ..default()
                },
                shadow,
                TextColor(TITLE_COLOR),
                TextLayout::new_with_justify(JustifyText::Center),
                Node {
                    position_type: PositionType::Relative,
                    top: Val::Percent(slices * 5.0),
                    width: Val::Percent(100.0),
                    ..default()
                },
            ))
            .observe(Self::pointer_over_observer)
            .observe(Self::pointer_out_observer)
            .observe(Self::pointer_click_observer)
        ;
        let mut shadow = TextShadow {
            offset: Vec2::ZERO,
            color: Color::WHITE,
        };
        let mut color = BLUR_COLOR;
        if easy.0 {
            shadow.offset = Vec2::splat(4.0);
            color = TITLE_COLOR;
        }
        commands
            .spawn((
                EasyItem,
                Text::new("Easy"),
                TextFont {
                    font: title_font.clone_weak(),
                    font_size: 64.0,
                    ..default()
                },
                shadow,
                TextColor(color),
                TextLayout::new_with_justify(JustifyText::Center),
                Node {
                    position_type: PositionType::Relative,
                    top: Val::Percent(slices * 6.0),
                    width: Val::Percent(100.0),
                    ..default()
                },
            ))
            .observe(Self::toggle_easy_observer)
        ;
    }

    pub fn unload(
        mut commands: Commands,
        query: Query<Entity, With<Self>>,
    ) {
        for entity in &query {
            commands.entity(entity).despawn();
        }
    }

    pub fn key_events(
        mut keyboard: EventReader<KeyboardInput>,
        mut exit: EventWriter<AppExit>,
    ) {
        for (input, _) in keyboard.par_read() {
            if input.state.is_pressed() && !input.repeat {
                if input.logical_key == Key::Escape {
                    exit.write(AppExit::Success);
                    return;
                }
            }
        }
    }

    fn pointer_over_observer(
        mut trigger: Trigger<Pointer<Over>>,
        mut commands: Commands,
    ) {
        commands
            .entity(trigger.target())
            .entry::<TextShadow>()
            .and_modify(|mut shadow| shadow.offset = Vec2::splat(4.0))
        ;
        trigger.propagate(false);
    }

    fn pointer_out_observer(
        mut trigger: Trigger<Pointer<Out>>,
        mut commands: Commands,
    ) {
        commands
            .entity(trigger.target())
            .entry::<TextShadow>()
            .and_modify(|mut shadow| shadow.offset = Vec2::ZERO)
        ;
        trigger.propagate(false);
    }

    fn pointer_click_observer(
        mut trigger: Trigger<Pointer<Click>>,
        mut commands: Commands,
        query: Query<(Entity, &LevelSize), With<Self>>,
        mut next_state: ResMut<NextState<AppState>>,
    ) {
        if trigger.event().button == PointerButton::Primary {
            for (entity, &level) in &query {
                if entity == trigger.target() {
                    commands.insert_resource(level);
                    trigger.propagate(false);
                    // XXX: the next_state blocks using EntityEntryCommands::and_modify
                    next_state.set(AppState::Gameplay);
                    return;
                }
            }
        }
    }

    fn toggle_easy_observer(
        mut trigger: Trigger<Pointer<Click>>,
        mut easy: ResMut<Easy>,
    ) {
        if trigger.event().button == PointerButton::Primary {
            easy.0 = !easy.0;
            trigger.propagate(false);
        }
    }

    pub fn update_easy_item(
        mut query: Query<(&mut TextColor, &mut TextShadow), With<EasyItem>>,
        easy: ResMut<Easy>,
    ) {
        if let Ok((mut color, mut shadow)) = query.single_mut() {
            if easy.0 {
                color.0 = TITLE_COLOR;
                shadow.offset = Vec2::splat(4.0);
            } else {
                color.0 = BLUR_COLOR;
                shadow.offset = Vec2::ZERO;
            }
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Component)]
#[require(Title)]
pub struct EasyItem;
