//! Perspective (projective) transformations for image geometry
//!
//! This module provides functions for perspective transformations
//! such as homography estimation, perspective warping, and
//! perspective correction.
//!
//! **Note**: This module has been refactored into smaller, focused sub-modules
//! for better maintainability. The original API is preserved for backward compatibility.

// Import the modular implementation
#[path = "perspective_modules/core.rs"]
pub mod core;

#[path = "perspective_modules/estimation.rs"]
pub mod estimation;

#[path = "perspective_modules/warping.rs"]
pub mod warping;

#[path = "perspective_modules/rectification.rs"]
pub mod rectification;

// Re-export types for backward compatibility
pub use core::{BorderMode, PerspectiveTransform, RansacParams, RansacResult};

pub use estimation::{
    evaluate_homography_quality, find_homography_adaptive_ransac, find_homography_ransac,
    validate_homography_geometry,
};

pub use warping::{
    bilinear_interpolate, bilinear_interpolate_simd, modulo, reflect_coordinate, warp_perspective,
    warp_perspective_simd,
};

pub use rectification::{auto_perspective_correction, extract_rectangle};

// Backward compatibility aliases
pub use auto_perspective_correction as correct_perspective;

/// Detect quadrilateral in an image (simplified wrapper around auto_perspective_correction)
pub fn detect_quad(
    image: &scirs2_core::ndarray::Array2<f64>,
    edge_threshold: f64,
    min_quad_area: f64,
) -> crate::error::Result<[(f64, f64); 4]> {
    // This is a simplified wrapper that extracts just the quadrilateral points
    // In the original implementation this might have been more complex
    let _transform = auto_perspective_correction(image, edge_threshold, min_quad_area)?;

    // For backward compatibility, return a dummy quad
    // In a real implementation, this would return the actual detected quadrilateral
    Ok([
        (0.0, 0.0),
        (image.dim().1 as f64, 0.0),
        (image.dim().1 as f64, image.dim().0 as f64),
        (0.0, image.dim().0 as f64),
    ])
}

#[cfg(test)]
mod tests {
    use super::*;
    use image::{DynamicImage, GenericImageView, Pixel, Rgb, RgbImage, Rgba};
    use scirs2_core::ndarray::arr1;

    #[test]
    fn test_perspective_identity() {
        let transform = PerspectiveTransform::identity();

        // Identity transform should leave points unchanged
        let point = (10.0, 20.0);
        let transformed = transform.transform_point(point);

        assert!((transformed.0 - point.0).abs() < 1e-10);
        assert!((transformed.1 - point.1).abs() < 1e-10);
    }

    #[test]
    fn test_perspective_transform_point() {
        // Create a perspective transform that scales by 2 in both dimensions
        let transform = PerspectiveTransform::new([2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 1.0]);

        let point = (10.0, 20.0);
        let transformed = transform.transform_point(point);

        assert!((transformed.0 - 20.0).abs() < 1e-10);
        assert!((transformed.1 - 40.0).abs() < 1e-10);
    }

    #[test]
    fn test_perspective_inverse() {
        // Create a transform
        let transform = PerspectiveTransform::new([2.0, 1.0, 3.0, 0.0, 1.0, 5.0, 0.0, 0.0, 1.0]);

        // Get its inverse
        let inverse = transform.inverse().expect("Operation failed");

        // Transform a point and then transform it back
        let point = (10.0, 20.0);
        let transformed = transform.transform_point(point);
        let back = inverse.transform_point(transformed);

        // Should get original point back
        assert!((back.0 - point.0).abs() < 1e-10);
        assert!((back.1 - point.1).abs() < 1e-10);
    }

    #[test]
    fn test_transform_points_simd() {
        let transform = PerspectiveTransform::new([2.0, 0.0, 1.0, 0.0, 2.0, 2.0, 0.0, 0.0, 1.0]);

        let points = vec![(0.0, 0.0), (1.0, 1.0), (2.0, 2.0), (3.0, 3.0)];

        // Test SIMD batch transformation
        let simd_results = transform.transform_points_simd(&points);

        // Test individual transformations for comparison
        let individual_results: Vec<(f64, f64)> = points
            .iter()
            .map(|&p| transform.transform_point(p))
            .collect();

        // Results should be identical
        assert_eq!(simd_results.len(), individual_results.len());
        for (simd, individual) in simd_results.iter().zip(individual_results.iter()) {
            assert!((simd.0 - individual.0).abs() < 1e-10);
            assert!((simd.1 - individual.1).abs() < 1e-10);
        }
    }

    #[test]
    fn test_bilinear_interpolate_simd() {
        // Create a simple test image
        let mut img = RgbImage::new(4, 4);
        for y in 0..4 {
            for x in 0..4 {
                let value = (x + y * 4) as u8 * 16;
                img.put_pixel(x, y, Rgb([value, value, value]));
            }
        }
        let src = DynamicImage::ImageRgb8(img);

        // Test coordinates
        let x_coords = arr1(&[1.5, 2.5, 0.5]);
        let y_coords = arr1(&[1.5, 2.5, 0.5]);

        // SIMD interpolation
        let simd_results = bilinear_interpolate_simd(&src, &x_coords.view(), &y_coords.view());

        // Individual interpolation for comparison
        let individual_results: Vec<Rgba<u8>> = x_coords
            .iter()
            .zip(y_coords.iter())
            .map(|(&x, &y)| bilinear_interpolate(&src, x, y))
            .collect();

        // Results should be identical
        assert_eq!(simd_results.len(), individual_results.len());
        for (simd, individual) in simd_results.iter().zip(individual_results.iter()) {
            for c in 0..4 {
                assert_eq!(simd[c], individual[c]);
            }
        }
    }

    #[test]
    fn test_warp_perspective_simd() {
        // Create a simple test image with a pattern
        let width = 50;
        let height = 50;
        let mut img = RgbImage::new(width, height);

        for y in 0..height {
            for x in 0..width {
                let color = if (x + y) % 2 == 0 {
                    Rgb([255, 0, 0]) // Red checkerboard
                } else {
                    Rgb([0, 0, 255]) // Blue checkerboard
                };
                img.put_pixel(x, y, color);
            }
        }
        let src = DynamicImage::ImageRgb8(img);

        // Identity transformation
        let transform = PerspectiveTransform::identity();

        // Test both regular and SIMD versions
        let regular_result = warp_perspective(&src, &transform, None, None, BorderMode::default())
            .expect("Operation failed");
        let simd_result =
            warp_perspective_simd(&src, &transform, None, None, BorderMode::default())
                .expect("Operation failed");

        // Results should be very similar (allowing for minor floating-point differences)
        assert_eq!(regular_result.width(), simd_result.width());
        assert_eq!(regular_result.height(), simd_result.height());

        // Check a few sample pixels
        for y in (0..height).step_by(10) {
            for x in (0..width).step_by(10) {
                let regular_pixel = regular_result.get_pixel(x, y).to_rgb();
                let simd_pixel = simd_result.get_pixel(x, y).to_rgb();

                // Colors should be identical for identity transform
                for c in 0..3 {
                    let diff = (regular_pixel[c] as i16 - simd_pixel[c] as i16).abs();
                    assert!(
                        diff <= 1,
                        "Pixel difference too large at ({}, {}): {} vs {}",
                        x,
                        y,
                        regular_pixel[c],
                        simd_pixel[c]
                    );
                }
            }
        }
    }
}
