// A path picked point by point, and the way it is drawn while it is being
// picked.
//
// Two features ask the user for the same gesture. TRIM and EXTEND take a fence
// through the drawing and cut whatever it crosses; the Fence / WPolygon /
// CPolygon selection modes take one and select whatever it crosses or encloses.
// Only what happens to the result differs, so the gesture itself — the points,
// the rubber-banded dashed preview, and whether the path closes back on itself
// — lives here and both bind to it.

use crate::scene::model::wire_model::WireModel;

/// Dash rhythm shared by every in-progress selection gesture, so a fence being
/// picked reads the same wherever it is offered.
const DASH_LENGTH: f32 = 0.8;
const DASH_PATTERN: [f32; 8] = [0.5, -0.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];

/// The crossing-box green, matching the selection marquee the option imitates.
const CROSSING_GREEN: [f32; 4] = [0.35, 0.85, 0.35, 0.9];

/// A path being picked one point at a time.
#[derive(Clone, Debug, Default)]
pub struct FencePick {
    /// Points committed so far, in drawing coordinates.
    pub points: Vec<[f64; 2]>,
    /// Whether the path closes back to its first point. A polygon encloses an
    /// area and can be asked what lies inside it; an open fence only ever
    /// answers what it cuts.
    pub closed: bool,
}

impl FencePick {
    /// An open fence: it selects what it crosses, and encloses nothing.
    pub fn fence() -> Self {
        Self {
            points: Vec::new(),
            closed: false,
        }
    }

    /// A closed polygon, which encloses an area as well as cutting through one.
    pub fn polygon() -> Self {
        Self {
            points: Vec::new(),
            closed: true,
        }
    }

    pub fn push(&mut self, point: [f64; 2]) {
        self.points.push(point);
    }

    pub fn len(&self) -> usize {
        self.points.len()
    }

    pub fn is_empty(&self) -> bool {
        self.points.is_empty()
    }

    /// Enough points to mean anything: two for a fence to cut with, three for a
    /// polygon to enclose with.
    pub fn is_usable(&self) -> bool {
        self.points.len() >= if self.closed { 3 } else { 2 }
    }

    /// The picked path, brought back to its first point when it is a polygon.
    pub fn path(&self) -> Vec<[f64; 2]> {
        let mut path = self.points.clone();
        if self.closed {
            if let Some(first) = self.points.first().copied() {
                path.push(first);
            }
        }
        path
    }

    /// The path so far, trailing the cursor, dashed.
    pub fn preview(&self, cursor: [f64; 2], name: &str) -> WireModel {
        let mut path = self.points.clone();
        path.push(cursor);
        if self.closed {
            if let Some(first) = self.points.first().copied() {
                path.push(first);
            }
        }
        dashed(path, name, WireModel::CYAN)
    }
}

/// Rubber-banded crossing rectangle, drawn like the selection marquee it stands
/// in for so the option reads as the selection it performs. (#336)
pub fn crossing_box_preview(corner: [f64; 2], cursor: [f64; 2], name: &str) -> WireModel {
    dashed(
        vec![
            corner,
            [corner[0], cursor[1]],
            cursor,
            [cursor[0], corner[1]],
            corner,
        ],
        name,
        CROSSING_GREEN,
    )
}

fn dashed(path: Vec<[f64; 2]>, name: &str, color: [f32; 4]) -> WireModel {
    let points: Vec<[f32; 3]> = path
        .into_iter()
        .map(|p| [p[0] as f32, p[1] as f32, 0.0])
        .collect();
    let mut wire = WireModel::solid(name.into(), points, color, false);
    wire.pattern_length = DASH_LENGTH;
    wire.pattern = DASH_PATTERN;
    wire
}
