import * as Binutils from "./binutils.mjs"
import * as Triscit from "./triscit.mjs"

const SamplePrograms = [
    `05 0d 
61 62 63 64 65 66 67 68 69 6a 00 
02 02 
01 02 
06
`,
    `05 31
61 62 63 64 65 66 67 68 69 6a 00
70 61 73 73 77 6f 72 64 00
53 75 70 65 72 20 53 65 63 72 65 74 00
53 6f 72 72 79 2c 20 77 72 6f 6e 67 2e 00
02 02
03 02 0d
04 3b
01 16
06
01 23
06
`
]

function fill(element, values) {
    for (let e of element.querySelectorAll("[data-fill]")) {
        e.textContent = values[e.dataset.fill]
    }
}

class UI {
    constructor(program=[]) {
        this.program = program
        this.Init()
        this.Reset()
    }

    Init() {
        for (let e of document.querySelectorAll("[data-control]")) {
            switch (e.dataset.control) {
            case "step":
                e.addEventListener("click", () => this.Step())
                break
            case "back":
                e.addEventListener("click", () => this.Unstep())
                break
            case "reset":
                e.addEventListener("click", () => this.Reset())
                break
            case "input":
                e.addEventListener("input", () => this.SetInput())
                break
            case "program":
                e.addEventListener("input", () => this.SetProgram())
            }
        }

        let ih = document.querySelector("#instructions-help")
        for (let i = 0; i < Triscit.Instructions.length; i++) {
            let inst = Triscit.Instructions[i]
            let doc = document.querySelector("template#instruction-help").content.cloneNode(true)
            let tr = doc.firstElementChild
            fill(tr, {
                num: Binutils.Hexlify([i]),
                args: inst.Args.join(" "),
                name: inst.Name,
                description: inst.Description,
            })
            ih.appendChild(doc)
        }

        let fields = new URLSearchParams(window.location.search);
        let progNumber = fields.get("p") || 0
        let progElement = document.querySelector('[data-control="program"]')
        let prog = SamplePrograms[progNumber]
        if (prog) {
            progElement.value = SamplePrograms[progNumber]
            progElement.parentElement.classList.add("is-hidden")
        }

        this.SetInput()
        this.SetProgram()
    }

    Reset() {
        this.cpu = new Triscit.CPU(this.program, this.input)
        this.stack = []
        this.Refresh()
    }

    Refresh() {
        let inste = document.querySelector("#instructions")
        while (inste.firstChild) inste.firstChild.remove()
        for (let i of this.cpu.DisassembleProgram()) {
            let doc = document.querySelector("template#instruction").content.cloneNode(true)
            let tr = doc.firstElementChild
            fill(tr, {
                addr: Binutils.Hexlify([i.addr]),
                name: i.name,
                hex: Binutils.Hexlify(i.buf),
            })
            
            let args = tr.querySelector(".args")
            for (let a of i.args) {
                if (! isNaN(a)) {
                    a = Binutils.Hexlify([a])
                }
                args.textContent += `${a} `
            }

            inste.appendChild(tr)
            if (i.addr == this.cpu.PC) {
                tr.classList.add("is-selected")
                tr.scrollIntoView(false)
                // let top = tr.offsetTop
                // let parent = tr.parentElement

                // tr.parentElement.scrollTop = top
            }
        }

        for (let e of document.querySelectorAll("[data-flag]")) {
            let mask = 0
            let className = "is-info"
            switch (e.dataset.flag) {
            case "negative":
                mask = Triscit.FLAG_NEGATIVE
                break
            case "halt":
                mask = Triscit.FLAG_HALT
                break
            case "fire":
                mask = Triscit.FLAG_ABLAZE
                className = "is-danger"
                break
            }
            if (this.cpu.Flags & mask) {
                e.classList.add(className)
            } else {
                e.classList.remove(className)
            }
        }

        for (let cpuBox of document.querySelectorAll("#cpu-box")) {
            if (this.cpu.Flags & Triscit.FLAG_ABLAZE) {
                cpuBox.classList.add("ablaze")
            } else {
                cpuBox.classList.remove("ablaze")
            }
        }

        for (let e of document.querySelectorAll("[data-value]")) {
            switch (e.dataset.value) {
            case "pc":
                e.textContent = Binutils.Hexlify([this.cpu.PC])
                break
            case "output":
                e.textContent = Binutils.Stringify(this.cpu.Output)
                break
            }
        }
    }

    Step() {
        if (this.cpu.Flags & Triscit.FLAG_HALT) {
            return
        }
        this.stack.push(this.cpu)
        this.cpu = this.cpu.Clone()
        this.cpu.Step()
        this.Refresh()
    }

    Unstep() {
        if (this.stack.length == 0) {
            return
        }
        this.cpu = this.stack.pop()
        this.Refresh()
    }

    SetProgram() {
        let e =document.querySelector('[data-control="program"]')
        this.program = Binutils.Unhexlify(e.value || "")
        this.Reset()
    }

    SetInput() {
        let e = document.querySelector('[data-control="input"]')
        let v = e.value || ""
        this.input = Binutils.Unescape(v)
        this.Reset()
    }
}

function init() {
    let app = new UI()
    window.app = app
}

if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", init)
} else {
    init()
}
