use crate::segm::segment::Segment;
use crate::segm::winding_count::WindingCount;

pub(crate) trait ShapeSegmentsMerge {
    fn merge_if_needed(&mut self);
}

impl<C: WindingCount> ShapeSegmentsMerge for Vec<Segment<C>> {
    fn merge_if_needed(&mut self) {
        if self.len() < 2 { return; }

        let mut prev = &self[0].x_segment;
        for i in 1..self.len() {
            let this = &self[i].x_segment;
            if prev.eq(this) {
                let new_len = merge(self, i);
                self.truncate(new_len);
                return;
            }
            prev = this;
        }
    }
}

fn merge<C: WindingCount>(segments: &mut [Segment<C>], after: usize) -> usize {
    let mut i = after;
    let mut j = i - 1;
    let mut prev = segments[j];

    while i < segments.len() {
        if prev.x_segment.eq(&segments[i].x_segment) {
            prev.count.apply(segments[i].count);
        } else {
            if prev.count.is_not_empty() {
                segments[j] = prev;
                j += 1;
            }
            prev = segments[i];
        }
        i += 1;
    }

    if prev.count.is_not_empty() {
        segments[j] = prev;
        j += 1;
    }

    j
}

#[cfg(test)]
mod tests {
    use i_float::int::point::IntPoint;
    use super::*;
    use crate::segm::winding_count::ShapeCountBoolean;

    #[test]
    fn test_merge_if_needed_empty() {
        let mut segments: Vec<Segment<ShapeCountBoolean>> = Vec::new();
        segments.merge_if_needed();
        assert!(segments.is_empty(), "Empty vector should remain empty after merge");
    }

    #[test]
    fn test_merge_if_needed_single_element() {
        let a = IntPoint::new(1, 2);
        let b = IntPoint::new(3, 4);
        let count = ShapeCountBoolean::new(1, 1);
        let segment = Segment::create_and_validate(a, b, count);
        let mut segments = vec![segment];
        segments.merge_if_needed();
        assert_eq!(segments.len(), 1, "Single segment should remain unchanged");
        assert_eq!(segments[0], segment, "Segment should be unchanged after merge");
    }

    #[test]
    fn test_merge_if_needed_no_merge() {
        let a1 = IntPoint::new(1, 2);
        let b1 = IntPoint::new(3, 4);
        let count1 = ShapeCountBoolean::new(1, 0);
        let segment1 = Segment::create_and_validate(a1, b1, count1);

        let a2 = IntPoint::new(5, 6);
        let b2 = IntPoint::new(7, 8);
        let count2 = ShapeCountBoolean::new(0, 1);
        let segment2 = Segment::create_and_validate(a2, b2, count2);

        let mut segments = vec![segment1, segment2];
        segments.merge_if_needed();

        assert_eq!(segments.len(), 2, "Segments with different x_segments should not be merged");
        assert_eq!(segments[0], segment1, "First segment should remain unchanged");
        assert_eq!(segments[1], segment2, "Second segment should remain unchanged");
    }

    #[test]
    fn test_merge_if_needed_single_merge() {
        let a1 = IntPoint::new(1, 2);
        let b1 = IntPoint::new(3, 4);
        let count1 = ShapeCountBoolean::new(1, 0);
        let segment1 = Segment::create_and_validate(a1, b1, count1);

        let a2 = IntPoint::new(1, 2);
        let b2 = IntPoint::new(3, 4);
        let count2 = ShapeCountBoolean::new(0, 1);
        let segment2 = Segment::create_and_validate(a2, b2, count2);

        let mut segments = vec![segment1, segment2];
        segments.merge_if_needed();

        assert_eq!(segments.len(), 1, "Segments should be merged into one");
        let merged_count = ShapeCountBoolean::new(1, 1);
        let expected_segment = Segment::create_and_validate(a1, b1, merged_count);
        assert_eq!(segments[0], expected_segment, "Merged segment should have combined counts");
    }

    #[test]
    fn test_merge_if_needed_multiple_merges() {
        let a = IntPoint::new(1, 2);
        let b = IntPoint::new(3, 4);

        let count1 = ShapeCountBoolean::new(1, 0);
        let count2 = ShapeCountBoolean::new(0, 1);
        let count3 = ShapeCountBoolean::new(2, 2);

        let segment1 = Segment::create_and_validate(a, b, count1);
        let segment2 = Segment::create_and_validate(a, b, count2);
        let segment3 = Segment::create_and_validate(a, b, count3);

        let mut segments = vec![segment1, segment2, segment3];
        segments.merge_if_needed();

        assert_eq!(segments.len(), 1, "All segments should be merged into one");
        let merged_count = ShapeCountBoolean::new(3, 3);
        let expected_segment = Segment::create_and_validate(a, b, merged_count);
        assert_eq!(segments[0], expected_segment, "Merged segment should have combined counts");
    }

    #[test]
    fn test_merge_if_needed_segments_with_inverted_order() {
        let a1 = IntPoint::new(3, 4);
        let b1 = IntPoint::new(1, 2);
        let count1 = ShapeCountBoolean::new(1, 0);
        // create_and_validate should order the points
        let segment1 = Segment::create_and_validate(a1, b1, count1);

        let a2 = IntPoint::new(1, 2);
        let b2 = IntPoint::new(3, 4);
        let count2 = ShapeCountBoolean::new(0, 1);
        let segment2 = Segment::create_and_validate(a2, b2, count2);

        let mut segments = vec![segment1, segment2];
        segments.merge_if_needed();

        // Both segments should have the same ordered x_segment
        assert_eq!(segments.len(), 1, "Segments with inverted points should be merged");

        let merged_count = ShapeCountBoolean::new(1, 1);
        let expected_segment = Segment::create_and_validate(IntPoint::new(1, 2), IntPoint::new(3, 4), merged_count);
        assert_eq!(segments[0], expected_segment, "Merged segment should have combined counts and ordered points");
    }

    #[test]
    fn test_merge_if_needed_no_merge_different_x_segments() {
        let a1 = IntPoint::new(1, 1);
        let b1 = IntPoint::new(2, 2);
        let count1 = ShapeCountBoolean::new(1, 1);
        let segment1 = Segment::create_and_validate(a1, b1, count1);

        let a2 = IntPoint::new(3, 3);
        let b2 = IntPoint::new(4, 4);
        let count2 = ShapeCountBoolean::new(2, 2);
        let segment2 = Segment::create_and_validate(a2, b2, count2);

        let a3 = IntPoint::new(5, 5);
        let b3 = IntPoint::new(6, 6);
        let count3 = ShapeCountBoolean::new(3, 3);
        let segment3 = Segment::create_and_validate(a3, b3, count3);

        let mut segments = vec![segment1, segment2, segment3];
        segments.merge_if_needed();

        assert_eq!(segments.len(), 3, "Segments with different x_segments should not be merged");
        assert_eq!(segments[0], segment1, "First segment should remain unchanged");
        assert_eq!(segments[1], segment2, "Second segment should remain unchanged");
        assert_eq!(segments[2], segment3, "Third segment should remain unchanged");
    }
}
