package math.shapes;

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

/**
 * Klasa umożliwiająca tworzenie obiektów typu Arrow (strzałka). Obiekt tej
 * klasy może być rysowany podobnie jak inne kształty Javy, a więc tylko kontur,
 * tylko wypełnienie albo kontur z wypełnieniem. Strzałka może mieć dowolny
 * kolor. Kontur może mieć dowolny kolor i być dowolnego typu.
 */
public class Arrow implements Shape {
    private final GeneralPath path;

    /**
     * Tworzy obiekt typu Arrow
     *
     * @param endX        float - współrzędna x tylnego końca strzałki
     * @param endY        float - współrzędna y tylnego końca strzałki
     * @param arrowWidth  float - długość strzałki
     * @param arrowHeight float - grubość strzałki
     * @param arrowAngle  float - kat pod ktorym jest skierowana strzalka
     *                    katy podawane sa w stopniach, kat zerowy to polozenie godziny 3.00,
     *                    katy wzrastaja w kierunku przeciwnym do wskazowek zegara
     * @param brudWidth   float - długość grota mierzona wzdłuż osi strzałki
     * @param brudAngle   float - kąt między osią strzałki a brzegiem grota
     * @param close       boolean -
     *                    <ul>
     *                    <li>true - grot bez wcięcia *
     *                    <li>false - grot z wcięciem
     *                    </ul>
     */
    public Arrow(float endX, float endY, float arrowWidth, float arrowHeight,
                 float arrowAngle, float brudWidth, float brudAngle, boolean close) {
        float cosa = (float) Math.cos(Math.toRadians(arrowAngle));
        float sina = (float) Math.sin(Math.toRadians(arrowAngle));
        float half = arrowHeight / 2f;
        float cosah = cosa * half;
        float sinah = sina * half;
        float startX = endX + arrowWidth
                * (float) Math.cos(Math.toRadians(-arrowAngle));
        float startY = endY + arrowWidth
                * (float) Math.sin(Math.toRadians(-arrowAngle));
        float middleX = endX + (arrowWidth - brudWidth)
                * (float) Math.cos(Math.toRadians(-arrowAngle));//x7
        float middleY = endY + (arrowWidth - brudWidth)
                * (float) Math.sin(Math.toRadians(-arrowAngle));//y7
        float cosb = (float) Math.cos(Math.toRadians(brudAngle));
        float sinb = (float) Math.sin(Math.toRadians(brudAngle));
        float w2 = half * cosb / sinb;
        float mmiddleX = endX + (arrowWidth - w2)
                * (float) Math.cos(Math.toRadians(-arrowAngle));//
        float mmiddleY = endY + (arrowWidth - w2)
                * (float) Math.sin(Math.toRadians(-arrowAngle));//
        float w3 = brudWidth - w2;
        float w4 = w3 * sinb / cosb;
        float w5 = w4 + half;
        //-
        float x1 = endX - sinah;
        float y1 = endY - cosah;
        float x2 = endX + sinah;
        float y2 = endY + cosah;
        float x5 = middleX + sinah;
        float y5 = middleY + cosah;
        float x6 = middleX - sinah;
        float y6 = middleY - cosah;
        float x8 = mmiddleX + sinah;
        float y8 = mmiddleY + cosah;
        float x9 = mmiddleX - sinah;
        float y9 = mmiddleY - cosah;
        float x10 = middleX + sina * w5;
        float y10 = middleY + cosa * w5;
        float x11 = middleX - sina * w5;
        float y11 = middleY - cosa * w5;
        //-
        path = new GeneralPath();
        if (close) {
            path.moveTo(x1, y1);
            path.lineTo(x2, y2);
            path.lineTo(x5, y5);
            path.lineTo(x10, y10);
            path.lineTo(x8, y8);
            path.lineTo(startX, startY);
            path.lineTo(x9, y9);
            path.lineTo(x11, y11);
            path.lineTo(x6, y6);
            path.closePath();
        } else {
            path.moveTo(x1, y1);
            path.lineTo(x2, y2);
            path.lineTo(x8, y8);
            path.lineTo(startX, startY);
            path.lineTo(x9, y9);
            path.lineTo(x1, y1);
            path.moveTo(x11, y11);
            path.lineTo(x9, y9);
            path.moveTo(x10, y10);
            path.lineTo(x8, y8);
        }
    }

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

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

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

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

    @Override
    public Rectangle getBounds() {
        return path.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);
    }

    @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);
    }
}
