package renderer.canvas;

import basics.math.algebra.Vector;
import optics.light.Color;

public class ContributionBuffer {

    // linear rgb colors
    private volatile Color[][] buffer;
    // amount of samples for every pixel
    private volatile double[][] samples;

    private int width;
    private int height;

    private int maxSample;

    public ContributionBuffer(int width, int height) {
        this.width = width;
        this.height = height;

        this.buffer = new Color[width][height];
        this.samples = new double[width][height];

        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                this.buffer[x][y] = Color.BLACK;
            }
        }
    }

    public void contribute(int x, int y, Color color) {
        buffer[x][y] = buffer[x][y].add(color);
        samples[x][y]++;

        maxSample = Math.max(maxSample, (int) samples[x][y]);
    }

    /**
     * Set all color values and sample count to zero
     * All other calls to contribute() should be blocked during execution
     */
    public void clear() {
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                // clear color
                Color color = buffer[x][y];
                color.r = color.g = color.b = 0;
                // reset sample count
                samples[x][y] = 0;
            }
        }
    }

    private int getRGB(int x, int y) {
        Color linearRGB = buffer[x][y].scale(1.0 / samples[x][y]);

        linearRGB.colorCorrect();

        int red = (int) (linearRGB.r* 255.0);
        int green = (int) (linearRGB.g * 255.0);
        int blue = (int) (linearRGB.b * 255.0);

        red = Math.max(Math.min(red, 255), 0);
        green = Math.max(Math.min(green, 255), 0);
        blue = Math.max(Math.min(blue, 255), 0);

        return 0xff000000 | red << 16 | green << 8 | blue;
    }

    public Color getPixel(int x, int y) {
        return buffer[x][y].scale(1.0 / samples[x][y]);
    }

    public void visualizeSamples(int[] buffer) {
        for (int y = 0; y < height; y++) {
            int col = y * width;

            for (int x = 0; x < width; x++) {
                buffer[col + x] = Color.diagonal(samples[x][y] / maxSample).getIntRGB();
            }
        }
    }

    public void blit(int[] buffer) {
        for (int y = 0; y < height; y++) {
            int col = y * width;

            for (int x = 0; x < width; x++) {
                buffer[col + x] = getRGB(x, y);
            }
        }
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }
}
