#![doc = r#"
Implementations of visualization functions for the [`Intersection`] type.

All specific instantiations / monomorphizations of [`Intersection`] which only
use the types `()`, [`SegmentIdx`] or [`ShapeIdx`] implement a method `draw`,
which can be used to visualize the intersection as a cross. The properties of
the cross can be adjusted via [`IntersectionStyle`], which is always the
second argument to `draw`.

If an [`Intersection`] uses the types [`SegmentIdx`] or [`ShapeIdx`], it was
usually generated by one of the intersection functions provided by the
[`Composite`] trait. The `draw` functions for those specific instantiations of
[`Intersection`] can also take references to the involved implementors of
[`Composite`] ([`SegmentChain`], [`Contour`](crate::contour::Contour) or
[`Shape`]) as optional arguments so the intersected segments can be highlighted.
The highlighting style is defined by a reference to a [`Style`] struct, which
needs to be provided together with the composite.

For example, the signature of the [`Intersection<SegmentIdx, ShapeIdx>::draw`]
function looks like this:

```ignore
impl Intersection<SegmentIdx, ShapeIdx> {
fn draw(&self, style: &IntersectionStyle,
        left: Option<(&SegmentChain, &Style)>,
        right: Option<(&Shape, &Style)>,
        context: &cairo::Context,
    ) -> Result<(), cairo::Error> {
        ...
    }
}
```

If an intersection between a composite type and a primitive type (which
implements the [`Primitive`](crate::primitive::Primitive) trait) is calculated,
the corresponding left or right intersection side type is `()` and the argument
`left` or `right` is omitted:

```ignore
impl Intersection<(), ShapeIdx> {
fn draw(&self, style: &IntersectionStyle,
        right: Option<(&Shape, &Style)>,
        context: &cairo::Context,
    ) -> Result<(), cairo::Error> {
        ...
    }
}
```

# Examples

"#]
#![cfg_attr(
    docsrs,
    doc = "![](https://raw.githubusercontent.com/StefanMathis/planar_geo/refs/heads/main/docs/intersection_example_2.svg \"All geometric types\")"
)]
#![cfg_attr(
    not(docsrs),
    doc = "![>> Example image missing, copy folder docs from crate root to doc root folder (where index.html is) to display the image <<](../../docs/intersection_example_2.svg \"All geometric types\")"
)]
#![doc = r#"

This image from the [lib](crate) module documentation was created using the
following code:
````
use cairo_viewport::{SideLength, Viewport};
use planar_geo::prelude::*;

let vertices = &[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]];
let contour = Contour::new(SegmentChain::from_points(vertices));

let vertices = &[[0.1, 0.1], [0.9, 0.1], [0.9, 0.9], [0.1, 0.9]];
let hole = Contour::new(SegmentChain::from_points(vertices));

let shape = Shape::new(vec![contour.clone(), hole]).expect("valid inputs");

let chain =
    SegmentChain::from_points(&[[-1.0, 1.0], [-0.5, 0.5], [1.5, 0.5], [2.0, 1.0]]);

let view = Viewport::from_bounding_box(
    &BoundingBox::new(-1.2, 2.2, -1.2, 2.2),
    SideLength::Long(500),
);

let mut style = Style::default();
style.line_color = Color::new(0.0, 0.0, 0.0, 1.0);
style.line_width = 2.0;
style.line_style = LineStyle::Solid;
style.background_color = Color::from_rgba8(144, 213, 255, 255);

let mut intersected_segments_style = Style::default();
intersected_segments_style.line_color = Color::new(1.0, 1.0, 0.0, 1.0);
intersected_segments_style.line_width = 3.0;
intersected_segments_style.line_style = LineStyle::Solid;

let intersection_style = IntersectionStyle::default();

let draw_fn = |cr: &cairo::Context| {
    // Set the background to white
    cr.set_source_rgb(1.0, 1.0, 1.0);
    cr.paint()?;

    shape.draw(&style, cr)?;
    chain.draw(&style, cr)?;
    for i in shape.intersections_segment_chain(&chain, DEFAULT_EPSILON, DEFAULT_MAX_ULPS) {
        i.draw(
            &intersection_style,
            Some((&shape, &intersected_segments_style)),
            Some((&chain, &intersected_segments_style)),
            cr,
        )?;
    }

    return Ok(());
};

// Comment this in to actually create the shown image
// view.write_to_file("docs/intersection_example_2.svg", draw_fn).expect("image could not be created");
# assert!(
#     view.compare_or_create(
#         std::path::Path::new("tests/img/intersection_segment_chain_shape.png"),
#         draw_fn, 0.99
#     )
#     .is_ok()
# );
```
"#]

use crate::composite::*;
use crate::prelude::SegmentChain;
use crate::shape::Shape;
use crate::visualize::*;

/**
[`Intersection`]s are visualized as crosses, whose properties are defined by
this struct. See the [module documentation](crate::visualize::intersection) for
an example.
 */
pub struct IntersectionStyle {
    /// Color of all lines of the geometric object. Corresponds to "border-color"
    /// as defined in <https://www.w3.org/TR/css-backgrounds-3/#border-color>.
    pub line_color: Color,
    /// Width of all lines of the geometric object. Corresponds to "border-color"
    /// as defined in <https://www.w3.org/TR/css-backgrounds-3/#the-border-width>.
    pub line_width: f64,
    /// How the end points of lines are rendered. See
    /// <https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-line-cap-t>.
    pub line_cap: cairo::LineCap,
    /// Height / width of the intersection cross in points.
    pub cross_size: f64,
}

impl Default for IntersectionStyle {
    fn default() -> Self {
        Self {
            line_color: Color::new(1.0, 0.0, 0.0, 1.0),
            line_width: 5.0,
            line_cap: cairo::LineCap::Butt,
            cross_size: 10.0,
        }
    }
}

fn draw_segment_of_chain(
    segment_chain: &SegmentChain,
    style: &Style,
    segment_idx: SegmentIdx,
    context: &cairo::Context,
) -> Result<(), cairo::Error> {
    if let Some(segment) = segment_chain.get(segment_idx.0) {
        segment.draw(style, context)?;
    }
    return Ok(());
}

fn draw_segment_of_shape(
    shape: &Shape,
    style: &Style,
    shape_idx: ShapeIdx,
    context: &cairo::Context,
) -> Result<(), cairo::Error> {
    if let Some(contour) = shape.contours().get(shape_idx.contour_idx) {
        draw_segment_of_chain(
            contour.segment_chain(),
            style,
            shape_idx.segment_idx,
            context,
        )?;
    }
    return Ok(());
}

impl Intersection<ShapeIdx, ShapeIdx> {
    /**
    Draws the intersection between two [`Shape`]s onto the given
    [`cairo::Context`] using the given [`IntersectionStyle`].

    Optionally, the intersected segments of the involved composite types
    [`SegmentChain`], [`Contour`](crate::contour::Contour) or [`Shape`] can also
    be drawn onto the canvas if the corresponding `(&Composite, &Style)` tuple
    is provided. See the [module documentation](crate::visualize::intersection)
    for an example.
     */
    pub fn draw(
        &self,
        style: &IntersectionStyle,
        left: Option<(&Shape, &Style)>,
        right: Option<(&Shape, &Style)>,
        context: &cairo::Context,
    ) -> Result<(), cairo::Error> {
        if let Some(s) = left {
            draw_segment_of_shape(s.0, s.1, self.left, context)?;
        }
        if let Some(s) = right {
            draw_segment_of_shape(s.0, s.1, self.right, context)?;
        }
        let intersection: Intersection<(), ()> = self.point.into();
        return intersection.draw(style, context);
    }
}

impl Intersection<ShapeIdx, SegmentIdx> {
    /**
    Draws the intersection between a [`Shape`] and a [`SegmentChain`] onto the
    given [`cairo::Context`] using the given [`IntersectionStyle`].

    Optionally, the intersected segments of the involved composite types
    [`SegmentChain`], [`Contour`](crate::contour::Contour) or [`Shape`] can also
    be drawn onto the canvas if the corresponding `(&Composite, &Style)` tuple
    is provided. See the [module documentation](crate::visualize::intersection)
    for an example.
     */
    pub fn draw(
        &self,
        style: &IntersectionStyle,
        left: Option<(&Shape, &Style)>,
        right: Option<(&SegmentChain, &Style)>,
        context: &cairo::Context,
    ) -> Result<(), cairo::Error> {
        if let Some(s) = left {
            draw_segment_of_shape(s.0, s.1, self.left, context)?;
        }
        if let Some(s) = right {
            draw_segment_of_chain(s.0, s.1, self.right, context)?;
        }
        let intersection: Intersection<(), ()> = self.point.into();
        return intersection.draw(style, context);
    }
}

impl Intersection<SegmentIdx, ShapeIdx> {
    /**
    Draws the intersection between a [`SegmentChain`] and a [`Shape`] onto the
    given [`cairo::Context`] using the given [`IntersectionStyle`].

    Optionally, the intersected segments of the involved composite types
    [`SegmentChain`], [`Contour`](crate::contour::Contour) or [`Shape`] can also
    be drawn onto the canvas if the corresponding `(&Composite, &Style)` tuple
    is provided. See the [module documentation](crate::visualize::intersection)
    for an example.
     */
    pub fn draw(
        &self,
        style: &IntersectionStyle,
        left: Option<(&SegmentChain, &Style)>,
        right: Option<(&Shape, &Style)>,
        context: &cairo::Context,
    ) -> Result<(), cairo::Error> {
        let inv = self.switch();
        inv.draw(style, right, left, context)
    }
}

impl Intersection<ShapeIdx, ()> {
    /**
    Draws the intersection between a [`Shape`] and a
    [`Primitive`](crate::primitive::Primitive) onto the given [`cairo::Context`]
    using the given [`IntersectionStyle`].

    Optionally, the intersected segments of the involved composite types
    [`SegmentChain`], [`Contour`](crate::contour::Contour) or [`Shape`] can also
    be drawn onto the canvas if the corresponding `(&Composite, &Style)` tuple
    is provided. See the [module documentation](crate::visualize::intersection)
    for an example.
     */
    pub fn draw(
        &self,
        style: &IntersectionStyle,
        left: Option<(&Shape, &Style)>,
        context: &cairo::Context,
    ) -> Result<(), cairo::Error> {
        if let Some(s) = left {
            draw_segment_of_shape(s.0, s.1, self.left, context)?;
        }
        let intersection: Intersection<(), ()> = self.point.into();
        return intersection.draw(style, context);
    }
}

impl Intersection<(), ShapeIdx> {
    /**
    Draws the intersection between a [`Primitive`](crate::primitive::Primitive)
    and a [`Shape`] onto the given [`cairo::Context`] using the given
    [`IntersectionStyle`].

    Optionally, the intersected segments of the involved composite types
    [`SegmentChain`], [`Contour`](crate::contour::Contour) or [`Shape`] can also
    be drawn onto the canvas if the corresponding `(&Composite, &Style)` tuple
    is provided. See the [module documentation](crate::visualize::intersection)
    for an example.
     */
    pub fn draw(
        &self,
        style: &IntersectionStyle,
        right: Option<(&Shape, &Style)>,
        context: &cairo::Context,
    ) -> Result<(), cairo::Error> {
        let inv = self.switch();
        inv.draw(style, right, context)
    }
}

impl Intersection<SegmentIdx, SegmentIdx> {
    /**
    Draws the intersection between two [`SegmentChain`]s onto the given
    [`cairo::Context`] using the given [`IntersectionStyle`].

    Optionally, the intersected segments of the involved composite types
    [`SegmentChain`], [`Contour`](crate::contour::Contour) or [`Shape`] can also
    be drawn onto the canvas if the corresponding `(&Composite, &Style)` tuple
    is provided. See the [module documentation](crate::visualize::intersection)
    for an example.
     */
    pub fn draw(
        &self,
        style: &IntersectionStyle,
        left: Option<(&SegmentChain, &Style)>,
        right: Option<(&SegmentChain, &Style)>,
        context: &cairo::Context,
    ) -> Result<(), cairo::Error> {
        if let Some(s) = left {
            draw_segment_of_chain(s.0, s.1, self.left, context)?;
        }
        if let Some(s) = right {
            draw_segment_of_chain(s.0, s.1, self.right, context)?;
        }
        let intersection: Intersection<(), ()> = self.point.into();
        return intersection.draw(style, context);
    }
}

impl Intersection<SegmentIdx, ()> {
    /**
    Draws the intersection between a [`SegmentChain`] and a
    [`Primitive`](crate::primitive::Primitive) onto the given [`cairo::Context`]
    using the given [`IntersectionStyle`].

    Optionally, the intersected segments of the involved composite types
    [`SegmentChain`], [`Contour`](crate::contour::Contour) or [`Shape`] can also
    be drawn onto the canvas if the corresponding `(&Composite, &Style)` tuple
    is provided. See the [module documentation](crate::visualize::intersection)
    for an example.
     */
    pub fn draw(
        &self,
        style: &IntersectionStyle,
        left: Option<(&SegmentChain, &Style)>,
        context: &cairo::Context,
    ) -> Result<(), cairo::Error> {
        if let Some(s) = left {
            draw_segment_of_chain(s.0, s.1, self.left, context)?;
        }
        let intersection: Intersection<(), ()> = self.point.into();
        return intersection.draw(style, context);
    }
}

impl Intersection<(), SegmentIdx> {
    /**
    Draws the intersection between a [`Primitive`](crate::primitive::Primitive)
    and a [`SegmentChain`] onto the given [`cairo::Context`] using the given
    [`IntersectionStyle`].

    Optionally, the intersected segments of the involved composite types
    [`SegmentChain`], [`Contour`](crate::contour::Contour) or [`Shape`] can also
    be drawn onto the canvas if the corresponding `(&Composite, &Style)` tuple
    is provided. See the [module documentation](crate::visualize::intersection)
    for an example.
     */
    pub fn draw(
        &self,
        style: &IntersectionStyle,
        right: Option<(&SegmentChain, &Style)>,
        context: &cairo::Context,
    ) -> Result<(), cairo::Error> {
        if let Some(s) = right {
            draw_segment_of_chain(s.0, s.1, self.right, context)?;
        }
        let intersection: Intersection<(), ()> = self.point.into();
        return intersection.draw(style, context);
    }
}

impl Intersection<(), ()> {
    /**
    Draws the intersection between two
    [`Primitive`](crate::primitive::Primitive)s onto the given
    [`cairo::Context`] using the given [`IntersectionStyle`].

    Optionally, the intersected segments of the involved composite types
    [`SegmentChain`], [`Contour`](crate::contour::Contour) or [`Shape`] can also
    be drawn onto the canvas if the corresponding `(&Composite, &Style)` tuple
    is provided. See the [module documentation](crate::visualize::intersection)
    for an example.
     */
    pub fn draw(
        &self,
        style: &IntersectionStyle,
        context: &cairo::Context,
    ) -> Result<(), cairo::Error> {
        context.set_dash([].as_slice(), 0.0);
        context.set_line_width(style.line_width);
        let lc = &style.line_color;
        context.set_source_rgba(lc.r.into(), lc.g.into(), lc.b.into(), lc.a.into());

        let [x, y] = self.point;

        // First line
        let offset = 0.5 * style.cross_size / context.matrix().xx();
        context.move_to(x - offset, y - offset);
        context.line_to(x + offset, y + offset);
        context.save()?;
        context.identity_matrix();
        context.stroke()?;
        context.restore()?;

        // Second line
        context.move_to(x + offset, y - offset);
        context.line_to(x - offset, y + offset);
        context.save()?;
        context.identity_matrix();
        context.stroke()?;
        context.restore()?;
        return Ok(());
    }
}
