" Vim filetype plugin file
" Language:    Intel .hex files
" Maintainer:  Stefan Liebl
"
" Features:    Display hex-address in statusline
"              :HexGotoAddress
"              :HexStatusLineOff
"
" Source:      included in http://code.google.com/p/vimsuite

" Parse Intel Hex Line into Dictionary
function! s:HexParseLine(line)
    let Pattern = '^:\(..\)\(....\)\(..\)\(.*\)\(..\)$'
    let Length   = substitute(a:line, Pattern, '\1', '')
    let Address  = substitute(a:line, Pattern, '\2', '')
    let Type     = substitute(a:line, Pattern, '\3', '')
    let Data     = substitute(a:line, Pattern, '\4', '')
    let Checksum = substitute(a:line, Pattern, '\5', '')
    let LineDict = {
                \'Length': Length,
                \'Address': Address,
                \'Type': Type,
                \'Data': Data,
                \'Checksum': Checksum
                \}
    return LineDict
endfunction

" Get number of current byte of data
function! s:HexGetDataByte()
    let Pos = getpos('.')
    let Column = Pos[2]
    let FirstData = 10
    let LastData = len(getline(line('.'))) - 2
    if Column < FirstData
        let Column = FirstData
    endif
    if Column > LastData
        let Column = LastData
    endif
    let DataByte = eval('('.Column.'-'.FirstData.') / 2')
    return DataByte
endfunction

" Get Extended linear address
function! s:HexGetExtLinAddress()
    let AddressLineNumber = search('^:......04', 'bcnW')
    let AddressLine = getline(AddressLineNumber)
    let LineDict = s:HexParseLine(AddressLine)
    let ExtLinAddress = LineDict['Data']
    return printf('0x%s0000', ExtLinAddress)
endfunction

" Get Address of current line
function! s:HexGetLineAddress()
    let ExtLinAddress = s:HexGetExtLinAddress()

    let LineDict = s:HexParseLine(getline(line('.')))
    let AddressOffset = LineDict['Address']

    let LineAddress = eval(
                \  ' (  '.ExtLinAddress.')'
                \ .'+(0x'.AddressOffset.')'
                \ )
    return printf('0x%x', LineAddress)
endfunction

" Get Address of current cursor position
function! s:HexGetAddress()
    try
        let LineAddress       = s:HexGetLineAddress()
        let LineAddressOffset = s:HexGetDataByte()

        let Address = eval(
                    \  ' (  '.LineAddress.')'
                    \ .'+(  '.LineAddressOffset.')'
                    \ )
        let HexAddress = printf('0x%x', Address)
    catch
        let HexAddress = 'unknown'
    endtry
    return HexAddress
endfunction

" Split data string in List of byte strings
function! HexSplitData(DataString)
    let DataList = split(a:DataString, '..\zs')
    return DataList
endfunction

" Get ASCII representation of current data
function! s:HexGetAsciiLine()
    try
        let String = ''
        let LineDict = s:HexParseLine(getline(line('.')))
        let Data = LineDict['Data']
        let DataList = HexSplitData(Data)
        for Byte in DataList
            let ByteVal = eval('0x'.Byte)
            let String .= nr2char(ByteVal)
        endfor
    catch
        let String = '?'
    endtry
    return String
endfunction

" Get value of current data under cursor for a:Bytes
function! HexGetVal(Bytes)
    let StartByte = s:HexGetDataByte()
    let HexString = ''
    let LineDict = s:HexParseLine(getline(line('.')))
    let Data = LineDict['Data']
    let DataList = HexSplitData(Data)
    if (StartByte + a:Bytes) <= len(DataList)
        let ByteNum = 0
        while ByteNum < a:Bytes
            let HexString .= DataList[StartByte + ByteNum]
            let ByteNum += 1
        endwhile
        return eval('0x'.HexString)
    else
        return -1
    endif
endfunction

" Get actual values for 1, 2, 4 Bytes in hex and dez
function! s:HexGetDezValuesString()
    try
        let String = ''
        for i in [1, 2, 4]
            let Byte = HexGetVal(i)
            if Byte != -1
                let String .= ' ' . printf('0x%x (%d)', Byte, Byte)
            endif
        endfor
    catch
        let String = '?'
    endtry
    return String
endfunction

" Build string for statusline
function! HexStatusLine()
    let StatusLine =
                \   ' Address: '
                \ . s:HexGetAddress()
                \ . ' Values: '
                \ . s:HexGetDezValuesString()
                \ . ' Data: '
                \ . s:HexGetAsciiLine()
    return StatusLine
endfunction

function! s:HexAddressIsSmaller(a1, a2)
    let a1 = eval(a:a1)
    let a2 = eval(a:a2)
    if (a1 < 0) && (a2 >= 0)
        " a1 is greater
        return 0
    elseif (a1 >= 0) && (a2 < 0)
        " a2 is greater
        return 1
    else
        return a1 < a2
    endif
endfunction

command! -nargs=1 HexGotoAddress call HexGotoAddress("<args>")
function! HexGotoAddress(address)
    let target = a:address
    " Find correct section
    normal G
    while s:HexAddressIsSmaller(target, s:HexGetExtLinAddress())
        call search('^:......04', 'bcW')
        normal k
    endwhile

    " Find correct line
    while s:HexAddressIsSmaller(target, s:HexGetLineAddress())
        normal k
    endwhile

    " Find corret position
    normal $h
    while s:HexAddressIsSmaller(target, s:HexGetAddress())
        normal hh
    endwhile
endfunction

"command! HexAddress call Test()
"function! Test()
"    echo HexStatusLine()
"endfunction

command! HexStatusLine set statusline=%!HexStatusLine()
command! HexStatusLineOff set statusline=
" Always update statusline with HEX info
set statusline=%!HexStatusLine()
" Always show statusline
set laststatus=2