package math.shapes;

import math.utils.Tuple2d;
import math.utils.Tuple3d;

import java.awt.*;
import java.awt.geom.*;

public class Line implements Shape {
    private final GeneralPath path;
    private Tuple2d start;
    private Tuple2d end;
    private final double length;
    private final double cosinus;
    private final double sinus;
    private Tuple3d equation;

    public Line(double x1, double y1, double x2, double y2) {
        start = new Tuple2d(x1, y1);
        end = new Tuple2d(x2, y2);
        length = start.distance(end);
        cosinus = (x2 - x1) / length;
        sinus = (y2 - y1) / length;
        equation = findEquation();
        path = new GeneralPath();
        path.moveTo(start.getX(), start.getY());
        path.lineTo(end.getX(), end.getY());
        path.closePath();
    }

    public Line(Tuple2d start, Tuple2d end) {
        this.start = start;
        this.end = end;
        length = start.distance(end);
        cosinus = (end.getX() - start.getX()) / length;
        sinus = (end.getY() - start.getY()) / length;
        equation = findEquation();
        path = new GeneralPath();
        path.moveTo(start.getX(), start.getY());
        path.lineTo(end.getX(), end.getY());
        path.closePath();
    }

    /**
     * Oblicza współczynnik nachylenia tej linii
     *
     * @return double - zwraca współczynnik nachylenia linii
     */
    public double slope() throws ArithmeticException {
        double xx1 = start.getX() - end.getX();
        double yy1 = start.getY() - end.getY();
        if (xx1 != 0.0) {
            return yy1 / xx1;
        } else {
            throw new ArithmeticException("Próba dzielenia przez 0");
        }
    }

    /**
     * Zwraca współczynniki równania linii w postaci Vectora3d
     *
     * @return Vector3D - zawierający współczynniki równania liniowego
     * Vector3d.x = a, Vector3d.y = b, Vector3d.z = c
     * otrzymujemy równanie linii aX + bY + c = 0;
     */
    public Tuple3d findEquation() {
        if (start.getX() - end.getX() == 0.0) {
            return (new Tuple3d(0, 1, -getStart().getY()));
        } else {
            double m = slope();
            double a = -m;
            double b = 1;
            double c = m * getStart().getX() - getStart().getY();
            return (new Tuple3d(a, b, c));
        }
    }

    @Override
    public boolean contains(Point2D p) {
        return false;
    }

    @Override
    public boolean contains(Rectangle2D r) {
        return false;
    }

    @Override
    public boolean contains(double x, double y) {
        return false;
    }

    @Override
    public boolean contains(double x, double y, double w, double h) {
        return false;
    }

    @Override
    public Rectangle getBounds() {
        return getBounds2D().getBounds();
    }

    @Override
    public Rectangle2D getBounds2D() {
        return path.getBounds2D();
    }

    @Override
    public PathIterator getPathIterator(AffineTransform at) {
        return path.getPathIterator(at);
    }

    @Override
    public PathIterator getPathIterator(AffineTransform at, double flatness) {
        return path.getPathIterator(at, flatness);
    }

    public Tuple2d getStart() {
        return start;
    }

    public void setStart(Tuple2d start) {
        this.start = start;
    }

    @Override
    public boolean intersects(Rectangle2D r) {
        return path.intersects(r);
    }

    @Override
    public boolean intersects(double x, double y, double w, double h) {
        return path.intersects(x, y, w, h);
    }

    @Override
    public String toString() {
        return "[" + "(" + start.getX() + ", " + start.getY() + ")" + ";" + "("
                + end.getX() + ", " + end.getY() + ")" + "]";
    }

    public Tuple2d getEnd() {
        return end;
    }

    public void setEnd(Tuple2d end) {
        this.end = end;
    }

    public Tuple3d getEquation() {
        return equation;
    }

    public void setEquation(Tuple3d equation) {
        this.equation = equation;
    }

    public double getLength() {
        return length;
    }

    public double getCosinus() {
        return cosinus;
    }

    public double getSinus() {
        return sinus;
    }
}
