use crate::types::{Color, Size};
use crate::{
    api::{CoreRequest, PlayStoneRequest},
    Coordinate, Goban,
};
use serde::{Deserialize, Serialize};

#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct Jitter {
    pub x: i8,
    pub y: i8,
}

#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct StoneElement {
    pub color: Color,
    pub jitter: Jitter,
    pub liberties: Option<u8>,
}

impl StoneElement {
    pub fn new(color: Color, liberties: Option<u8>) -> Self {
        Self {
            color,
            liberties,
            jitter: Jitter { x: 0, y: 0 },
        }
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum IntersectionElement {
    Unplayable,
    Empty(CoreRequest),
    Filled(StoneElement),
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BoardElement {
    pub size: Size,
    pub spaces: Vec<IntersectionElement>,
}

impl BoardElement {
    pub fn new(size: Size) -> Self {
        let spaces: Vec<IntersectionElement> = (0..size.height)
            .map(|row| {
                (0..size.width)
                    .map(|column| {
                        IntersectionElement::Empty(CoreRequest::PlayStone(PlayStoneRequest {
                            column,
                            row,
                        }))
                    })
                    .collect::<Vec<IntersectionElement>>()
            })
            .collect::<Vec<Vec<IntersectionElement>>>()
            .concat();
        Self { size, spaces }
    }

    pub fn stone(&self, column: u8, row: u8) -> IntersectionElement {
        let addr = self.addr(column, row);
        self.spaces[addr].clone()
    }

    pub fn stone_mut(&mut self, column: u8, row: u8) -> &mut IntersectionElement {
        let addr = self.addr(column, row);
        &mut self.spaces[addr]
    }

    fn addr(&self, column: u8, row: u8) -> usize {
        (row as usize) * (self.size.width as usize) + (column as usize)
    }
}

impl From<&Goban> for BoardElement {
    fn from(board: &Goban) -> Self {
        let spaces: Vec<IntersectionElement> =
            (0..board.size.height)
                .map(|row| {
                    (0..board.size.width)
                        .map(|column| match board.stone(&Coordinate { column, row }) {
                            Some(color) => IntersectionElement::Filled(StoneElement {
                                jitter: Jitter { x: 0, y: 0 },
                                liberties: None,
                                color,
                            }),
                            None => IntersectionElement::Empty(CoreRequest::PlayStone(
                                PlayStoneRequest { column, row },
                            )),
                        })
                        .collect::<Vec<IntersectionElement>>()
                })
                .collect::<Vec<Vec<IntersectionElement>>>()
                .concat();
        Self {
            size: board.size,
            spaces,
        }
    }
}

impl Default for BoardElement {
    fn default() -> Self {
        Self::new(Size::default())
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PlayerCardElement {
    pub color: Color,
    pub name: String,
    pub rank: String,
    pub clock: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ChatElement {
    pub messages: Vec<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TextFieldElement {}
