package eu.jonahbauer.raytracing.render;

import org.jetbrains.annotations.NotNull;

import java.util.Random;

public record Color(double r, double g, double b) {
    public static final @NotNull Color BLACK = new Color(0.0, 0.0, 0.0);
    public static final @NotNull Color WHITE = new Color(1.0, 1.0, 1.0);
    public static final @NotNull Color RED = new Color(1.0, 0.0, 0.0);
    public static final @NotNull Color GREEN = new Color(0.0, 1.0, 0.0);
    public static final @NotNull Color BLUE = new Color(0.0, 0.0, 1.0);

    public static @NotNull Color lerp(@NotNull Color a, @NotNull Color b, double t) {
        if (t < 0) return a;
        if (t > 1) return b;
        return new Color(
                (1 - t) * a.r + t * b.r,
                (1 - t) * a.g + t * b.g,
                (1 - t) * a.b + t * b.b
        );
    }

    public static @NotNull Color multiply(@NotNull Color a, @NotNull Color b) {
        return new Color(a.r() * b.r(), a.g() * b.g(), a.b() * b.b());
    }

    public static @NotNull Color add(@NotNull Color a, @NotNull Color b) {
        return new Color(a.r() + b.r(), a.g() + b.g(), a.b() + b.b());
    }

    public static @NotNull Color random(@NotNull Random random) {
        return new Color(random.nextDouble(), random.nextDouble(), random.nextDouble());
    }

    public static @NotNull Color random(@NotNull Random random, double min, double max) {
        var span = max - min;
        return new Color(
                Math.fma(random.nextDouble(), span, min),
                Math.fma(random.nextDouble(), span, min),
                Math.fma(random.nextDouble(), span, min)
        );
    }

    public static @NotNull Color average(@NotNull Color current, @NotNull Color next, int index) {
        return new Color(
                current.r() + (next.r() - current.r()) / index,
                current.g() + (next.g() - current.g()) / index,
                current.b() + (next.b() - current.b()) / index
        );
    }

    public static @NotNull Color gamma(@NotNull Color color, double gamma) {
        if (gamma == 1.0) {
            return color;
        } else if (gamma == 2.0) {
            return new Color(
                    Math.sqrt(color.r()),
                    Math.sqrt(color.g()),
                    Math.sqrt(color.b())
            );
        } else {
            return new Color(
                    Math.pow(color.r(), 1 / gamma),
                    Math.pow(color.g(), 1 / gamma),
                    Math.pow(color.b(), 1 / gamma)
            );
        }
    }

    public Color {}
    
    public Color(int red, int green, int blue) {
        this(red / 255f, green / 255f, blue / 255f);
    }

    public int red() {
        return toInt(r);
    }

    public int green() {
        return toInt(g);
    }

    public int blue() {
        return toInt(b);
    }

    private static int toInt(double value) {
        return Math.max(0, Math.min(255, (int) (255.99 * value)));
    }
}
