'*****************************
'  TEXT FRAMES LIBRARY CODE
'*****************************

SUB SetFont (WhichHandle AS INTEGER, FontHandle AS LONG)
    u = UBOUND(TextHandles)
    Handle = WhichHandle
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION
    TextHandles(Handle).Font = FontHandle
END SUB

SUB PrintOut (WhichHandle AS INTEGER, What AS STRING)
    u = UBOUND(TextHandles)
    Handle = WhichHandle
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION
    Where = TextHandles(Handle).VerticalAlignment
    How = TextHandles(Handle).Justification
    UpdatePrintPosition = TextHandles(Handle).UpdateMethod
    PlaceText Handle, Where, How, What, UpdatePrintPosition
END SUB


SUB PlaceText (WhichHandle AS INTEGER, Where AS INTEGER, How AS INTEGER, What AS STRING, UpdatePrintPosition AS INTEGER)
    'WhichHandle is the handle which designates which text area we want to use
    'Where is where we want it to go in that text area
    '  -- Online prints the text to the current print position line in that text area.
    '  -- CenterLine centers the text to the center of that text area.
    '  -- any other value will print to that line positon in that particular box.
    'How tells us how we want to place that text (LeftJustified, RightJustified,CenterJustified, or NoJustify)
    'What is the text that we want to print in our text area
    'UpdatePrintPosition lets us know if we need to move to a newline or stay on the same line.  (Think PRINT with a semicolon vs PRINT without a semicolon).
    Handle = WhichHandle: u = UBOUND(texthandles)
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION


    DIM FG AS _UNSIGNED LONG, BG AS _UNSIGNED LONG
    FG = _DEFAULTCOLOR: BG = _BACKGROUNDCOLOR
    D = _DEST: S = _SOURCE
    FrameLibraryY = CSRLIN: FrameLibraryX = POS(0)
    F = _FONT: _FONT TextHandles(Handle).Font


    fh = _FONTHEIGHT
    pw = _PRINTWIDTH(What)

    IF TextHandles(Handle).HideFrame THEN
        _DEST TextHandles(Handle).SavedBackground
        _SOURCE TextHandles(Handle).SavedBackground
    END IF
    h = TextHandles(Handle).h - 4
    w = TextHandles(Handle).w - 4

    SELECT CASE Where
        CASE BottomLine
            TextFrameY = h \ fh
        CASE OnLine
            TextFrameY = TextHandles(Handle).Ypos
            IF TextFrameY = 0 THEN TextFrameY = 1
        CASE CenterLine
            linesused = 0
            tpw = pw: tw = w: tWhat$ = What
            DO UNTIL tpw <= tw
                textallowed = WordBreak(LEFT$(tWhat$, w \ _FONTWIDTH))
                text$ = RTRIM$(LEFT$(tWhat$, textallowed))
                linesused = linesused + 1
                tWhat$ = MID$(tWhat$, textallowed + 1)
                tpw = _PRINTWIDTH(tWhat$)
            LOOP
            linesused = linesused + 1
            py = (h - linesused * fh) \ 2
            TextFrameY = py \ fh + 1
            IF TextFrameY < 1 THEN TextFrameY = 1
        CASE ELSE
            TextFrameY = Where
    END SELECT

    IF TextFrameY < 1 THEN ERROR 5: EXIT FUNCTION 'We don't print above the allocated text area.
    blend = _BLEND
    _DONTBLEND
    DO UNTIL TextFrameY * fh < h 'We need to scroll the text area up, if someone is trying to print below it.
        'first let's get a temp image handle for the existing area of the screen.
        x1 = TextHandles(Handle).x1 + 2
        y1 = TextHandles(Handle).y1 + 2
        x2 = TextHandles(Handle).x1 + w
        y2 = TextHandles(Handle).y1 + h
        nh = y2 - y1 + 1 - fh
        nw = x2 - x1 + 1
        tempimage = _NEWIMAGE(nw, nh, 32) 'Really, I should swap this to a routine to pick which screen mode the user is in, but I'll come back to that later.
        _PUTIMAGE , , tempimage, (x1, y1 + fh)-(x2, y2)
        DrawTextArea Handle
        _PUTIMAGE (x1, y1)-(x2, y2 - fh), tempimage
        TextFrameY = TextFrameY - 1
    LOOP
    IF blend THEN _BLEND

    COLOR TextHandles(Handle).TextColor, TextHandles(Handle).TextBackgroundColor

    SELECT CASE How
        CASE LeftJustify
            TextFrameX = 0
            IF pw > w THEN
                textallowed = WordBreak(LEFT$(What, w \ _FONTWIDTH))
                text$ = RTRIM$(LEFT$(What, textallowed))
                _PRINTSTRING (TextFrameX + 2 + TextHandles(Handle).x1, (TextFrameY - 1) * fh + TextHandles(Handle).y1 + 2), text$
                PlaceText Handle, TextFrameY + 1, LeftJustify, MID$(What, textallowed + 1), 0
            ELSE
                _PRINTSTRING (TextFrameX + 2 + TextHandles(Handle).x1, (TextFrameY - 1) * fh + TextHandles(Handle).y1 + 2), What
                finished = -1
            END IF
        CASE CenterJustify
            IF pw > w THEN
                textallowed = WordBreak(LEFT$(What, w \ _FONTWIDTH))
                text$ = RTRIM$(LEFT$(What, textallowed))
                TextFrameX = (w - _PRINTWIDTH(text$)) \ 2
                _PRINTSTRING (TextFrameX + 2 + TextHandles(Handle).x1, (TextFrameY - 1) * fh + TextHandles(Handle).y1 + 2), text$
                PlaceText Handle, TextFrameY + 1, CenterJustify, MID$(What, textallowed + 1), NoUpdate
            ELSE
                TextFrameX = (w - pw) \ 2
                _PRINTSTRING (TextFrameX + 2 + TextHandles(Handle).x1, (TextFrameY - 1) * fh + TextHandles(Handle).y1 + 2), What
                finished = -1
            END IF
        CASE RightJustify
            IF pw > w THEN
                textallowed = WordBreak(LEFT$(What, w \ _FONTWIDTH))
                text$ = RTRIM$(LEFT$(What, textallowed))
                TextFrameX = w - _PRINTWIDTH(text$)
                _PRINTSTRING (TextFrameX + 2 + TextHandles(Handle).x1, (TextFrameY - 1) * fh + TextHandles(Handle).y1 + 2), text$
                PlaceText Handle, TextFrameY + 1, RightJustify, MID$(What, textallowed + 1), 0
            ELSE
                TextFrameX = w - pw
                _PRINTSTRING (TextFrameX + 2 + TextHandles(Handle).x1, (TextFrameY - 1) * fh + TextHandles(Handle).y1 + 2), What
                finished = -1
            END IF
        CASE NoJustify
            TextFrameX = TextHandles(Handle).Xpos
            firstlinelimit = (w - TextFrameX) \ _FONTWIDTH 'the limit of characters on the first line
            IF LEN(What) > firstlinelimit THEN
                textallowed = WordBreak(LEFT$(What, firstlinelimit))
                text$ = RTRIM$(LEFT$(What, textallowed))
                _PRINTSTRING (TextFrameX + 2 + TextHandles(Handle).x1, (TextFrameY - 1) * fh + TextHandles(Handle).y1 + 2), text$
                PlaceText Handle, TextFrameY + 1, LeftJustify, MID$(What, textallowed + 1), 0 'After the first line we start printing over on the left, after a line break
            ELSE
                _PRINTSTRING (TextFrameX + 2 + TextHandles(Handle).x1, (TextFrameY - 1) * fh + TextHandles(Handle).y1 + 2), What
                finished = -1
            END IF
    END SELECT

    IF finished THEN
        SELECT CASE TextHandles(Handle).UpdateMethod
            CASE NoUpdate 'We don't update the position at all.
            CASE NoNewLine
                TextHandles(Handle).Xpos = TextFrameX + pw
                TextHandles(Handle).Ypos = TextFrameY
            CASE NewLine
                TextHandles(Handle).Ypos = TextFrameY + 1
                TextHandles(Handle).Xpos = 1
        END SELECT
        _DEST D: _SOURCE S
        COLOR FG, BG

    END IF
    _FONT F
    LOCATE FrameLibraryY, FrameLibraryX
END SUB

SUB SetTextForeground (Handle AS INTEGER, Foreground AS _UNSIGNED LONG)
    u = UBOUND(TextHandles)
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION
    TextHandles(Handle).TextColor = Foreground
END SUB


SUB SetTextBackground (Handle AS INTEGER, Background AS _UNSIGNED LONG)
    u = UBOUND(TextHandles)
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION
    TextHandles(Handle).TextBackgroundColor = Background
END SUB



SUB SetTextColor (Handle AS INTEGER, Foreground AS _UNSIGNED LONG, Background AS _UNSIGNED LONG)
    u = UBOUND(TextHandles)
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION
    TextHandles(Handle).TextColor = Foreground
    TextHandles(Handle).TextBackgroundColor = Background
END SUB


SUB SetPrintUpdate (Handle AS INTEGER, Method AS INTEGER)
    u = UBOUND(TextHandles)
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION
    IF Method < 0 OR Method > 2 THEN ERROR 5: EXIT FUNCTION
    TextHandles(Handle).UpdateMethod = Method
END SUB


SUB SetPrintPosition (Handle AS INTEGER, TextFrameX AS INTEGER, TextFrameY AS INTEGER)
    u = UBOUND(TextHandles)
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION
    SELECT CASE TextFrameY
        CASE BottomLine
            TextHandles(Handle).VerticalAlignment = -2
        CASE CenterLine
            TextHandles(Handle).VerticalAlignment = -1
        CASE ELSE
            TextHandles(Handle).VerticalAlignment = 0
    END SELECT
    IF TextFrameX < 1 AND TextFrameX > -4 THEN
        TextHandles(Handle).Justification = TextFrameX
    ELSE
        TextHandles(Handle).Xpos = TextFrameX
    END IF
    IF TextFrameY < 1 THEN EXIT SUB
    TextHandles(Handle).Ypos = TextFrameY
END SUB

SUB SetPrintStyleX (Handle AS INTEGER, TextFrameX AS INTEGER)
    u = UBOUND(TextHandles)
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION
    IF TextFrameX < 1 AND TextFrameX > -4 THEN
        TextHandles(Handle).Justification = TextFrameX
    ELSE
        TextHandles(Handle).Xpos = TextFrameX
    END IF
END SUB

SUB SetPrintStyleY (Handle AS INTEGER, TextFrameY AS INTEGER)
    u = UBOUND(TextHandles)
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION
    SELECT CASE TextFrameY
        CASE BottomLine
            TextHandles(Handle).VerticalAlignment = -2
        CASE CenterLine
            TextHandles(Handle).VerticalAlignment = -1
        CASE ELSE
            TextHandles(Handle).VerticalAlignment = 0
    END SELECT
    IF TextFrameY < 1 THEN EXIT SUB
    TextHandles(Handle).Ypos = TextFrameY
END SUB


FUNCTION GetPrintPositionY (Handle AS INTEGER)
    u = UBOUND(TextHandles)
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION
    GetPrintPositionY = TextHandles(Handle).Ypos
END FUNCTION

FUNCTION GetPrintPositionX (Handle AS INTEGER)
    u = UBOUND(TextHandles)
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION
    GetPrintPositionX = TextHandles(Handle).Xpos
END FUNCTION



FUNCTION WordBreak (text$)
    CONST Breaks = " ;,.?!-"
    FOR i = LEN(text$) TO 0 STEP -1
        IF INSTR(Breaks, MID$(text$, i, 1)) THEN EXIT FOR
        loopcount = loopcount + 1
    NEXT
    IF i = 0 THEN i = LEN(text$)
    WordBreak = i
END FUNCTION



SUB ClearTextArea (Handle AS INTEGER)
    u = UBOUND(TextHandles)
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).SavedBackground THEN
        w = TextHandles(Handle).w
        h = TextHandles(Handle).h
        x1 = TextHandles(Handle).ScreenX
        y1 = TextHandles(Handle).ScreenY
        x2 = x1 + w - 1
        y2 = y1 + h - 1
        blend = _BLEND
        _DONTBLEND
        _PUTIMAGE (x1, y1)-(x2, y2), TextHandles(Handle).SavedBackground
        IF blend THEN _BLEND
    END IF
    DrawTextArea Handle
END SUB



SUB DrawTextArea (Handle AS INTEGER)
    u = UBOUND(TextHandles)
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION
    w = TextHandles(Handle).w
    h = TextHandles(Handle).h
    x1 = TextHandles(Handle).ScreenX
    y1 = TextHandles(Handle).ScreenY
    x2 = x1 + w - 1
    y2 = y1 + h - 1

    LINE (x1, y1)-(x2, y2), TextHandles(Handle).BackColor, BF
    LINE (x1, y1)-(x2, y2), TextHandles(Handle).FrameColor, B
END SUB



SUB ColorTextArea (Handle AS INTEGER, FrameColor AS _UNSIGNED LONG, BackColor AS _UNSIGNED LONG)
    u = UBOUND(TextHandles)
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION
    TextHandles(Handle).FrameColor = FrameColor
    TextHandles(Handle).BackColor = BackColor
END SUB



FUNCTION NewTextArea% (tx1 AS INTEGER, ty1 AS INTEGER, tx2 AS INTEGER, ty2 AS INTEGER, SaveBackground AS INTEGER)
    x1 = tx1: y1 = ty1 'We pass temp variables to the function so we can swap values if needed without altering user variables
    x2 = tx2: y2 = ty2
    IF x1 > x2 THEN SWAP x1, x2
    IF y1 > y2 THEN SWAP y1, y2
    w = x2 - x1 + 1
    h = y2 - y1 + 1
    IF w = 0 AND h = 0 THEN ERROR 5: EXIT FUNCTION 'Illegal Function Call if the user tries to define an area with no size
    'Error checking for if the user sends coordinates which are off the screen
    IF x1 < 0 OR x2 > _WIDTH - 1 THEN ERROR 5: EXIT FUNCTION
    IF y1 < 0 OR y2 > _HEIGHT - 1 THEN ERROR 5: EXIT FUNCTION

    u = UBOUND(TextHandles)
    FOR i = 1 TO u 'First let's check to see if we have an open handle from where one was freed earlier
        IF TextHandles(i).InUse = False THEN Handle = i: EXIT FOR
    NEXT
    IF Handle = 0 THEN 'We didn't have an open spot, so we need to add one to our list
        Handle = u + 1
        REDIM _PRESERVE TextHandles(Handle) AS TextArea
    END IF
    TextHandles(Handle).x1 = x1
    TextHandles(Handle).y1 = y1
    TextHandles(Handle).w = w: TextHandles(Handle).h = h
    TextHandles(Handle).InUse = True
    TextHandles(Handle).Xpos = 0
    TextHandles(Handle).Ypos = 1
    TextHandles(Handle).UpdateMethod = NewLine
    TextHandles(Handle).TextColor = _RGB32(255, 255, 255) 'White text
    TextHandles(Handle).TextBackgroundColor = _RGB32(0, 0, 0) 'Black background
    TextHandles(Handle).Font = 16 'by default, use the QB64 standard font

    IF SaveBackground THEN
        imagehandle = _NEWIMAGE(w, h, 32)
        _PUTIMAGE , 0, imagehandle, (x1, y1)-(x2, y2)
        TextHandles(Handle).SavedBackground = imagehandle
    END IF
    TextHandles(Handle).ScreenX = x1
    TextHandles(Handle).ScreenY = y1

    NewTextArea% = Handle
END FUNCTION

SUB FreeTextArea (Handle AS INTEGER)
    IF Handle > 0 AND Handle <= UBOUND(TextHandles) THEN
        IF TextHandles(Handle).InUse THEN
            TextHandles(Handle).InUse = False
            IF TextHandles(Handle).SavedBackground THEN
                IF TextHandles(Handle).HideFrame = 0 THEN 'If the frame isn't hidden, then restore what's supposed to be beneath it
                    w = TextHandles(Handle).w
                    h = TextHandles(Handle).h
                    x1 = TextHandles(Handle).ScreenX
                    y1 = TextHandles(Handle).ScreenY
                    x2 = x1 + w - 1
                    y2 = y1 + h - 1
                    blend = _BLEND
                    _DONTBLEND
                    _PUTIMAGE (x1, y1)-(x2, y2), TextHandles(Handle).SavedBackground
                    IF blend THEN _BLEND
                END IF
                'Even if it is hidden though, if we're going to free that frame, we need to free the stored image held with it to reduce memory usage.
                _FREEIMAGE TextHandles(Handle).SavedBackground
            END IF
        ELSE
            ERROR 258 'Invalid handle if the user tries to free a handle which has already been freed.
        END IF
    ELSE
        ERROR 5 'Illegal function call if the user tries to free a handle that doesn't exist at all.
    END IF
END SUB

SUB HideFrame (Handle AS INTEGER)
    IF TextHandles(Handle).HideFrame = 0 THEN 'only if the frame isn't hidden, can we hide it.
        TextHandles(Handle).HideFrame = -1
        w = TextHandles(Handle).w
        h = TextHandles(Handle).h
        x1 = TextHandles(Handle).ScreenX
        y1 = TextHandles(Handle).ScreenY
        x2 = x1 + w - 1
        y2 = y1 + h - 1
        imagehandle = _NEWIMAGE(TextHandles(Handle).w, TextHandles(Handle).h, 32)
        _PUTIMAGE , 0, imagehandle, (x1, y1)-(x2, y2)
        IF TextHandles(Handle).SavedBackground THEN
            blend = _BLEND
            _DONTBLEND
            _PUTIMAGE (x1, y1)-(x2, y2), TextHandles(Handle).SavedBackground
            _FREEIMAGE TextHandles(Handle).SavedBackground
            IF blend THEN _BLEND
        END IF
        TextHandles(Handle).SavedBackground = imagehandle
        TextHandles(Handle).x1 = 0 'When the frames are hidden, we calculate our print position based off the hidden image
        TextHandles(Handle).y1 = 0 'So we'd start at point (0,0) as being top left
    END IF
END SUB

SUB RestoreFrame (Handle AS INTEGER)
    IF TextHandles(Handle).HideFrame THEN 'only if we have a hidden frame do we have to worry about restoring it
        TextHandles(Handle).HideFrame = 0
        w = TextHandles(Handle).w
        h = TextHandles(Handle).h
        x1 = TextHandles(Handle).ScreenX
        y1 = TextHandles(Handle).ScreenY
        x2 = x1 + w - 1
        y2 = y1 + h - 1
        imagehandle = _NEWIMAGE(TextHandles(Handle).w, TextHandles(Handle).h, 32)
        blend = _BLEND
        _DONTBLEND
        _PUTIMAGE , 0, imagehandle, (x1, y1)-(x2, y2)
        _PUTIMAGE (x1, y1)-(x2, y2), TextHandles(Handle).SavedBackground ', 0, (0, 0)-(w, h)
        _FREEIMAGE TextHandles(Handle).SavedBackground
        IF blend THEN _BLEND
        TextHandles(Handle).SavedBackground = imagehandle
        TextHandles(Handle).x1 = x1 'When the frames are frames are restored, we need to recalculate our print position
        TextHandles(Handle).y1 = y1 'as we're no longer going over the image cooridinates, but the screen location of the top left corner instead.
    END IF
END SUB

SUB MoveFrame (Handle AS INTEGER, x1 AS INTEGER, y1 AS INTEGER)
    'Only two coordinates here, so we'll be positioning our frames new movement by the top left corner.
    u = UBOUND(TextHandles)
    IF Handle < 1 OR Handle > u THEN ERROR 5: EXIT FUNCTION
    IF TextHandles(Handle).InUse = False THEN ERROR 5: EXIT FUNCTION
    HideFrame Handle
    TextHandles(Handle).ScreenX = x1
    TextHandles(Handle).ScreenY = y1
    RestoreFrame Handle
END SUB

