package razorvine.ksim65

import kotlin.math.max

class Monitor(val bus: Bus, val cpu: Cpu6502Core) {

    private val disassembler = Disassembler(cpu)

    fun command(command: String): IVirtualMachine.MonitorCmdResult {
        if (command.isEmpty()) return IVirtualMachine.MonitorCmdResult("", "", false)

        return when (command[0]) {
            'h', '?' -> {
                val text = "h)elp  m)emory  i)nspect  f)ill  p)oke  g)o  a)ssemble  d)isassemble\n$ # % for hex, dec, bin number"
                IVirtualMachine.MonitorCmdResult(text, "", false)
            }
            'f' -> {
                val parts = command.substring(1).trim().split(' ')
                if (parts.size != 3) IVirtualMachine.MonitorCmdResult("?syntax error", command, false)
                else {
                    val start = Assembler.parseNumber(parts[0])
                    val end = Assembler.parseNumber(parts[1])
                    val value = Assembler.parseNumber(parts[2]).toShort()
                    for (addr in start..end) {
                        bus.write(addr, value)
                    }
                    IVirtualMachine.MonitorCmdResult("ok", "", true)
                }
            }
            'm' -> {
                val addresses = command.substring(1).trim().split(' ')
                val start = Assembler.parseNumber(addresses[0])
                val end = if (addresses.size > 1) Assembler.parseNumber(addresses[1]) else start+1
                val result = mutableListOf<String>()
                for (addr in start until end step 16) {
                    result.add("m$${hexW(addr)}  "+(0..15).joinToString(" ") { hexB(bus.read(addr+it)) }+"  "+(0..15).joinToString("") {
                        val chr = bus.read(addr+it).toInt().toChar()
                        if (chr.isLetterOrDigit()) chr.toString()
                        else "."
                    })
                }
                IVirtualMachine.MonitorCmdResult(result.joinToString("\n"), "", true)
            }
            'p' -> {
                val numbers = command.substring(1).trim().split(' ')
                val address = Assembler.parseNumber(numbers[0])
                val values = numbers.drop(1).map { Assembler.parseNumber(it) }
                values.forEachIndexed { index, i -> bus.write(address+index, i.toShort()) }
                IVirtualMachine.MonitorCmdResult("ok", "", true)
            }
            'i' -> {
                val addresses = command.substring(1).trim().split(' ')
                val start = Assembler.parseNumber(addresses[0])
                val end = if (addresses.size > 1) Assembler.parseNumber(addresses[1]) else start+1
                val result = mutableListOf<String>()
                for (addr in start until end step 64) {
                    result.add("i$${hexW(addr)}  "+(0..63).joinToString("") {
                        val chr = bus.read(addr+it).toInt().toChar()
                        if (chr.isLetterOrDigit()) chr.toString()
                        else "."
                    })
                }
                IVirtualMachine.MonitorCmdResult(result.joinToString("\n"), "", true)
            }
            '$' -> {
                val number = Assembler.parseNumber(command)
                val output = "$${hexW(number)}  #$number  %${number.toString(2)}"
                IVirtualMachine.MonitorCmdResult(output, "", true)
            }
            '#' -> {
                val number = Assembler.parseNumber(command)
                val output = "$${hexW(number)}  #$number  %${number.toString(2)}"
                IVirtualMachine.MonitorCmdResult(output, "", true)
            }
            '%' -> {
                val number = Assembler.parseNumber(command)
                val output = "$${hexW(number)}  #$number  %${number.toString(2)}"
                IVirtualMachine.MonitorCmdResult(output, "", true)
            }
            'g' -> {
                val address = Assembler.parseNumber(command.substring(1))
                cpu.regPC = address
                IVirtualMachine.MonitorCmdResult("", "", true)
            }
            'a' -> {
                val parts = command.substring(1).trim().split(' ')
                val address = if(parts.size>=2) Assembler.parseNumber(parts[0]) else 0
                val assembler = Assembler(cpu, bus.memoryComponentFor(address), address)
                val result = assembler.assemble(command.substring(1).trimStart())
                if(result.success) {
                    val memory = (result.startAddress..result.startAddress+result.numBytes).map { bus[it] }.toTypedArray()
                    val d = disassembler.disassembleOneInstruction(memory, 0, result.startAddress)
                    IVirtualMachine.MonitorCmdResult(d.first, "a$${hexW(result.startAddress+result.numBytes)} ", false)
                }
                else
                    IVirtualMachine.MonitorCmdResult(result.error, command, false)
            }
            'd' -> {
                val addresses = command.substring(1).trim().split(' ')
                val start = Assembler.parseNumber(addresses[0])
                val end = if (addresses.size > 1) Assembler.parseNumber(addresses[1]) else start
                val memory = (start .. max(0xffff, end+3)).map {bus[it]}.toTypedArray()
                val disassem = disassembler.disassemble(memory, 0 .. end-start, start)
                IVirtualMachine.MonitorCmdResult(disassem.first.joinToString("\n") { "d$it" }, "d$${hexW(disassem.second)}", false)
            }
            else -> {
                IVirtualMachine.MonitorCmdResult("?unknown command", "", true)
            }
        }
    }
}
