use ezcad::{array_of::ArrayOf, layer::Layer, pen::Pen};
use serde::{Deserialize, Serialize};

use self::{
    object::{DeleteObjects, ObjectOperation},
    pen::{ClonePen, ImportExportPen, PatchPen, PatternPen, RandomizePen},
};

pub mod hatch;
pub mod object;
pub mod pen;

#[derive(Debug, Serialize, Deserialize)]
pub enum Operation {
    PatchPen(PatchPen),
    ClonePen(ClonePen),
    PatternPen(PatternPen),
    RandomizePen(RandomizePen),
    ExportPen(ImportExportPen),
    ImportPen(ImportExportPen),
    DeleteObjects(DeleteObjects),
    Object(ObjectOperation),
}

pub trait Operations {
    fn apply(&self, pens: &mut Vec<Pen>, layers: &mut ArrayOf<Layer>);
}

impl Operations for Vec<Operation> {
    fn apply(&self, pens: &mut Vec<Pen>, layers: &mut ArrayOf<Layer>) {
        for op in self {
            match op {
                Operation::PatchPen(x) => x.patch(pens),
                Operation::ClonePen(x) => x.clone(pens),
                Operation::PatternPen(x) => x.pattern(pens),
                Operation::RandomizePen(x) => x.random(pens),
                Operation::ImportPen(x) => x.import(pens),
                Operation::ExportPen(x) => x.export(pens),
                Operation::DeleteObjects(x) => x.delete(layers),
                Operation::Object(x) => x.process(pens, layers),
            }
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Config {
    pub ops: Vec<Operation>,
}

/// Helper function that returns a window of two mutable elements
pub fn double_window_mut<T, F>(slice: &mut [T], mut function: F)
where
    F: FnMut(&mut T, &mut T),
{
    for start in 0..(slice.len().saturating_sub(1)) {
        let (a, b) = slice.split_at_mut(start + 1);
        function(&mut a[a.len() - 1], &mut b[0])
    }
}

#[test]
fn test_double_window_mut() {
    let mut data: Vec<u8> = vec![1, 2, 3, 4, 5];
    double_window_mut(&mut data[..], |prev, next| {
        println!("Prev: {}, Next: {}", prev, next);
        *next = *next + *prev;
    });
    assert_eq!(data, vec![1, 3, 6, 10, 15]);
}
