package io.github.js0ny.ilp_coursework.data.common;

/**
 * Represents the data transfer object for angle
 *
 * @param val value of the angle in degrees
 */
public record Angle(double degrees) {
    private static final double STEP = 22.5;
    private static final double EPSILON = 1e-10;

    public Angle {
        if (degrees < 0 || degrees >= 360) {
            throw new IllegalArgumentException("Angle must be in range [0, 360). Got: " + degrees);
        }

        // Should be a multiple of 22.5 (one of the 16 major directions)
        double remainder = degrees % STEP;

        // Floating point modulo may have tiny errors, e.g. 45.0 % 22.5 could be 0.0 or
        // 1.0e-15
        // So we need to check if the remainder is small enough, or close enough to STEP
        // (handling negative errors)
        if (Math.abs(remainder) > EPSILON && Math.abs(remainder - STEP) > EPSILON) {
            throw new IllegalArgumentException(
                    "Angle must be a multiple of 22.5 (one of the 16 major directions). Got: "
                            + degrees);
        }
    }

    public static Angle fromIndex(int index) {
        if (index < 0 || index > 15) {
            throw new IllegalArgumentException("Direction index must be between 0 and 15");
        }
        return new Angle(index * STEP);
    }

    public static Angle snap(double rawAngle) {
        double normalized = normalize(rawAngle);
        double snapped = Math.round(normalized / STEP) * STEP;
        return new Angle(normalize(snapped));
    }

    public Angle offset(int increments) {
        double rotated = degrees + increments * STEP;
        return new Angle(normalize(rotated));
    }

    private static double normalize(double angle) {
        double normalized = angle % 360;
        if (normalized < 0) {
            normalized += 360;
        }
        return normalized;
    }

    public static double toRadians(double degrees) {
        return Math.toRadians(degrees);
    }

    public double toRadians() {
        return Math.toRadians(degrees);
    }
}
