use log::warn;
use serde::{Deserialize, Serialize};
use serde_json::from_reader;
use smallvec::{smallvec, SmallVec};
use space_crush_common::misc::{Vfs, WorldHelper as _};
use specs::World;

use crate::{
    misc::{MouseButton, VirtualKeyCode},
    Error,
};

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct Config {
    #[serde(default)]
    pub video: Video,

    #[serde(default)]
    pub input: Input,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Video {
    #[serde(default = "defaults::resolution")]
    pub resolution: (u32, u32),
    #[serde(default = "defaults::multisampling")]
    pub multisampling: u16,
    #[serde(default = "defaults::fullscreen")]
    pub fullscreen: bool,
    #[serde(default = "defaults::windowed")]
    pub windowed: bool,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Input {
    #[serde(default = "defaults::camera_move_key_up")]
    pub camera_move_key_up: VirtualKeyCodes,
    #[serde(default = "defaults::camera_move_key_down")]
    pub camera_move_key_down: VirtualKeyCodes,
    #[serde(default = "defaults::camera_move_key_left")]
    pub camera_move_key_left: VirtualKeyCodes,
    #[serde(default = "defaults::camera_move_key_right")]
    pub camera_move_key_right: VirtualKeyCodes,

    #[serde(default = "defaults::camera_move_button")]
    pub camera_move_button: MouseButton,

    #[serde(default = "defaults::camera_move_speed")]
    pub camera_move_speed: f32,
    #[serde(default = "defaults::camera_zoom_speed")]
    pub camera_zoom_speed: f32,
}

type VirtualKeyCodes = SmallVec<[VirtualKeyCode; 2]>;

impl Config {
    pub fn new(world: &World) -> Result<Self, Error> {
        let vfs = world.resource::<Vfs>()?;
        let config = match vfs.join("config.json")?.open_file() {
            Ok(mut file) => from_reader(&mut file)?,
            Err(err) => {
                warn!(
                    "Unable to load config file, falling back to default config: {}",
                    err
                );

                Config::default()
            }
        };

        Ok(config)
    }
}

impl Default for Video {
    fn default() -> Self {
        Self {
            resolution: defaults::resolution(),
            multisampling: defaults::multisampling(),
            fullscreen: defaults::fullscreen(),
            windowed: defaults::windowed(),
        }
    }
}

impl Default for Input {
    fn default() -> Self {
        Self {
            camera_move_key_up: defaults::camera_move_key_up(),
            camera_move_key_down: defaults::camera_move_key_down(),
            camera_move_key_left: defaults::camera_move_key_left(),
            camera_move_key_right: defaults::camera_move_key_right(),

            camera_move_button: defaults::camera_move_button(),

            camera_move_speed: defaults::camera_move_speed(),
            camera_zoom_speed: defaults::camera_zoom_speed(),
        }
    }
}

mod defaults {
    use super::*;

    pub fn resolution() -> (u32, u32) {
        (1280, 720)
    }

    pub fn multisampling() -> u16 {
        0
    }

    pub fn fullscreen() -> bool {
        true
    }

    pub fn windowed() -> bool {
        true
    }

    pub fn camera_move_key_up() -> VirtualKeyCodes {
        smallvec![VirtualKeyCode::W, VirtualKeyCode::Up]
    }

    pub fn camera_move_key_down() -> VirtualKeyCodes {
        smallvec![VirtualKeyCode::S, VirtualKeyCode::Down]
    }

    pub fn camera_move_key_left() -> VirtualKeyCodes {
        smallvec![VirtualKeyCode::A, VirtualKeyCode::Left]
    }

    pub fn camera_move_key_right() -> VirtualKeyCodes {
        smallvec![VirtualKeyCode::D, VirtualKeyCode::Right]
    }

    pub fn camera_move_button() -> MouseButton {
        MouseButton::Right
    }

    pub fn camera_move_speed() -> f32 {
        500.0
    }

    pub fn camera_zoom_speed() -> f32 {
        100.0
    }
}
