use freya_engine::prelude::*;
use freya_node_state::{
    Border,
    BorderAlignment,
    CornerRadius,
};
use torin::prelude::Area;
pub enum BorderShape {
    DRRect(RRect, RRect),
    Path(Path),
}
pub fn render_border(
    canvas: &Canvas,
    rounded_rect: RRect,
    area: Area,
    border: &Border,
    corner_radius: CornerRadius,
) {
    let mut border_paint = Paint::default();
    border_paint.set_style(PaintStyle::Fill);
    border_paint.set_anti_alias(true);
    border.fill.apply_to_paint(&mut border_paint, area);
    match border_shape(*rounded_rect.rect(), corner_radius, border) {
        BorderShape::DRRect(outer, inner) => {
            canvas.draw_drrect(outer, inner, &border_paint);
        }
        BorderShape::Path(path) => {
            canvas.draw_path(&path, &border_paint);
        }
    }
}
pub fn border_shape(
    base_rect: Rect,
    base_corner_radius: CornerRadius,
    border: &Border,
) -> BorderShape {
    let border_alignment = border.alignment;
    let border_width = border.width;
    let (outer_rrect, outer_corner_radius) = {
        let corner_radius = CornerRadius {
            top_left: outer_border_path_corner_radius(
                border_alignment,
                base_corner_radius.top_left,
                border_width.top,
                border_width.left,
            ),
            top_right: outer_border_path_corner_radius(
                border_alignment,
                base_corner_radius.top_right,
                border_width.top,
                border_width.right,
            ),
            bottom_left: outer_border_path_corner_radius(
                border_alignment,
                base_corner_radius.bottom_left,
                border_width.bottom,
                border_width.left,
            ),
            bottom_right: outer_border_path_corner_radius(
                border_alignment,
                base_corner_radius.bottom_right,
                border_width.bottom,
                border_width.right,
            ),
            smoothing: base_corner_radius.smoothing,
        };
        let rrect = RRect::new_rect_radii(
            {
                let mut rect = base_rect;
                let alignment_scale = match border_alignment {
                    BorderAlignment::Outer => 1.0,
                    BorderAlignment::Center => 0.5,
                    BorderAlignment::Inner => 0.0,
                };
                rect.left -= border_width.left * alignment_scale;
                rect.top -= border_width.top * alignment_scale;
                rect.right += border_width.right * alignment_scale;
                rect.bottom += border_width.bottom * alignment_scale;
                rect
            },
            &[
                (corner_radius.top_left, corner_radius.top_left).into(),
                (corner_radius.top_right, corner_radius.top_right).into(),
                (corner_radius.bottom_right, corner_radius.bottom_right).into(),
                (corner_radius.bottom_left, corner_radius.bottom_left).into(),
            ],
        );
        (rrect, corner_radius)
    };
    let (inner_rrect, inner_corner_radius) = {
        let corner_radius = CornerRadius {
            top_left: inner_border_path_corner_radius(
                border_alignment,
                base_corner_radius.top_left,
                border_width.top,
                border_width.left,
            ),
            top_right: inner_border_path_corner_radius(
                border_alignment,
                base_corner_radius.top_right,
                border_width.top,
                border_width.right,
            ),
            bottom_left: inner_border_path_corner_radius(
                border_alignment,
                base_corner_radius.bottom_left,
                border_width.bottom,
                border_width.left,
            ),
            bottom_right: inner_border_path_corner_radius(
                border_alignment,
                base_corner_radius.bottom_right,
                border_width.bottom,
                border_width.right,
            ),
            smoothing: base_corner_radius.smoothing,
        };
        let rrect = RRect::new_rect_radii(
            {
                let mut rect = base_rect;
                let alignment_scale = match border_alignment {
                    BorderAlignment::Outer => 0.0,
                    BorderAlignment::Center => 0.5,
                    BorderAlignment::Inner => 1.0,
                };
                rect.left += border_width.left * alignment_scale;
                rect.top += border_width.top * alignment_scale;
                rect.right -= border_width.right * alignment_scale;
                rect.bottom -= border_width.bottom * alignment_scale;
                rect
            },
            &[
                (corner_radius.top_left, corner_radius.top_left).into(),
                (corner_radius.top_right, corner_radius.top_right).into(),
                (corner_radius.bottom_right, corner_radius.bottom_right).into(),
                (corner_radius.bottom_left, corner_radius.bottom_left).into(),
            ],
        );
        (rrect, corner_radius)
    };
    if base_corner_radius.smoothing > 0.0 {
        let mut path = Path::new();
        path.set_fill_type(PathFillType::EvenOdd);
        path.add_path(
            &outer_corner_radius.smoothed_path(outer_rrect),
            Point::new(outer_rrect.rect().x(), outer_rrect.rect().y()),
            None,
        );
        path.add_path(
            &inner_corner_radius.smoothed_path(inner_rrect),
            Point::new(inner_rrect.rect().x(), inner_rrect.rect().y()),
            None,
        );
        BorderShape::Path(path)
    } else {
        BorderShape::DRRect(outer_rrect, inner_rrect)
    }
}
fn outer_border_path_corner_radius(
    alignment: BorderAlignment,
    corner_radius: f32,
    width_1: f32,
    width_2: f32,
) -> f32 {
    if alignment == BorderAlignment::Inner || corner_radius == 0.0 {
        return corner_radius;
    }
    let mut offset = if width_1 == 0.0 {
        width_2
    } else if width_2 == 0.0 {
        width_1
    } else {
        width_1.min(width_2)
    };
    if alignment == BorderAlignment::Center {
        offset *= 0.5;
    }
    corner_radius + offset
}
fn inner_border_path_corner_radius(
    alignment: BorderAlignment,
    corner_radius: f32,
    width_1: f32,
    width_2: f32,
) -> f32 {
    if alignment == BorderAlignment::Outer || corner_radius == 0.0 {
        return corner_radius;
    }
    let mut offset = if width_1 == 0.0 {
        width_2
    } else if width_2 == 0.0 {
        width_1
    } else {
        width_1.min(width_2)
    };
    if alignment == BorderAlignment::Center {
        offset *= 0.5;
    }
    corner_radius - offset
}