import {toFixed, mul} from './fixed.js';

let toFixedLog = toFixed;

function toFixed16(val) {
    // 4.12
    return Math.round(val * (2 ** 12))
}

let four16 = toFixed16(4);

function imul(a, b) {
    return Math.imul(a, b) >> 12;
}

function logmul(a, b) {
    if ((a | b) == 0) {
        return 0;
    }
    let neg = 0;
    if (a < 0) {
        a = -a;
        neg++;
    }
    if (b < 0) {
        b = -b;
        neg++;
    }
    let product = mul(a, b);
    if (neg == 1) {
        product = -product;
    }
    return product;
}

let fourLog = toFixedLog(4);

let max = 256;
let width = 256;
let height = 256;

let zoom = 0;
let offsetX = 0;
let offsetY = 0;

function scale() {
    return 1 / (2 ** zoom);
}

let cancelled = false;

async function cancel() {
    cancelled = true;
    return await nap(100);
}

let black = 0xff000000;
let tricolor = [
    0xffff0000,
    0xff00ff00,
    0xff0000ff,
];

let palette = new Uint32Array(256);
palette[0] = black;
for (let i = 0; i < 255; i++) {
    palette[i + 1] = tricolor[i % 3];
}
function nap(ms=0) {
    return new Promise((resolve) => setTimeout(() => resolve(), ms));
}

function attach(id, func) {
    document.getElementById(id).addEventListener('click', (_event) => {
        cancel().then(() => {
            func();
            run();
        });
    });
}

attach('zoom-in', () => {
    zoom++;
});

attach('zoom-out', () => {
    if (zoom >= 1) {
        zoom--;
    }
});

attach('left', () => {
    offsetX -= scale();
});

attach('right', () => {
    offsetX += scale();
});

attach('up', () => {
    offsetY -= scale();
});

attach('down', () => {
    offsetY += scale();
});

async function setup(id, iterfunc) {
    let canvas = document.getElementById(id);
    let ctx = canvas.getContext('2d');
    let imageData = ctx.createImageData(width, height);
    let rgba = new Uint32Array(imageData.data.buffer);

    cancelled = false;
    for (let y = 0; y < height; y++) {
        let cy = scale() * (y * 2 - height) / (height / 2) + offsetY;
        for (let x = 0; x < width; x++) {
            let cx = scale() * (x * 2 - width) / (width / 2) + offsetX;
            let i = iterfunc(cx, cy);
            let color = palette[i];
            rgba[y * width + x] = color;

            if (x % 256 == 255) {
                ctx.putImageData(imageData, 0, 0);
                await nap();
                if (cancelled) {
                    return;
                }
            }
        }
    }
}

function run() {
    setup('float', (cx, cy) => {
        let zx = 0;
        let zy = 0;
        let zx_2 = 0;
        let zy_2 = 0;
        let zx_zy = 0;
        for (let i = 1; i < max; i++) {
            zx = zx_2 - zy_2 + cx;
            zy = zx_zy + zx_zy + cy;
            zx_2 = zx * zx;
            zy_2 = zy * zy;
            zx_zy = zx * zy;
            if (zx_2 + zy_2 >= 4) {
                return i;
            }
        }
        return 0;
    }).then(() => {
        console.log('float done');
    });
    
    setup('imul', (cx, cy) => {
        cx = toFixed16(cx);
        cy = toFixed16(cy);
        let zx = 0;
        let zy = 0;
        let zx_2 = 0;
        let zy_2 = 0;
        let zx_zy = 0;
        for (let i = 1; i < max; i++) {
            zx = zx_2 - zy_2 + cx;
            zy = zx_zy + zx_zy + cy;
            zx_2 = imul(zx, zx);
            zy_2 = imul(zy, zy);
            zx_zy = imul(zx, zy);
            if (zx_2 + zy_2 >= four16) {
                return i;
            }
        }
        return 0;
    }).then(() => {
        console.log('imul done');
    });
    
    setup('log', (cx, cy) => {
        cx = toFixedLog(cx);
        cy = toFixedLog(cy);
        let zx = 0;
        let zy = 0;
        let zx_2 = 0;
        let zy_2 = 0;
        let zx_zy = 0;
        for (let i = 1; i < max; i++) {
            zx = zx_2 - zy_2 + cx;
            zy = zx_zy + zx_zy + cy;
            zx_2 = logmul(zx, zx);
            zy_2 = logmul(zy, zy);
            zx_zy = logmul(zx, zy);
            if (zx_2 + zy_2 >= fourLog) {
                return i;
            }
        }
        return 0;
    }).then(() => {
        console.log('log done');
    });
}

run();