package optics.light;

import basics.math.algebra.Vector;

public class Color {

    public static final Color BLACK = new Color(0,0,0);
    public static final Color WHITE = Color.diagonal(1.0);

    public double r;
    public double g;
    public double b;

    public Color(double r, double g, double b) {
        this.r = r;
        this.g = g;
        this.b = b;
    }

    public Color(double k) {
        this.r = k;
        this.g = k;
        this.b = k;
    }

    public static Color diagonal(double k) {
        return new Color(k, k, k);
    }

    public static Color fromVector(Vector vector) {
        return new Color(vector.x, vector.y, vector.z);
    }

    public Color add(Color other) {
        return new Color(this.r + other.r, this.g + other.g, this.b + other.b);
    }

    public Color sub(Color other) {
        return new Color(this.r - other.r, this.g - other.g, this.b - other.b);
    }

    public Color mul(Color other) {
        return new Color(this.r * other.r, this.g * other.g, this.b * other.b);
    }

    public Color scale(double scalar) {
        return new Color(this.r * scalar, this.g * scalar, this.b * scalar);
    }

    public double average() {
        return (this.r + this.g + this.b) / 3.0;
    }

    @Override
    public String toString() {
        return String.format("{ %f, %f, %f }", this.r, this.g, this.b);
    }

    public Color shuffle(Color.SwizzleMask mask) {
        Color result = new Color(r, g, b);

        switch (mask) {
            case rgb -> {
            }
            case grb -> {
                result.r = this.g;
                result.g = this.r;
                result.b = this.b;
            }
            case brg -> {
                result.r = this.b;
                result.g = this.r;
                result.b = this.g;
            }
            case bgr -> {
                result.r = this.b;
                result.g = this.g;
                result.b = this.r;
            }
            case rbg -> {
                result.r = this.r;
                result.g = this.b;
                result.b = this.g;
            }
            case gbr -> {
                result.r = this.g;
                result.g = this.b;
                result.b = this.r;
            }
        }

        return result;
    }

    public Color negate() {
        return new Color(-r, -g, -b);
    }

    public Color cross(Color other) {
        return new Color(
                this.g * other.b - this.b * other.g,
                this.b * other.r - this.r * other.b,
                this.r * other.g - this.g * other.r
        );
    }

    public Color lerp(Color other, double k) {
        return this.scale(k).add(other.scale(1.0 - k));
    }

    public void colorCorrect() {
        this.r = tonemap(this.r);
        this.g = tonemap(this.g);
        this.b = tonemap(this.b);
    }

    private double tonemap(double k) {
        return -1 / (k + 1) + 1;
    }

    public int getIntRGB() {
        int r = (int) (this.r * 255);
        int g = (int) (this.g * 255);
        int b = (int) (this.b * 255);

        return 0xff000000 | (r << 16) | (g << 8) | b;
    }

    public enum SwizzleMask {
        rgb,
        grb,
        brg,
        bgr,
        rbg,
        gbr,
    }
}
