use js_sys::JsString;
use serde::Serialize;
use crate::{
    local::{Position, RoomCoordinate, RoomName},
    objects::{CircleStyle, LineStyle, PolyStyle, RectStyle, TextStyle},
};
#[derive(Clone, Serialize)]
pub struct MapCircleData {
    x: RoomCoordinate,
    y: RoomCoordinate,
    n: RoomName,
    #[serde(rename = "s", skip_serializing_if = "Option::is_none")]
    style: Option<CircleStyle>,
}
#[derive(Clone, Serialize)]
pub struct MapLineData {
    x1: RoomCoordinate,
    y1: RoomCoordinate,
    n1: RoomName,
    x2: RoomCoordinate,
    y2: RoomCoordinate,
    n2: RoomName,
    #[serde(rename = "s", skip_serializing_if = "Option::is_none")]
    style: Option<LineStyle>,
}
#[derive(Clone, Serialize)]
pub struct MapRectData {
    x: RoomCoordinate,
    y: RoomCoordinate,
    n: RoomName,
    #[serde(rename = "w")]
    width: u32,
    #[serde(rename = "h")]
    height: u32,
    #[serde(rename = "s", skip_serializing_if = "Option::is_none")]
    style: Option<RectStyle>,
}
#[derive(Clone, Serialize)]
pub struct MapPolyPoint {
    x: RoomCoordinate,
    y: RoomCoordinate,
    n: RoomName,
}
impl From<&Position> for MapPolyPoint {
    fn from(pos: &Position) -> MapPolyPoint {
        MapPolyPoint {
            x: pos.x(),
            y: pos.y(),
            n: pos.room_name(),
        }
    }
}
#[derive(Clone, Serialize)]
pub struct MapPolyData {
    points: Vec<MapPolyPoint>,
    #[serde(rename = "s", skip_serializing_if = "Option::is_none")]
    style: Option<PolyStyle>,
}
#[derive(Clone, Serialize)]
pub struct MapTextData {
    text: String,
    x: RoomCoordinate,
    y: RoomCoordinate,
    n: RoomName,
    #[serde(rename = "s", skip_serializing_if = "Option::is_none")]
    style: Option<TextStyle>,
}
#[derive(Clone, Serialize)]
#[serde(tag = "t")]
pub enum MapVisualShape {
    #[serde(rename = "c")]
    Circle(MapCircleData),
    #[serde(rename = "l")]
    Line(MapLineData),
    #[serde(rename = "r")]
    Rect(MapRectData),
    #[serde(rename = "p")]
    Poly(MapPolyData),
    #[serde(rename = "t")]
    Text(MapTextData),
}
impl MapVisualShape {
    pub fn circle(center: Position, style: Option<CircleStyle>) -> MapVisualShape {
        MapVisualShape::Circle(MapCircleData {
            x: center.x(),
            y: center.y(),
            n: center.room_name(),
            style,
        })
    }
    pub fn line(from: Position, to: Position, style: Option<LineStyle>) -> MapVisualShape {
        MapVisualShape::Line(MapLineData {
            x1: from.x(),
            y1: from.y(),
            n1: from.room_name(),
            x2: to.x(),
            y2: to.y(),
            n2: to.room_name(),
            style,
        })
    }
    pub fn rect(
        top_left: Position,
        width: u32,
        height: u32,
        style: Option<RectStyle>,
    ) -> MapVisualShape {
        MapVisualShape::Rect(MapRectData {
            x: top_left.x(),
            y: top_left.y(),
            n: top_left.room_name(),
            width,
            height,
            style,
        })
    }
    pub fn poly(points: Vec<MapPolyPoint>, style: Option<PolyStyle>) -> MapVisualShape {
        MapVisualShape::Poly(MapPolyData { points, style })
    }
    pub fn text(pos: Position, text: String, style: Option<TextStyle>) -> MapVisualShape {
        MapVisualShape::Text(MapTextData {
            x: pos.x(),
            y: pos.y(),
            n: pos.room_name(),
            text,
            style,
        })
    }
}
pub struct MapVisual {}
impl MapVisual {
    pub fn draw(visual: &MapVisualShape) {
        let val = serde_wasm_bindgen::to_value(visual).expect("expect convert visual to value");
        crate::console::add_visual(Some(&JsString::from("map")), &val);
    }
    pub fn draw_multi(visuals: &[MapVisualShape]) {
        for visual in visuals {
            let val = serde_wasm_bindgen::to_value(visual).expect("expect convert visual to value");
            crate::console::add_visual(Some(&JsString::from("map")), &val);
        }
    }
    pub fn circle(pos: Position, style: Option<CircleStyle>) {
        Self::draw(&MapVisualShape::circle(pos, style));
    }
    pub fn line(from: Position, to: Position, style: Option<LineStyle>) {
        Self::draw(&MapVisualShape::line(from, to, style));
    }
    pub fn rect(top_left: Position, width: u32, height: u32, style: Option<RectStyle>) {
        Self::draw(&MapVisualShape::rect(top_left, width, height, style));
    }
    pub fn poly(points: Vec<Position>, style: Option<PolyStyle>) {
        let points = points.iter().map(Into::into).collect();
        Self::draw(&MapVisualShape::poly(points, style));
    }
    pub fn text(pos: Position, text: String, style: Option<TextStyle>) {
        Self::draw(&MapVisualShape::text(pos, text, style));
    }
}