//! Module implementing screentone and rotated screentone effects on single-channel `SVec` images.
//!
//! Screentone mimics comic-style dot screening by thresholding pixels against a dot matrix.
//!
//! # Examples
//!
//! ```rust
//! use pepecore::{screentone, rotate_screentone};
//! use pepecore_array::{SVec,Shape,PixelType,ImgData};
//! use pepecore::enums::{DotType};
//!
//! // Create a grayscale SVec (2x2, u8)
//! let mut img = SVec::new(Shape::new(2, 2, None), ImgData::U8(vec![50, 200, 150, 100]));
//! // Apply screentone with dot size 4 and circular dots
//! screentone(&mut img, 4, &DotType::CIRCLE);
//!
//! // Apply rotated screentone at 45 degrees on f32 image
//! let mut img_f32 = SVec::new(Shape::new(3, 3, None), ImgData::F32(vec![0.2; 9]));
//! rotate_screentone(&mut img_f32, 5, 45.0, &DotType::ELLIPSE);
//! ```

use fast_image_resize::ResizeAlg;
use crate::array::Shape;
use crate::enums::DotType;
use crate::ops::svec_ops::halftone::dot::dot_create;
use crate::ops::svec_ops::halftone::utils::{HalftonePixel, compute_cos_sin, rotate_pixel_coordinates, wrap_index};
use pepecore_array::{PixelType, SVec};
use crate::ops::svec_ops::resize::fir::ResizeSVec;

/// Apply screentone (non-rotated) to a single-channel image in-place.
///
/// - Uses a symmetric dot matrix of total size `dot_size*2` generated by `dot_type`.
/// - For each pixel, compares its value to the corresponding dot matrix threshold:
///   - If pixel < threshold, sets to `MIN_VALUE`.
///   - Otherwise, sets to `MAX_VALUE`.
///
/// # Parameters
///
/// - `img`: Mutable reference to a single-channel `SVec`.
/// - `dot_size`: Radius of dot pattern (matrix will be `2*dot_size`).
/// - `dot_type`: `DotType` enum specifying shape of dots.
fn apply_screentone<T: HalftonePixel>(img: &mut SVec, dot_size: usize, dot_type: &DotType) {
    let (h, w, _) = img.shape();
    let mut_img = img.get_data_mut::<T>().unwrap();

    let dot_matrix = dot_create(dot_size, dot_type);
    let dot_matrix_data = dot_matrix.get_data::<f32>().unwrap();
    let lx_bias = dot_size / 2;
    let ly_bias = dot_size / 2;
    let dot_size = dot_size * 2;

    let dot_matrix_converted = T::prepare_dot_matrix(dot_matrix_data);

    for ly in 0..h {
        let ly2 = (ly + ly_bias) % dot_size;

        for lx in 0..w {
            let idx = ly * w + lx;
            let dot_idx = (lx + lx_bias) % dot_size + ly2 * dot_size;

            mut_img[idx] = if mut_img[idx] < dot_matrix_converted[dot_idx] {
                T::MIN_VALUE
            } else {
                T::MAX_VALUE
            };
        }
    }
}

/// Apply rotated screentone effect to a single-channel image in-place.
///
/// - Rotates sampling coordinates by `angle` around image center before thresholding.
///
/// # Parameters
///
/// - `img`: Mutable reference to a single-channel `SVec`.
/// - `dot_size`: Radius of dot pattern.
/// - `angle`: Rotation angle in degrees.
/// - `dot_type`: `DotType` specifying shape.
fn apply_rotate_screentone<T: HalftonePixel>(img: &mut SVec, dot_size: usize, angle: f32, dot_type: &DotType) {
    let (h, w, _) = img.shape();
    let mut_img = img.get_data_mut::<T>().unwrap();
    let cos_sin = compute_cos_sin(angle.to_radians());
    let dot_matrix = dot_create(dot_size, dot_type);
    let dot_matrix_data = dot_matrix.get_data::<f32>().unwrap();
    let new_dot_matrix_data = T::prepare_dot_matrix(dot_matrix_data);
    let lx_bias = dot_size / 2;
    let ly_bias = dot_size / 2 ;
    let dot_size = dot_size * 2;

    for ly in 0..h {
        let ly2 = ly + ly_bias;

        for lx in 0..w {
            let lx2 = lx + lx_bias;
            let value = &mut mut_img[ly * w + lx];
            let rot = rotate_pixel_coordinates(lx2 as f32, ly2 as f32, w as f32, h as f32, cos_sin[0], cos_sin[1]);
            let dx = wrap_index(rot.0.round() as i32, dot_size);
            let dy = wrap_index(rot.1.round() as i32, dot_size);
            *value = if *value < new_dot_matrix_data[dx + dy * dot_size] {
                T::MIN_VALUE
            } else {
                T::MAX_VALUE
            };
        }
    }
}
fn apply_ssaa_screentone<T: HalftonePixel>(img: &mut SVec, dot_size: usize, dot_type: &DotType,scale:f32,resize_alg: ResizeAlg) {
    let (h, w, _) = img.shape();
    let mut_img = img.get_mut_vec::<T>().unwrap();
    let dot_size = (dot_size as f32 *scale).round() as usize;
    let dot_matrix = dot_create(dot_size, dot_type);
    let dot_matrix_data = dot_matrix.get_data::<f32>().unwrap();
    let lx_bias = dot_size / 2;
    let ly_bias = dot_size / 2;
    let dot_size = dot_size * 2;
    let dot_matrix_converted = T::prepare_dot_matrix(dot_matrix_data);

    let x_in_tab: Vec<usize> = (0..(w as f32 * scale) as usize)
        .map(|x| ((x as f32 / scale).round() as usize).min(w - 1))
        .collect();
    let y_in_tab: Vec<usize> = (0..(h as f32 * scale) as usize)
        .map(|y| ((y as f32 / scale).round() as usize).min(h - 1))
        .collect();

    let mut new_vec:Vec<T> = Vec::with_capacity((h as f32 *scale) as usize*(w as f32 *scale) as usize);
    for (ly, y) in y_in_tab.iter().enumerate(){
        let ly2 = (ly + ly_bias) % dot_size;

        for (lx,x) in x_in_tab.iter().enumerate(){
            let idx = y * w + x;
            let dot_idx = (lx + lx_bias) % dot_size + ly2 * dot_size;

            new_vec.push( if mut_img[idx] < dot_matrix_converted[dot_idx] {
                T::MIN_VALUE
            } else {
                T::MAX_VALUE
            });
        }
    }
    std::mem::swap(mut_img, &mut new_vec);
    img.shape = Shape::new((h as f32*scale) as usize,(w as f32*scale) as usize,None);
    img.resize(h,w,resize_alg,false);
    
}
fn apply_rotate_ssaa_screentone<T: HalftonePixel>(img: &mut SVec, dot_size: usize,angle:f32, dot_type: &DotType,scale:f32,resize_alg: ResizeAlg) {
    let (h, w, _) = img.shape();
    let mut_img = img.get_mut_vec::<T>().unwrap();
    let dot_size = (dot_size as f32 *scale).round() as usize;
    let dot_matrix = dot_create(dot_size, dot_type);
    let dot_matrix_data = dot_matrix.get_data::<f32>().unwrap();
    let (s_h,s_w)=((h as f32*scale) as usize,(w as f32* scale) as usize);
    let lx_bias = dot_size / 2;
    let ly_bias = dot_size / 2;
    let dot_size = dot_size * 2;
    let dot_matrix_converted = T::prepare_dot_matrix(dot_matrix_data);
    let cos_sin = compute_cos_sin(angle.to_radians());

    let x_in_tab: Vec<usize> = (0..(w as f32 * scale) as usize)
        .map(|x| ((x as f32 / scale).round() as usize).min(w - 1))
        .collect();
    let y_in_tab: Vec<usize> = (0..(h as f32 * scale) as usize)
        .map(|y| ((y as f32 / scale).round() as usize).min(h - 1))
        .collect();

    let mut new_vec:Vec<T> = Vec::with_capacity((h as f32 *scale) as usize*(w as f32 *scale) as usize);
    for (ly, y) in y_in_tab.iter().enumerate(){
        let ly2 = ly + ly_bias;

        for (lx,x) in x_in_tab.iter().enumerate(){
            let lx2 = lx + lx_bias;
            let idx = y * w + x;
            let rot = rotate_pixel_coordinates(lx2 as f32, ly2 as f32, s_w as f32, s_h as f32, cos_sin[0], cos_sin[1]);
            let dx = wrap_index(rot.0.round() as i32, dot_size);
            let dy = wrap_index(rot.1.round() as i32, dot_size);
            new_vec.push(
                          if mut_img[idx] < dot_matrix_converted[dx + dy * dot_size] {
                T::MIN_VALUE
            } else {
                T::MAX_VALUE
            }
        );
        }
    }
    std::mem::swap(mut_img, &mut new_vec);
    img.shape = Shape::new(s_h,s_w, None);
    img.resize(h,w,resize_alg,false);

}
/// Public API: apply non-rotated screentone, dispatching by pixel type.
pub fn screentone(img: &mut SVec, dot_size: usize, dot_type: &DotType) {
    match img.pixel_type() {
        PixelType::F32 => apply_screentone::<f32>(img, dot_size, dot_type),
        PixelType::U8 => apply_screentone::<u8>(img, dot_size, dot_type),
        PixelType::U16 => apply_screentone::<u16>(img, dot_size, dot_type),
    }
}
/// Public API: apply SSAA non-rotated screentone, dispatching by pixel type.
pub fn ssaa_screentone(img: &mut SVec, dot_size: usize, dot_type: &DotType, scale: f32, resize_alg: ResizeAlg) {
    match img.pixel_type() {
        PixelType::F32 => apply_ssaa_screentone::<f32>(img, dot_size, dot_type, scale, resize_alg),
        PixelType::U8 => apply_ssaa_screentone::<u8>(img, dot_size, dot_type, scale, resize_alg),
        PixelType::U16 => apply_ssaa_screentone::<u16>(img, dot_size, dot_type, scale, resize_alg),
    }
}
/// Public API: apply rotated screentone, dispatching by pixel type.
pub fn rotate_screentone(img: &mut SVec, dot_size: usize, angle: f32, dot_type: &DotType) {
    match img.pixel_type() {
        PixelType::F32 => apply_rotate_screentone::<f32>(img, dot_size, angle, dot_type),
        PixelType::U8 => apply_rotate_screentone::<u8>(img, dot_size, angle, dot_type),
        PixelType::U16 => apply_rotate_screentone::<u16>(img, dot_size, angle, dot_type),
    }
}
/// Public API: apply SSAA non-rotated screentone, dispatching by pixel type.
pub fn ssaa_rotate_screentone(img: &mut SVec, dot_size: usize, angle: f32, dot_type: &DotType, scale: f32, resize_alg: ResizeAlg) {
    match img.pixel_type() {
        PixelType::F32 => apply_rotate_ssaa_screentone::<f32>(img, dot_size, angle, dot_type, scale,resize_alg),
        PixelType::U8 => apply_rotate_ssaa_screentone::<u8>(img, dot_size, angle, dot_type, scale,resize_alg),
        PixelType::U16 => apply_rotate_ssaa_screentone::<u16>(img, dot_size, angle, dot_type, scale,resize_alg),
    }
}
