use crate::game::{Action, Controllable, Game};
use crate::playfield::{Matrix, PlayField};
use crate::tetromino::{Tetromino, TetrominoType};
use rand::rngs::SmallRng;

pub mod genetic;
pub mod qlearning;

#[derive(Hash, PartialEq, Eq, Clone, Debug)]
pub struct State {
    matrix: Matrix,
    active_piece: Option<Tetromino>,
    held_piece: Option<TetrominoType>,
    line_clears: u32,
}

impl From<Game> for State {
    fn from(game: Game) -> Self {
        (&game).into()
    }
}

impl From<&Game> for State {
    fn from(game: &Game) -> Self {
        let mut state: State = game.playfield().clone().into();
        state.line_clears = game.line_clears;
        state
    }
}

impl From<PlayField> for State {
    fn from(playfield: PlayField) -> Self {
        Self {
            matrix: playfield.field().clone(),
            active_piece: playfield.active_piece,
            held_piece: playfield.hold_piece().map(|t| t.clone()),
            line_clears: 0,
        }
    }
}

pub trait Actor {
    fn get_action(&self, rng: &mut SmallRng, game: &Game, legal_actions: &[Action]) -> Action;

    fn dbg(&self);
}

pub trait Predictable {
    fn get_next_state(&self, action: Action) -> Self;
}

impl Predictable for Game {
    /// Expensive, performs a full clone.
    fn get_next_state(&self, action: Action) -> Self {
        let mut game = self.clone();
        match action {
            Action::Nothing => (),
            Action::MoveLeft => game.move_left(),
            Action::MoveRight => game.move_right(),
            Action::SoftDrop => game.move_down(),
            Action::HardDrop => game.hard_drop(),
            Action::Hold => game.hold(),
            Action::RotateLeft => game.rotate_left(),
            Action::RotateRight => game.rotate_right(),
        };

        game
    }
}

pub fn apply_action_to_game(action: Action, game: &mut Game) {
    match action {
        Action::Nothing => (),
        Action::MoveLeft => game.move_left(),
        Action::MoveRight => game.move_right(),
        Action::SoftDrop => game.move_down(),
        Action::HardDrop => game.hard_drop(),
        Action::Hold => game.hold(),
        Action::RotateLeft => game.rotate_left(),
        Action::RotateRight => game.rotate_right(),
    }
}
