use crate::{
    game::Game,
    graphics::{MinoColor, Renderable, BORDER_RADIUS, CELL_SIZE, COLOR_BACKGROUND, UI_PADDING},
    playfield::{PlayField, PLAYFIELD_HEIGHT, PLAYFIELD_WIDTH},
    tetromino::{Position, RotationState, Tetromino, TetrominoType},
};
use log::error;
use sdl2::{
    pixels::Color,
    rect::Rect,
    render::{Canvas, RenderTarget},
    video::Window,
};

pub fn render(canvas: &mut Canvas<Window>, game: &Game) {
    let (x, y) = canvas.window().size();
    let game_width = game.width() as i32;
    let game_height = game.height() as i32;
    canvas.set_viewport(Some(Rect::new(
        x as i32 / 2 - game_width / 2 as i32,
        y as i32 / 2 - game_height / 2 as i32,
        game_width as u32,
        game_height as u32,
    )));
    match game.render(canvas) {
        Ok(_) => (),
        Err(e) => error!("{}", e),
    };
}

impl Renderable for Game {
    fn width(&self) -> usize {
        self.playfield().width()
    }

    fn height(&self) -> usize {
        self.playfield().height()
    }

    fn render<R: RenderTarget>(&self, canvas: &mut Canvas<R>) -> Result<(), String> {
        self.playfield().render(canvas)
    }
}

impl Renderable for PlayField {
    fn width(&self) -> usize {
        (CELL_SIZE as usize) * PLAYFIELD_WIDTH
    }

    fn height(&self) -> usize {
        (CELL_SIZE as usize) * PLAYFIELD_HEIGHT
    }

    fn render<R: RenderTarget>(&self, canvas: &mut Canvas<R>) -> Result<(), String> {
        for y in 0..PLAYFIELD_HEIGHT {
            for x in 0..PLAYFIELD_WIDTH {
                canvas.set_draw_color(Color::RGB(0, 0, 0));
                canvas.fill_rect(Rect::new(
                    CELL_SIZE as i32 * x as i32,
                    CELL_SIZE as i32 * y as i32,
                    CELL_SIZE,
                    CELL_SIZE,
                ))?;

                match self.field()[y + PLAYFIELD_HEIGHT][x] {
                    Some(mino) => canvas.set_draw_color(mino),
                    None => canvas.set_draw_color(COLOR_BACKGROUND),
                }
                canvas.fill_rect(Rect::new(
                    CELL_SIZE as i32 * x as i32 + BORDER_RADIUS as i32,
                    CELL_SIZE as i32 * y as i32 + BORDER_RADIUS as i32,
                    CELL_SIZE - 2 * BORDER_RADIUS,
                    CELL_SIZE - 2 * BORDER_RADIUS,
                ))?;
            }
        }

        match self.hold_piece() {
            Some(p) => {
                canvas.set_draw_color(p);
                canvas.fill_rect(Rect::new(-32 - UI_PADDING as i32, 0, CELL_SIZE, CELL_SIZE))?;
            }
            None => (),
        }

        match self.active_piece {
            Some(piece) => piece.render(canvas)?,
            None => (),
        }

        Ok(())
    }
}

impl Renderable for Tetromino {
    fn width(&self) -> usize {
        CELL_SIZE as usize
            * match self.rotation_state {
                RotationState::O | RotationState::U => match self.piece_type {
                    TetrominoType::I => 4,
                    TetrominoType::O => 2,
                    _ => 3,
                },
                RotationState::L | RotationState::R => match self.piece_type {
                    TetrominoType::I => 1,
                    _ => 2,
                },
            }
    }

    fn height(&self) -> usize {
        CELL_SIZE as usize
            * match self.rotation_state {
                RotationState::O | RotationState::U => match self.piece_type {
                    TetrominoType::I => 1,
                    _ => 2,
                },
                RotationState::L | RotationState::R => match self.piece_type {
                    TetrominoType::I => 4,
                    TetrominoType::O => 2,
                    _ => 3,
                },
            }
    }

    fn render<R: RenderTarget>(&self, canvas: &mut Canvas<R>) -> Result<(), String> {
        for Position { x, y } in self.get_cur_occupied_spaces() {
            canvas.set_draw_color::<MinoColor>(self.piece_type.into());
            let height = y as isize - PLAYFIELD_HEIGHT as isize;
            canvas.fill_rect(Rect::new(
                CELL_SIZE as i32 * x as i32 + BORDER_RADIUS as i32,
                CELL_SIZE as i32 * height as i32 + BORDER_RADIUS as i32,
                CELL_SIZE - 2 * BORDER_RADIUS,
                CELL_SIZE - 2 * BORDER_RADIUS,
            ))?;
        }
        Ok(())
    }
}
