include "inc/cmdsys.plh"
include "inc/fileio.plh"
include "dhgr.tk/inc/dhgrlib.plh"
include "dhgr.tk/inc/dhgrutils.plh"

const page1m = $C054
const page1x = $C055
//
// Font header
//
struc t_font
  byte[] fnt_typelen
  char   fnt_name[16]
  byte   fnt_first
  byte   fnt_last
  byte   fnt_width
  byte   fnt_height
  var    fnt_glyptrs[]
end
//
// Variable pitch glyphs
//
struc t_glyph
  var  gly_left
  var  gly_top
  byte gly_width
  byte gly_height
  byte gly_adv
  byte gly_strike[]
end
//
// Current and default font pointers
//
var fontPtr, fontDefault, fontDefThin, fontDefSlim
//
// Function pointers
//
export asm dcgrStr(x, y, strptr)#2
        JMP     $2000
end
//
// Utility functions
//
export def dhgrAllocBl7Mem(w, h)#2
    word memblk, span

    span   = (w+13)/14 << 3
    memblk = heapalloc(span * h)
    return memblk, span
end
def bitMonoGlyphStr(x, y, strptr)#2
    byte first, last, width, height, size, i, ch
    var strikes

    first   = fontPtr->fnt_first
    last    = fontPtr->fnt_last
    width   = fontPtr->fnt_width
    height  = fontPtr->fnt_height
    strikes = fontPtr + fnt_glyptrs
    size    = (width + 7) / 8 * height
    for i = 1 to ^strptr
        ch = ^(strptr + i)
        if ch >= first and ch <= last
            dcgrBitmap(x, y - height, width, height, \
                       strikes + (^(strptr + i) - first) * size)
            x = x + width
        fin
    next
    return x, y
end
def pixPropGlyphStr(x, y, strptr)#2
    var glyptrs, glyph
    byte first, last, i, ch

    first   = fontPtr->fnt_first
    last    = fontPtr->fnt_last
    glyptrs = fontPtr + fnt_glyptrs
    x       = x * 4
    for i = 1 to ^strptr
        ch = ^(strptr + i)
        if ch >= first and ch <= last
            glyph  = glyptrs=>[ch - first]
            dcgrPixmap((x + glyph=>gly_left) / 4, \
                        y + glyph=>gly_top,       \
                        glyph->gly_width,         \
                        glyph->gly_height,        \
                        glyph + gly_strike)
            x = x + glyph->gly_adv
        fin
    next
    return x / 4, y
end
def bitPropGlyphStr(x, y, strptr)#2
    var glyptrs, glyph
    byte first, last, i, ch

    first   = fontPtr->fnt_first
    last    = fontPtr->fnt_last
    glyptrs = fontPtr + fnt_glyptrs
    for i = 1 to ^strptr
        ch = ^(strptr + i)
        if ch >= first and ch <= last
            glyph  = glyptrs=>[ch - first]
            dcgrBitmap(x + glyph=>gly_left, \
                       y + glyph=>gly_top,  \
                       glyph->gly_width,    \
                       glyph->gly_height,   \
                       glyph + gly_strike)
            x = x + glyph->gly_adv
        fin
    next
    return x, y
end
export def dcgrStrExtents(strptr)#2
    var glyptrs, xext, yext
    byte first, last, width, i, ch

    first   = fontPtr->fnt_first
    last    = fontPtr->fnt_last
    if ^fontPtr & FONT_PROP
        glyptrs = fontPtr + fnt_glyptrs
        xext    = -glyptrs=>[^(strptr + 1) - first]=>gly_left
        if ^strptr > 1
            for i = 1 to ^strptr-1
                ch = ^(strptr + i)
                if ch >= first and ch <= last
                    xext = xext + glyptrs=>[ch - first]->gly_adv
                fin
            next
        fin
        xext = xext + glyptrs=>[^(strptr + ^strptr) - first]->gly_width
        if ^fontPtr & FONT_AA
            xext = xext / 4
        fin
    else
        xext = fontPtr->fnt_width * ^strptr
    fin
    return xext, fontPtr->fnt_height
end
export def dcgrBoldStr(clr, x, y, strptr)#2
    byte h, v

    for v = y - 1 to y + 1
        for h = x - 1 to x + 1
            dcgrStr(h, v, strptr)
        next
    next
    dcgrColor(clr)
    return dcgrStr(x, y, strptr)
end
export def fontRead(filename)#1
    var glyph, strksize, left, top
    byte refnum, num, i, width, height, adv
    byte roundup, pixbyte
    var font_header, gly_ptr
    byte[64] sysfont

    font_header = NULL
    if filename and ^filename
        refnum = fileio:open(filename)
        if not refnum // Look for system font
            strcpy(@sysfont, cmdsys:syspath)
            strcat(@sysfont, "fonts/")
            strcat(@sysfont, filename)
            refnum = fileio:open(@sysfont)
            if not refnum
                strcpy(@sysfont, cmdsys:syspath)
                strcat(@sysfont, "fonts/apple2.fon")
                refnum = fileio:open(@sysfont)
            fin
        fin
        if refnum
            font_header = heapalloc(t_font)
            fileio:read(refnum, font_header, t_font)
            num = font_header->fnt_last - font_header->fnt_first
            if font_header->fnt_typelen & FONT_PROP
                gly_ptr = heapalloc(num * 2) // Allocate glyph ptrs right after
                if font_header->fnt_typelen & FONT_AA
                    roundup = 1
                    pixbyte = 2
                else
                    roundup = 7
                    pixbyte = 8
                fin
                for i = 1 to num
                    fileio:read(refnum, @left,   1); left = sext(left)
                    fileio:read(refnum, @top,    1); top  = sext(top)
                    fileio:read(refnum, @width,  1)
                    fileio:read(refnum, @height, 1)
                    fileio:read(refnum, @adv,    1)
                    strksize = (width + roundup) / pixbyte * height
                    if strksize
                        *gly_ptr = heapalloc(strksize + t_glyph)
                        glyph = *gly_ptr
                        if glyph and fileio:read(refnum, glyph + gly_strike, strksize) == strksize
                            glyph=>gly_left   = left
                            glyph=>gly_top    = top
                            glyph->gly_width  = width
                            glyph->gly_height = height
                            glyph->gly_adv    = adv
                        else
                            heaprelease(font_header)
                            font_header = NULL
                            break
                        fin
                    else
                        *gly_ptr = NULL
                    fin
                    gly_ptr = gly_ptr + 2
                next
            else // FONT_MONO
                strksize = (font_header->fnt_width + 7) / 8 * font_header->fnt_height * num
                glyph = heapalloc(strksize) // Allocate glyphs right after
                if not glyph or fileio:read(refnum, glyph, strksize) <> strksize
                    heaprelease(font_header)
                    font_header = NULL
                fin
            fin
            fileio:close(refnum)
        fin
    fin
    return font_header
end
export def dcgrFont(font)#1
    var rows, pWide, pThin, prevFont

    if font == FONT_DEFAULT // Will also capture NULL font pointers
        if not fontDefault
            fontDefault = fontRead("apple2.fon")
            if not fontDefault
                return fontPtr
            fin
        fin
        font = fontDefault
    elsif font == FONT_DEF_THIN // Thin version of Apple2 font
        if not fontDefThin
            fontDefThin = fontRead("apple2thin.fon")
            if not fontDefThin
                return fontPtr
            fin
        fin
        font = fontDefThin
    elsif font == FONT_DEF_SLIM // Proportional version of thin font
        if not fontDefSlim
            fontDefSlim = fontRead("a2slim8.fon")
            if not fontDefSlim
                return fontPtr
            fin
        fin
        font = fontDefSlim
    fin
    prevFont = fontPtr
    fontPtr  = font
    if fontPtr->fnt_typelen & FONT_PROP
        if fontPtr->fnt_typelen & FONT_AA
            dcgrStr:1 = @pixPropGlyphStr
        else
            dcgrStr:1 = @bitPropGlyphStr
        fin
    else // FONT_MONO
        dcgrStr:1 = @bitMonoGlyphStr
    fin
    return prevFont
end
def dcgrLoadDefFont(x, y, strptr)#2 // Default if no font selected
    dcgrFont(FONT_DEFAULT)
    if fontDefault // Ensure it got loaded
        return dcgrStr(x, y, strptr)
    fin
    return 0, 0
end
export def fontGlyphDim(ch)#2
    byte width, height, first

    width  = fontPtr->fnt_width
    height = fontPtr->fnt_height
    if fontPtr->fnt_typelen & FONT_PROP
        first = fontPtr->fnt_first
        if ch >= first and ch <= fontPtr->fnt_last
            width = fontPtr=>fnt_glyptrs=>[ch - first]->gly_adv / 4
        fin
    fin
    return width, height
end
export def fontType#1
    return ^fontPtr & $F0
end
export def dcgrRect(x, y, w, h)#0
    byte x2, y2, v

    x2 = x + w - 1
    y2 = y + h - 1
    for v = y to y2
        dcgrHLin(x, x2, v)
    next
end
export def dcgrGetPixMap(x, y, w, h)#1
    var pixmap, pixptr
    byte i, j

    pixmap = heapalloc((w + 1)/2 * h)
    pixptr = pixmap
    w--; h--
    for j = 0 to h
        for i = 0 to w step 2
            ^pixptr = dcgrGetPixel(i+x,j+y)|(dcgrGetPixel(i+x+1,j+y)<<4)
            pixptr++
        next
    next
    return pixmap
end
export def dcgrEncPixMap(w, h, pixmap, rlemap)#1
    var rleptr
    byte i, j, run, pixrun

    rleptr = rlemap
    w--; h--
    for j = 0 to h
        run = 0
        for i = 0 to w step 2
            if run and ^pixmap == pixrun
                run++
            else
                if run
                    *rleptr = run*2 | (pixrun << 8)
                    //puth(*rleptr); putc(',')
                    rleptr  = rleptr + 2
                fin
                run    = 1
                pixrun = ^pixmap
            fin
            pixmap++
        next
        *rleptr = run*2 | (pixrun << 8)
        //puth(*rleptr); putln
        rleptr  = rleptr + 2
    next
    return rleptr - rlemap
end
export def spriteRead(filestr)#5
    var sprptr, sprsize, xorg, yorg
    byte refnum, width, height

    width  = 0
    height = 0
    sprptr = 0
    refnum = fileio:open(filestr)
    if refnum
        fileio:read(refnum, @xorg,   1); xorg = sext(xorg)
        fileio:read(refnum, @yorg,   1); yorg = sext(yorg)
        fileio:read(refnum, @width,  1)
        fileio:read(refnum, @height, 1)
        if width <= 40 and height <= 48
            sprsize = (width + 1) / 2 * height
            sprptr = heapalloc(sprsize)
            if sprptr
                if fileio:read(refnum, sprptr,  sprsize) <> sprsize
                    heaprelease(sprptr)
                    sprptr = NULL
                fin
            fin
        fin
        fileio:close(refnum)
    fin
    return xorg, yorg, width, height, sprptr
end
export def spriteWrite(filestr, xorg, yorg, width, height, sprptr)#1
    var sprsize
    byte refnum, i, result

    sprsize = (width + 1) / 2 * height
    fileio:destroy(filestr)
    result = fileio:create(filestr, $06, $0000)
    if result == FILE_ERR_OK
        refnum = fileio:open(filestr)
        if refnum
            fileio:write(refnum, @xorg,   1)
            fileio:write(refnum, @yorg,   1)
            fileio:write(refnum, @width,  1)
            fileio:write(refnum, @height, 1)
            sprsize = (width + 1) / 2 * height
            fileio:write(refnum, sprptr,  sprsize)
            result = perr
            fileio:close(refnum)
        else
            result = perr
        fin
    fin
    return result
end
export def screenRead(filestr)#1
    byte refnum

    refnum = fileio:open(filestr)
    if refnum
        ^page1x // Read AUX bytes
        fileio:read(refnum, $2000, $2000)
        ^page1m // Read MAIN bytes
        fileio:read(refnum, $2000, $2000)
        fileio:close(refnum)
    fin
    return perr
end
export def screenWrite(filestr)#1
    byte refnum, result

    fileio:destroy(filestr)
    result = fileio:create(filestr, $06, $2000)
    if result == FILE_ERR_OK
        refnum = fileio:open(filestr)
        if refnum
            ^page1x // Write AUX bytes
            if fileio:write(refnum, $2000, $2000) == $2000
                ^page1m // Write MAIN bytes
                fileio:write(refnum, $2000, $2000)
            fin
            result = perr
            fileio:close(refnum)
        else
            result = perr
        fin
    fin
    return result
end

//
// Fixups
//
dcgrStr:1 = @dcgrLoadDefFont
//
// Keep module in memory
//
return modkeep
done
