package com.knecon.fforesight.service.layoutparser.processor.visualization;

import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import lombok.experimental.UtilityClass;

@UtilityClass
public class ConnectionLineUtil {

    public static Line2D[] splitRectangleIntoLines(Rectangle2D rect) {

        double x = rect.getX();
        double y = rect.getY();
        double width = rect.getWidth();
        double height = rect.getHeight();

        Line2D[] lines = new Line2D[4];
        lines[0] = new Line2D.Double(x, y, x + width, y);           // Top
        lines[1] = new Line2D.Double(x + width, y, x + width, y + height); // Right
        lines[2] = new Line2D.Double(x + width, y + height, x, y + height); // Bottom
        lines[3] = new Line2D.Double(x, y + height, x, y);          // Left

        return lines;
    }


    public static Line2D transform(Line2D line2D, AffineTransform affineTransform) {

        var p1 = affineTransform.transform(line2D.getP1(), null);
        var p2 = affineTransform.transform(line2D.getP2(), null);
        return new Line2D.Double(p1, p2);
    }


    public static double length(Line2D line2D) {

        return line2D.getP1().distance(line2D.getP2());
    }


    public static Line2D findClosestMidpointLine(Rectangle2D rect1, Rectangle2D rect2) {

        Line2D[] lines1 = splitRectangleIntoLines(rect1);
        Line2D[] lines2 = splitRectangleIntoLines(rect2);

        Line2D closestLine1 = null;
        Line2D closestLine2 = null;
        double minDistance = Double.MAX_VALUE;

        for (Line2D line1 : lines1) {
            for (Line2D line2 : lines2) {
                double distance = lineDistance(line1, line2);
                if (distance < minDistance) {
                    minDistance = distance;
                    closestLine1 = line1;
                    closestLine2 = line2;
                }
            }
        }

        if (closestLine1 == null || closestLine2 == null) {
            throw new IllegalStateException("Could not find closest lines");
        }

        Point2D midpoint1 = getMidpoint(closestLine1);
        Point2D midpoint2 = getMidpoint(closestLine2);

        return new Line2D.Double(midpoint1, midpoint2);
    }


    private static double lineDistance(Line2D line1, Line2D line2) {

        return Math.abs(getMidpoint(line1).distance(getMidpoint(line2)));
    }


    private static Point2D getMidpoint(Line2D line) {

        double x = (line.getX1() + line.getX2()) / 2;
        double y = (line.getY1() + line.getY2()) / 2;
        return new Point2D.Double(x, y);
    }


    public static Line2D[] createArrowHead(Line2D line, double arrowLength) {

        Point2D start = line.getP1();
        Point2D end = line.getP2();

        // Calculate the angle of the line
        double angle = Math.atan2(end.getY() - start.getY(), end.getX() - start.getX());

        // Calculate the points for the two arrow lines
        double arrowHeadAngle = Math.PI / 6;
        double x1 = end.getX() - arrowLength * Math.cos(angle - arrowHeadAngle);
        double y1 = end.getY() - arrowLength * Math.sin(angle - arrowHeadAngle);
        double x2 = end.getX() - arrowLength * Math.cos(angle + arrowHeadAngle);
        double y2 = end.getY() - arrowLength * Math.sin(angle + arrowHeadAngle);

        // Create and return the two arrow lines
        Line2D arrow1 = new Line2D.Double(end, new Point2D.Double(x1, y1));
        Line2D arrow2 = new Line2D.Double(end, new Point2D.Double(x2, y2));

        return new Line2D[]{arrow1, arrow2};
    }

}
