
import { createCanvas, getPixelInt, setPixel } from '@common/display/canvas';
import { gameLoop } from '@common/game';
import Input from '@common/input';

const sandWidth = 128;

const setup = () => {
    const canvas = createCanvas(window.innerWidth >> 2, window.innerHeight >> 2);
    const ctx = canvas.getContext('2d');
    if (!ctx) {
        throw new Error('Failed to get canvas context');
    }
    document.body.style.backgroundColor = '#eee';

    const imageData = initData();

    return {
        canvas,
        ctx,
        imageData,
    };
};

const initData = () => {
    const imageData = new ImageData(sandWidth, sandWidth);

    for (let x = 0; x < imageData.width; x++) {
        for (let y = 0; y < imageData.height; y++) {
            const index = (y * imageData.width + x) * 4;
            imageData.data[index + 0] = Math.random() < 0.3 ? 255 : 0;
            imageData.data[index + 1] = Math.random() < 0.3 ? 255 : 0;
            imageData.data[index + 2] = Math.random() < 0.3 ? 255 : 0;
            imageData.data[index + 3] = 255;
        }
    }

    return imageData;
}

const emptyColor = 0x000000FF;
const isEmpty = (pixel: number) => (pixel >> 8) === 0;

type State = ReturnType<typeof setup>;

const frame = async (dt: number, state: State): Promise<State> => {
    const { canvas, ctx } = state;

    if (Input.isPressed(Input.KeyCode.SPACE)) {
        state.imageData = initData();
    }

    const imageData = state.imageData;

    for (let y = imageData.height - 2; y >= 0; y--) {
        const direction = Math.random() < 0.5 ? 1 : -1;
        let startX, endX;
        if (direction > 0) {
            startX = 0;
            endX = imageData.width;
        } else {
            startX = imageData.width - 1;
            endX = -1;
        }
        for (let x = startX; x != endX; x += direction) {
            const pixel = getPixelInt(imageData, x, y);
            if (isEmpty(pixel)) continue;

            const pixelBelow = getPixelInt(imageData, x, y + 1);
            if (isEmpty(pixelBelow)) {
                setPixel(imageData, x, y, emptyColor);
                setPixel(imageData, x, y + 1, pixel);
                continue;
            }

            let leftEmpty = false;
            let rightEmpty = false;

            if (x > 0) {
                const pixelLeft = getPixelInt(imageData, x - 1, y + 1);
                leftEmpty = isEmpty(pixelLeft);
            }
            if (x < imageData.width - 1) {
                const pixelRight = getPixelInt(imageData, x + 1, y + 1);
                rightEmpty = isEmpty(pixelRight);
            }

            const selector = Math.random() < 0.5;
            const selectedLeft = (leftEmpty && rightEmpty && selector) || (leftEmpty && !rightEmpty);
            const selectedRight = (leftEmpty && rightEmpty && !selector) || (!leftEmpty && rightEmpty);

            if (selectedLeft) {
                setPixel(imageData, x, y, emptyColor);
                setPixel(imageData, x - 1, y + 1, pixel);
            } else if (selectedRight) {
                setPixel(imageData, x, y, emptyColor);
                setPixel(imageData, x + 1, y + 1, pixel);
            }
        }
    }

    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.putImageData(imageData, (canvas.width - sandWidth) / 2, (canvas.height - sandWidth) / 2);

    return {
        ...state,
    };
};

export default gameLoop(setup, frame);