use indexmap::IndexSet;
use nightshade::prelude::*;

#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub enum CoordinateSpace {
    #[default]
    World,
    Local,
}

#[derive(Clone, Default)]
pub struct Selection {
    pub entities: IndexSet<Entity>,
    pub primary: Option<Entity>,
}

pub fn selection_is_empty(selection: &Selection) -> bool {
    selection.entities.is_empty()
}

pub fn selection_contains(selection: &Selection, entity: Entity) -> bool {
    selection.entities.contains(&entity)
}

pub fn selection_primary(selection: &Selection) -> Option<Entity> {
    selection.primary
}

pub fn selection_entities_vec(selection: &Selection) -> Vec<Entity> {
    selection.entities.iter().copied().collect()
}

pub fn selection_set_single(selection: &mut Selection, entity: Entity) {
    selection.entities.clear();
    selection.entities.insert(entity);
    selection.primary = Some(entity);
}

pub fn selection_add(selection: &mut Selection, entity: Entity) {
    if selection.entities.insert(entity) && selection.primary.is_none() {
        selection.primary = Some(entity);
    }
}

pub fn selection_remove(selection: &mut Selection, entity: Entity) {
    selection.entities.swap_remove(&entity);
    if selection.primary == Some(entity) {
        selection.primary = selection.entities.first().copied();
    }
}

pub fn selection_toggle(selection: &mut Selection, entity: Entity) {
    if selection.entities.contains(&entity) {
        selection_remove(selection, entity);
    } else {
        selection_add(selection, entity);
    }
}

pub fn selection_clear(selection: &mut Selection) {
    selection.entities.clear();
    selection.primary = None;
}

#[derive(Clone, Default)]
pub struct MarqueeState {
    pub active: bool,
    pub start_pos: Vec2,
    pub current_pos: Vec2,
}

pub fn marquee_start(state: &mut MarqueeState, pos: Vec2) {
    state.active = true;
    state.start_pos = pos;
    state.current_pos = pos;
}

pub fn marquee_update(state: &mut MarqueeState, pos: Vec2) {
    state.current_pos = pos;
}

pub fn marquee_finish(state: &mut MarqueeState) -> Option<(Vec2, Vec2)> {
    if state.active {
        state.active = false;
        let min_x = state.start_pos.x.min(state.current_pos.x);
        let min_y = state.start_pos.y.min(state.current_pos.y);
        let max_x = state.start_pos.x.max(state.current_pos.x);
        let max_y = state.start_pos.y.max(state.current_pos.y);
        Some((Vec2::new(min_x, min_y), Vec2::new(max_x, max_y)))
    } else {
        None
    }
}

pub fn marquee_cancel(state: &mut MarqueeState) {
    state.active = false;
}

pub fn marquee_rect(state: &MarqueeState) -> Option<(Vec2, Vec2)> {
    if state.active {
        let min_x = state.start_pos.x.min(state.current_pos.x);
        let min_y = state.start_pos.y.min(state.current_pos.y);
        let max_x = state.start_pos.x.max(state.current_pos.x);
        let max_y = state.start_pos.y.max(state.current_pos.y);
        Some((Vec2::new(min_x, min_y), Vec2::new(max_x, max_y)))
    } else {
        None
    }
}

#[derive(Clone, Default)]
pub struct SnapSettings {
    pub translation_snap: f32,
    pub rotation_snap_degrees: f32,
    pub scale_snap: f32,
    pub snap_enabled: bool,
}

pub fn snap_settings_new() -> SnapSettings {
    SnapSettings {
        translation_snap: 0.25,
        rotation_snap_degrees: 15.0,
        scale_snap: 0.1,
        snap_enabled: false,
    }
}

pub fn snap_translation(settings: &SnapSettings, value: f32) -> f32 {
    if settings.snap_enabled && settings.translation_snap > 0.0 {
        (value / settings.translation_snap).round() * settings.translation_snap
    } else {
        value
    }
}

pub fn snap_rotation(settings: &SnapSettings, degrees: f32) -> f32 {
    if settings.snap_enabled && settings.rotation_snap_degrees > 0.0 {
        (degrees / settings.rotation_snap_degrees).round() * settings.rotation_snap_degrees
    } else {
        degrees
    }
}

pub fn snap_scale(settings: &SnapSettings, value: f32) -> f32 {
    if settings.snap_enabled && settings.scale_snap > 0.0 {
        (value / settings.scale_snap).round() * settings.scale_snap
    } else {
        value
    }
}
