import { createElement } from "../../lib/skeleton/index.js";
import assert from "../../lib/assert.js";
import loadWASM, { writeFS, readFS } from "../../helpers/loader_wasm.js";
import { createModal } from "../../components/modal.js";

export default async function(ITable, { $menubar }) {
    const { instance } = await loadWASM(import.meta.url, "./lib/loader_dbf.wasm");

    return class TableImpl extends ITable {
        constructor(response) {
            super();
            writeFS(new Uint8Array(response), "/input.dat");
            writeFS(new Uint8Array(0), "/input.INF"); // not sure what is this for, but xbase64 fails without injecting this file
            const fdOutHeader = writeFS(new Uint8Array(128));
            const fdOutRows = writeFS(new Uint8Array(1024));
            const fdOutMeta = writeFS(new Uint8Array(8));

            const res = assert.truthy(instance.exports["execute"])(fdOutHeader, fdOutRows, fdOutMeta);
            if (res !== 0) throw new Error(`WASM exited with code=${res}`);

            this.header = this.decode(readFS(fdOutHeader)).split(",");
            const body = this.decode(readFS(fdOutRows));
            this.body = body === "" ? [] : body.split("\n").map((line) => {
                const l = line.split(",");
                const obj = {};
                for (let i=0; i<this.header.length; i++) {
                    obj[this.header[i]] = l[i] || null;
                }
                return obj;
            });
            $menubar.add(buttonInfo(new TextDecoder().decode(readFS(fdOutMeta))));
        }

        getHeader() {
            const MAX_SIZE = 40;
            const head = this.header.map((name) => ({
                name,
                size: name === "name" ? MAX_SIZE : name.length,
            }));
            for (let i=0; i<Math.min(this.body.length, 500); i++) {
                for (let j=0; j<head.length; j++) {
                    head[j].size = Math.max(
                        head[j].size,
                        Math.min(MAX_SIZE, (this.body[i][head[j].name] || 'na').length),
                    );
                }
            }
            return head.sort((a, b) => {
                if (a.name === "name") return +1;
                else if (b.name === "name") return -1;
                return 0;
            });
        }

        getBody() {
            return this.body;
        }

        decode(buffer, i = 0) {
            const encodings = ["utf-8", "ISO-8859-1"];
            try {
                return new TextDecoder(encodings[i] || "utf-8", {
                    fatal: !!encodings[i]
                }).decode(buffer);
            } catch (err) {
                if (encodings[i]) return this.decode(buffer, i + 1);
                throw new Error(`Failed to decode buffer: ${err.message}`);
            }
        }
    };
}

function buttonInfo(infoText) {
    const renderModal = createModal({ withButtonsLeft: null, withButtonsRight: null })
    const $el = createElement(`
        <span>
            <img class="component_icon" draggable="false" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgwLjg4MiwwLDAsMC44ODIsNS45LDUuOSkiPgogICAgPHBhdGggc3R5bGU9ImZpbGw6I2YyZjJmMjtmaWxsLW9wYWNpdHk6MSIgZD0ibSA2Mi4xNjIsMCBjIDYuNjk2LDAgMTAuMDQzLDQuNTY3IDEwLjA0Myw5Ljc4OSAwLDYuNTIyIC01LjgxNCwxMi41NTUgLTEzLjM5MSwxMi41NTUgLTYuMzQ0LDAgLTEwLjA0NSwtMy43NTIgLTkuODY5LC05Ljk0NyBDIDQ4Ljk0NSw3LjE3NiA1My4zNSwwIDYyLjE2MiwwIFogTSA0MS41NDMsMTAwIGMgLTUuMjg3LDAgLTkuMTY0LC0zLjI2MiAtNS40NjMsLTE3LjYxNSBsIDYuMDcsLTI1LjQ1NyBjIDEuMDU3LC00LjA3NyAxLjIzLC01LjcwNyAwLC01LjcwNyAtMS41ODgsMCAtOC40NTEsMi44MTYgLTEyLjUxLDUuNTkgTCAyNyw1Mi40MDYgQyAzOS44NjMsNDEuNDggNTQuNjYyLDM1LjA3MiA2MS4wMDQsMzUuMDcyIGMgNS4yODUsMCA2LjE2OCw2LjM2MSAzLjUyNSwxNi4xNDggTCA1Ny41OCw3Ny45OCBjIC0xLjIzNCw0LjcyOSAtMC43MDMsNi4zNTkgMC41MjcsNi4zNTkgMS41ODYsMCA2Ljc4NywtMS45NjMgMTEuODk2LC02LjA0MSBMIDczLDgyLjM3NyBDIDYwLjQ4OCw5NS4xIDQ2LjgzLDEwMCA0MS41NDMsMTAwIFoiIC8+CiAgPC9nPgo8L3N2Zz4K" alt="info">
        </span>
    `);
    $el.onclick = () => {
        renderModal(createElement(`<pre style="margin:-5px 0 0 0">${infoText}</pre>`));
    };
    return $el;
}
