use crate::{
    graphics::*,
    playfield::{PLAYFIELD_HEIGHT, PLAYFIELD_WIDTH},
};

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum TetrominoType {
    I,
    O,
    T,
    S,
    Z,
    J,
    L,
}

impl Into<MinoColor> for TetrominoType {
    fn into(self) -> MinoColor {
        match self {
            Self::I => MinoColor::Cyan,
            Self::O => MinoColor::Yellow,
            Self::T => MinoColor::Purple,
            Self::S => MinoColor::Green,
            Self::Z => MinoColor::Red,
            Self::J => MinoColor::Blue,
            Self::L => MinoColor::Orange,
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum RotationState {
    O, // initial state
    R, // clockwise rotation
    L, // counter-clockwise rotation
    U, // 180 deg rotation
}

impl Default for RotationState {
    fn default() -> Self {
        RotationState::O
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Position {
    pub x: isize,
    pub y: isize,
}

impl Position {
    pub fn new(x: isize, y: isize) -> Position {
        Self {
            x: x as isize,
            y: y as isize,
        }
    }

    pub fn offset(&self, x: isize, y: isize) -> Position {
        Self {
            x: x + self.x,
            y: y + self.y,
        }
    }
}

impl std::ops::Add for Position {
    type Output = Self;
    fn add(self, other: Self) -> Self {
        Self {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

impl std::ops::Sub for Position {
    type Output = Self;
    fn sub(self, other: Self) -> Self {
        Self {
            x: self.x - other.x,
            y: self.y - other.y,
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Tetromino {
    pub position: Position,
    pub piece_type: TetrominoType,
    pub rotation_state: RotationState,
}

impl Tetromino {
    pub fn new(tetromino_type: TetrominoType) -> Self {
        Self {
            position: Self::get_start_position(tetromino_type),
            piece_type: tetromino_type,
            rotation_state: RotationState::default(),
        }
    }

    fn get_start_position(tetromino_type: TetrominoType) -> Position {
        if tetromino_type == TetrominoType::I {
            Position::new(
                PLAYFIELD_WIDTH as isize / 2 - 1,
                PLAYFIELD_HEIGHT as isize - 1,
            )
        } else {
            Position::new(PLAYFIELD_WIDTH as isize / 2 - 1, PLAYFIELD_HEIGHT as isize)
        }
    }

    pub fn get_falling_occupied_spaces(&self) -> Vec<Position> {
        self.get_occupied_spaces(self.position.offset(0, 1))
    }

    pub fn get_cur_occupied_spaces(&self) -> Vec<Position> {
        self.get_occupied_spaces(self.position)
    }

    pub fn get_occupied_spaces(&self, center: Position) -> Vec<Position> {
        let mut spaces = vec![center];
        match self.piece_type {
            TetrominoType::I => match self.rotation_state {
                RotationState::O => spaces.extend_from_slice(&[
                    center.offset(-1, 0),
                    center.offset(1, 0),
                    center.offset(2, 0),
                ]),
                RotationState::R => spaces.extend_from_slice(&[
                    center.offset(0, -1),
                    center.offset(0, 1),
                    center.offset(0, 2),
                ]),
                RotationState::U => spaces.extend_from_slice(&[
                    center.offset(-2, 0),
                    center.offset(-1, 0),
                    center.offset(1, 0),
                ]),
                RotationState::L => spaces.extend_from_slice(&[
                    center.offset(0, -2),
                    center.offset(0, -1),
                    center.offset(0, 1),
                ]),
            },
            TetrominoType::J => match self.rotation_state {
                RotationState::O => spaces.extend_from_slice(&[
                    center.offset(-1, -1),
                    center.offset(-1, 0),
                    center.offset(1, 0),
                ]),
                RotationState::R => spaces.extend_from_slice(&[
                    center.offset(0, -1),
                    center.offset(1, -1),
                    center.offset(0, 1),
                ]),
                RotationState::U => spaces.extend_from_slice(&[
                    center.offset(-1, 0),
                    center.offset(1, 0),
                    center.offset(1, 1),
                ]),
                RotationState::L => spaces.extend_from_slice(&[
                    center.offset(0, -1),
                    center.offset(-1, 1),
                    center.offset(0, 1),
                ]),
            },
            TetrominoType::L => match self.rotation_state {
                RotationState::O => spaces.extend_from_slice(&[
                    center.offset(1, -1),
                    center.offset(-1, 0),
                    center.offset(1, 0),
                ]),
                RotationState::R => spaces.extend_from_slice(&[
                    center.offset(0, -1),
                    center.offset(0, 1),
                    center.offset(1, 1),
                ]),
                RotationState::U => spaces.extend_from_slice(&[
                    center.offset(-1, 0),
                    center.offset(1, 0),
                    center.offset(-1, 1),
                ]),
                RotationState::L => spaces.extend_from_slice(&[
                    center.offset(-1, -1),
                    center.offset(0, -1),
                    center.offset(0, 1),
                ]),
            },
            TetrominoType::O => match self.rotation_state {
                RotationState::O => spaces.extend_from_slice(&[
                    center.offset(0, -1),
                    center.offset(1, -1),
                    center.offset(1, 0),
                ]),
                RotationState::R => spaces.extend_from_slice(&[
                    center.offset(1, 0),
                    center.offset(0, 1),
                    center.offset(1, 1),
                ]),
                RotationState::U => spaces.extend_from_slice(&[
                    center.offset(-1, 0),
                    center.offset(-1, 1),
                    center.offset(0, 1),
                ]),
                RotationState::L => spaces.extend_from_slice(&[
                    center.offset(-1, -1),
                    center.offset(0, -1),
                    center.offset(-1, 0),
                ]),
            },
            TetrominoType::S => match self.rotation_state {
                RotationState::O => spaces.extend_from_slice(&[
                    center.offset(0, -1),
                    center.offset(1, -1),
                    center.offset(-1, 0),
                ]),
                RotationState::R => spaces.extend_from_slice(&[
                    center.offset(0, -1),
                    center.offset(1, 0),
                    center.offset(1, 1),
                ]),
                RotationState::U => spaces.extend_from_slice(&[
                    center.offset(0, 1),
                    center.offset(1, 0),
                    center.offset(-1, 1),
                ]),
                RotationState::L => spaces.extend_from_slice(&[
                    center.offset(-1, -1),
                    center.offset(-1, 0),
                    center.offset(0, 1),
                ]),
            },
            TetrominoType::T => match self.rotation_state {
                RotationState::O => spaces.extend_from_slice(&[
                    center.offset(0, -1),
                    center.offset(-1, 0),
                    center.offset(1, 0),
                ]),
                RotationState::R => spaces.extend_from_slice(&[
                    center.offset(0, -1),
                    center.offset(0, 1),
                    center.offset(1, 0),
                ]),
                RotationState::U => spaces.extend_from_slice(&[
                    center.offset(0, 1),
                    center.offset(-1, 0),
                    center.offset(1, 0),
                ]),
                RotationState::L => spaces.extend_from_slice(&[
                    center.offset(0, -1),
                    center.offset(-1, 0),
                    center.offset(0, 1),
                ]),
            },
            TetrominoType::Z => match self.rotation_state {
                RotationState::O => spaces.extend_from_slice(&[
                    center.offset(-1, -1),
                    center.offset(0, -1),
                    center.offset(1, 0),
                ]),
                RotationState::R => spaces.extend_from_slice(&[
                    center.offset(1, -1),
                    center.offset(1, 0),
                    center.offset(0, 1),
                ]),
                RotationState::U => spaces.extend_from_slice(&[
                    center.offset(-1, 0),
                    center.offset(0, 1),
                    center.offset(1, 1),
                ]),
                RotationState::L => spaces.extend_from_slice(&[
                    center.offset(0, -1),
                    center.offset(-1, 0),
                    center.offset(-1, 1),
                ]),
            },
        }

        spaces
    }

    pub fn rotate_left(&mut self) {
        self.rotation_state = match self.rotation_state {
            RotationState::O => RotationState::L,
            RotationState::L => RotationState::U,
            RotationState::U => RotationState::R,
            RotationState::R => RotationState::O,
        }
    }

    pub fn rotate_right(&mut self) {
        self.rotation_state = match self.rotation_state {
            RotationState::O => RotationState::R,
            RotationState::R => RotationState::U,
            RotationState::U => RotationState::L,
            RotationState::L => RotationState::O,
        }
    }
}

impl From<TetrominoType> for Tetromino {
    fn from(tetromino_type: TetrominoType) -> Self {
        Self::new(tetromino_type)
    }
}
