# Read 32-bit serial data from ISP_MISO pin.

@static if ENABLE_GPIOC
using PiGPIOC: gpioCfgClock, gpioInitialise, gpioDelay
using PiGPIOC: gpioSerialReadOpen, gpioSerialReadClose, gpioSerialRead
using PiGPIOC: PI_INIT_FAILED
end

const SERIAL_BAUD = 10000
const SERIAL_BITS = 32

const TAG_PRINT  = 0b000
const TAG_PRINTN = 0b100
const TAG_ERROR  = 0b111


@static if ENABLE_GPIOC
function read_open(avr)
    assert_reset(avr)
    err = gpioSerialReadOpen(avr.miso, SERIAL_BAUD, SERIAL_BITS);
    @assert(err == 0);
    gpioDelay(100000)
    release_reset(avr)
end

function Base.read(avr::AVRDude)
    v = [UInt32(0)]
    n = gpioSerialRead(avr.miso, v, sizeof(UInt32));
    @assert(n == 0 || n == sizeof(UInt32))
    return n == 0 ? nothing : v[1]
end

function read_close(avr)
    gpioSerialReadClose(avr.miso)
end
end


function source_line(avr, address)
    if haskey(avr.linecache, address)
        source_line, line_text = avr.linecache[address]
    else
        cmd = `avr-addr2line -e $(avr.bin_file) 0x$(string(address, base=16))`
        source_line = try chomp(read(cmd, String)) catch err "?" end
        file, line_no = split(source_line, ":")
        line_text = try
            [eachline(file)...][parse(Int, line_no)]
        catch err
            "$file:$line_no"
        end
        avr.linecache[address] = (source_line, line_text)
    end
    source_line, line_text
end

function decode(avr, v)

    tag = v[1] >> 29
    if !(tag in (TAG_ERROR, TAG_PRINT, TAG_PRINTN))
        error("Bad tag: $tag (v =$v)")
    end

    address = if tag == TAG_PRINTN
        (v[1] & 0x7FFF0000) >> 16
    else
        v[1] & 0xFFFF
    end
    address *= 2
    #@show address
    
    source_line, line_text = source_line(avr, address)

    if tag == TAG_ERROR
        return string("ERROR: ", line, " ", source_line, " ", address)
    elseif tag == TAG_PRINT
        m = match(r"\s*println\(\"([^\"]*)\".*", line)
        return m == nothing ?  line : m[1]
    elseif tag == TAG_PRINTN
        m = match(r"\s*printn\(\"([^\"]*)\".*", line)
        return m == nothing ? line : string(m[1], v[1] & 0xFFFF)
    end
    @assert false
end

function monitor(avr)

    read_open(avr)

    try 
        while !avt.stop_monitor[]
            v = read(avr)
            if v != nothing
                println(decode(avr, v))
            else
                sleep(0.005)
            end
        end
    finally
        read_close(avr)
    end
end


# C Interface for message tags.

const c_header = string(
    "// DO NOT EDIT! - Generated by $(basename(@__FILE__))\n\n",
    ("#define $t 0b$(string(eval(t), base=2))\n"
     for t in [:TAG_PRINT, :TAG_PRINTN, :TAG_ERROR])...)
