//! The rows of xfig's Global settings panel that act on the drawing window
//! (`show_global_settings`, `w_cmdpanel.c`), read from the preferences
//! (`prefs.set` changes them): the canvas, the rulers and the tools ask here.

use crate::Engine;
use rustfig_geom::Point;
use rustfig_model::{Object, ObjectKind};

/// xfig's `DEF_FREEHAND_RES`: points of a freehand stroke are this many
/// Fig units (at zoom 1, in pixels) apart.
pub const DEFAULT_FREEHAND_RESOLUTION: i64 = 25;
/// Cap on the vertex numbers drawn at once.
pub const MAX_VERTEX_NUMBERS: usize = 5000;

impl Engine {
    fn pref_bool(&self, name: &str, default: bool) -> bool {
        self.prefs.bool(name).unwrap_or(default)
    }

    /// "Track mouse in rulers": the rulers' markers follow the pointer.
    pub fn track_mouse(&self) -> bool {
        self.pref_bool("trackCursor", true)
    }

    /// "Allow negative coords": points left of or above the origin.
    pub fn allow_negative_coords(&self) -> bool {
        self.pref_bool("allownegcoords", true)
    }

    /// "Draw axis lines": the lines through the origin.
    pub fn show_axis_lines(&self) -> bool {
        self.pref_bool("showaxislines", true)
    }

    /// "Show vertex numbers": the index of each point of lines and splines.
    pub fn show_vertex_numbers(&self) -> bool {
        self.pref_bool("shownums", false)
    }

    /// "Freehand drawing resolution" (xfig's `freehand_resolution`), at least 1.
    pub fn freehand_resolution(&self) -> f64 {
        self.prefs
            .int("freehand_resolution")
            .unwrap_or(DEFAULT_FREEHAND_RESOLUTION)
            .clamp(1, 10_000) as f64
    }

    /// Keep a point out of the negative quadrant unless negative coordinates
    /// are allowed.
    pub fn limit_point(&self, p: Point) -> Point {
        if self.allow_negative_coords() {
            p
        } else {
            Point::new(p.x.max(0.0), p.y.max(0.0))
        }
    }

    /// The numbers xfig draws at the vertices, in Fig units: the point and
    /// its number (counted from 1) for polylines, polygons and splines
    /// (also inside compounds).
    pub fn vertex_numbers(&self) -> Vec<(Point, usize)> {
        let mut out = Vec::new();
        for o in &self.doc().objects {
            collect_numbers(o, &mut out);
            if out.len() >= MAX_VERTEX_NUMBERS {
                break;
            }
        }
        out.truncate(MAX_VERTEX_NUMBERS);
        out
    }
}

fn collect_numbers(o: &Object, out: &mut Vec<(Point, usize)>) {
    match &o.kind {
        ObjectKind::Polyline(l) => out.extend(l.points.iter().enumerate().map(|(i, p)| (*p, i))),
        ObjectKind::Spline(s) => out.extend(s.points.iter().enumerate().map(|(i, p)| (*p, i))),
        ObjectKind::Compound(c) => {
            for m in &c.objects {
                if out.len() >= MAX_VERTEX_NUMBERS {
                    break;
                }
                collect_numbers(m, out);
            }
        }
        _ => {}
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    #[test]
    fn defaults_are_xfigs_and_prefs_change_them() {
        let mut e = Engine::new();
        assert!(e.track_mouse() && e.allow_negative_coords() && e.show_axis_lines());
        assert!(!e.show_vertex_numbers());
        assert_eq!(e.freehand_resolution(), 25.0);
        for (name, v) in [
            ("trackCursor", json!(false)),
            ("allownegcoords", json!(false)),
            ("showaxislines", json!(false)),
            ("shownums", json!(true)),
            ("freehand_resolution", json!(60)),
        ] {
            e.execute("prefs.set", &json!({"resource": name, "value": v})).unwrap();
        }
        assert!(!e.track_mouse() && !e.allow_negative_coords() && !e.show_axis_lines());
        assert!(e.show_vertex_numbers());
        assert_eq!(e.freehand_resolution(), 60.0);
        assert_eq!(e.limit_point(Point::new(-5.0, 7.0)), Point::new(0.0, 7.0));
    }

    #[test]
    fn vertex_numbers_follow_the_points() {
        let mut e = Engine::new();
        e.execute("shape.polyline", &json!({"points": [[0, 0], [100, 0], [100, 100]]}))
            .unwrap();
        let n = e.vertex_numbers();
        assert_eq!(n.len(), 3);
        // xfig numbers from 0 (W22-28).
        assert_eq!(n.first().map(|x| x.1), Some(0));
        assert_eq!(n.last().map(|x| x.1), Some(2));
    }
}
