#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum GameState {
    MainMenu,
    Serving,
    Playing,
    GameOver,
    Quiting,
}

pub struct State {
    pub ball: Ball,
    pub player1: Player,
    pub player2: Player,
    pub title_text: Text,
    pub play_button: Text,
    pub quit_button: Text,
    pub player1_score: Text,
    pub player2_score: Text,
    pub win_text: Text,
    pub game_state: GameState,
}

pub struct Ball {
    pub position: cgmath::Vector2<f32>,
    pub velocity: cgmath::Vector2<f32>,
    pub radius: f32,
    pub visible: bool,
}

#[derive(Debug)]
pub struct Player {
    pub position: cgmath::Vector2<f32>,
    pub size: cgmath::Vector2<f32>,
    pub score: u32,
    pub visible: bool,
}

// I don't like giving state methods, but whatever
impl Player {
    pub fn contains(&self, ball: &Ball) -> bool {
        let radii = self.size * 0.5;
        let min = self.position - radii;
        let max = self.position + radii;

        let b_radii = cgmath::Vector2 {
            x: ball.radius,
            y: ball.radius,
        };
        let b_min = ball.position - b_radii;
        let b_max = ball.position + b_radii;

        min.x < b_max.x && max.x > b_min.x && min.y < b_max.y && max.y > b_min.y
    }
}

pub const UNBOUNDED_F32: f32 = std::f32::INFINITY;

#[derive(Debug)]
pub struct Text {
    pub position: cgmath::Vector2<f32>,
    pub bounds: cgmath::Vector2<f32>,
    pub color: cgmath::Vector4<f32>,
    pub text: String,
    pub size: f32,
    pub visible: bool,
    pub focused: bool,
    pub centered: bool,
}

impl Default for Text {
    fn default() -> Self {
        Self {
            position: (0.0, 0.0).into(),
            bounds: (UNBOUNDED_F32, UNBOUNDED_F32).into(),
            color: (1.0, 1.0, 1.0, 1.0).into(),
            text: String::new(),
            size: 16.0,
            visible: false,
            focused: false,
            centered: false,
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub enum Event {
    ButtonPressed,
    FocusChanged,
    BallBounce(cgmath::Vector2<f32>),
    Score(u32),
    Resize(f32, f32),
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn player_contains_ball() {
        let ball = Ball {
            position: (0.0, 0.0).into(),
            velocity: (0.0, 0.0).into(),
            radius: 1.0,
            visible: true,
        };

        let p1 = Player {
            position: (0.0, -1.5).into(),
            size: (2.0, 2.0).into(),
            score: 0,
            visible: true,
        };

        assert!(p1.contains(&ball));
    }
}
