import {
    default as wasm,
    Chip8Neo
} from "./chip_ahoyto.js";

const PIXEL_SET_COLOR = 0x50cb93ff;
const PIXEL_UNSET_COLOR = 0x1b1a17ff;

const LOGIC_HZ = 480;
const TIMER_HZ = 60;
const VISUAL_HZ = 60;

const ROM = "res/roms/pong.ch8";

const state = {
    chip8: null,
    canvas: null,
    canvasScaled: null,
    canvasCtx: null,
    canvasScaledCtx: null,
    image: null,
    videoBuff: null
};

(async () => {
    // initializes the WASM module, this is required
    // so that the global symbols become available
    await wasm();

    // initializes the canvas sub-sytem
    initCanvas();
    registerDrop();

    // loads the ROM data and converts it into the
    // target u8 array bufffer
    const response = await fetch(ROM);
    const blob = await response.blob();
    const arrayBuffer = await blob.arrayBuffer();
    const data = new Uint8Array(arrayBuffer);

    // creates the CHIP-8 instance and resets it
    state.chip8 = new Chip8Neo();
    state.chip8.reset_hard_ws();
    state.chip8.load_rom_ws(data);

    // runs the sequence as an infinite loop, running
    // the associated CPU cycles accordingly
    while (true) {        
        const ratioLogic = LOGIC_HZ / VISUAL_HZ;
        for(let i = 0; i < ratioLogic; i++) {
            state.chip8.clock_ws();
        }

        const ratioTimer = TIMER_HZ / VISUAL_HZ;
        for(let i = 0; i < ratioTimer; i++) {
            state.chip8.clock_dt_ws();
            state.chip8.clock_st_ws();
        }

        // updates the canvas object with the new
        // visual information comming in
        updateCanvas(state.chip8.vram_ws());
        
        // waits a little bit for the next frame to be draw
        // @todo need to define target time for draw
        await new Promise((resolve, reject) => {
            setTimeout(resolve, 1000 / VISUAL_HZ);
        })
    }
})();

const registerDrop = () => {
    document.addEventListener("drop", async (e) => {
        e.preventDefault();
        e.stopPropagation();

        if (!e.dataTransfer.files) return;

        const file = e.dataTransfer.files[0];

        const arrayBuffer = await file.arrayBuffer();
        const data = new Uint8Array(arrayBuffer);

        state.chip8.reset_hard_ws();
        state.chip8.load_rom_ws(data);
    });
    document.addEventListener("dragover", async (e) => {
        e.preventDefault();
        e.stopPropagation();

        console.info("draging over");
    });
}

const initCanvas = () => {
    // initializes the off-screen canvas that is going to be
    // used in the drawing proces
    state.canvas = document.createElement("canvas");
    state.canvas.width = 64;
    state.canvas.height = 32;
    state.canvasCtx = state.canvas.getContext("2d");

    state.canvasScaled = document.getElementById("chip-canvas");
    state.canvasScaledCtx = state.canvasScaled.getContext("2d");

    state.canvasScaledCtx.scale(state.canvasScaled.width / state.canvas.width, state.canvasScaled.height / state.canvas.height);
    state.canvasScaledCtx.imageSmoothingEnabled = false;

    state.image = state.canvasCtx.createImageData(state.canvas.width, state.canvas.height);
    state.videoBuff = new DataView(state.image.data.buffer);
}

const updateCanvas = (pixels) => {
    for (let i = 0; i < pixels.length; i++) {
        state.videoBuff.setUint32(i * 4, pixels[i] ? PIXEL_SET_COLOR : PIXEL_UNSET_COLOR);
    }
    state.canvasCtx.putImageData(state.image, 0, 0);
    state.canvasScaledCtx.drawImage(state.canvas, 0, 0);
}