' Text mode windowing system. Each window can display text file.
' Window content can be scrolled horizontally and vertically.
' Window can have arbitrary size and location on the screen.
'
' This program is free software: released under Creative Commons Zero (CC0) license
' by Svjatoslav Agejenko.
' Email: svjatoslav@svjatoslav.eu
' Homepage: http://www.svjatoslav.eu

' Changelog:
' 2003, Initial version
' 2024-2025, Improved program readability

DECLARE SUB demo ()
DECLARE FUNCTION GetLineFromWindow$ (windowNum%, lineNum%)
DECLARE SUB LoadFileIntoWindow (fileName$, windowNum%)
DECLARE SUB SendLineToWindow (windowNum%, lineNum%, newString$)
DECLARE FUNCTION GetFreeLineIndex% ()
DECLARE SUB RefreshAllWindows ()
DECLARE FUNCTION AddWindow% (x%, y%, widt%, haigh%, title$)
DECLARE SUB DrawBox (x%, y%, widt%, haigh%, edgeStyle$)
DEFINT A-Z
DECLARE SUB ShowWindow (windowNum)
DECLARE SUB InitializeSystem ()

' Global variables for text storage
DIM SHARED maxStorage%
maxStorage% = 5000
DIM SHARED textStorage$(1 TO maxStorage%)
DIM SHARED storagePointer%

' Window-related global arrays
DIM SHARED windowData(1 TO 10, 1 TO 1000)      ' Stores line indices for each window
DIM SHARED windowX(1 TO 10), windowY(1 TO 10)  ' Position of each window
DIM SHARED windowWidth(1 TO 10), windowHeight(1 TO 10)  ' Size of each window
DIM SHARED windowActiveStatus(1 TO 10)         ' Whether window is active
DIM SHARED windowTitle$(1 TO 10)               ' Title of each window

' Window scrolling/shifting
DIM SHARED windowShiftX(1 TO 10)      ' Horizontal shift
DIM SHARED windowShiftY(1 TO 10)      ' Vertical shift

DIM SHARED currentActiveWindow%       ' Currently active window for display

InitializeSystem

demo

FUNCTION AddWindow% (x%, y%, widt%, heigh%, title$)
    ' Adds a new window to the system

    ' Find an empty window slot
    FOR windowNum% = 1 TO 10
        IF windowActiveStatus(windowNum%) = 0 THEN
            foundWindow% = windowNum%
            GOTO FoundEmptySlot
        END IF
    NEXT windowNum%

FoundEmptySlot:
    ' Initialize the window properties
    windowActiveStatus(foundWindow%) = 1
    windowX(foundWindow%) = x%
    windowY(foundWindow%) = y%
    windowWidth(foundWindow%) = widt%
    windowHeight(foundWindow%) = heigh%
    windowTitle$(foundWindow%) = title$

    ' Return the window number
    AddWindow% = foundWindow%
END FUNCTION

SUB ClearWindowContent (windowNum%)
    ' Clears all content from a window

    FOR lineNum% = 1 TO 1000
        IF windowData(windowNum%, lineNum%) > 0 THEN
            textStorage$(windowData(windowNum%, lineNum%)) = ""
            windowData(windowNum%, lineNum%) = 0
        END IF
    NEXT lineNum%
END SUB

SUB demo
    ' Create three windows with different sizes and titles
    window1% = AddWindow%(1, 1, 30, 10, "window 1.")
    window2% = AddWindow%(1, 12, 80, 30, "second window")
    window3% = AddWindow%(31, 2, 30, 10, "last window")

    ' Load the same file into all windows
    LoadFileIntoWindow "wsystem.bas", window2%
    LoadFileIntoWindow "wsystem.bas", window1%
    LoadFileIntoWindow "wsystem.bas", window3%

AnimateWindows:
    ' Randomly select an active window to animate
    currentActiveWindow% = INT(RND * 3) + 1
    RefreshAllWindows

    ' Animate the windows by shifting their content
    FOR animationFrame% = 1 TO 100
        windowShiftX(currentActiveWindow%) = SIN(animationFrame% / 10) * 10 + 10
        windowShiftY(currentActiveWindow%) = animationFrame%
        ShowWindow currentActiveWindow%
        ' split-second delay
        SOUND 0, 1
        IF INKEY$ <> "" THEN SYSTEM
    NEXT animationFrame%

    GOTO AnimateWindows
END SUB

FUNCTION GetFreeLineIndex%
    ' Finds a free line in text storage

FindNextLine:
    IF storagePointer% > 1000 THEN
        storagePointer% = 1
    END IF

    IF textStorage$(storagePointer%) = "" THEN
        GetFreeLineIndex% = storagePointer%
        storagePointer% = storagePointer% + 1
    ELSE
        storagePointer% = storagePointer% + 1
        GOTO FindNextLine
    END IF
END FUNCTION

FUNCTION GetLineFromWindow$ (windowNum%, lineNum%)
    ' Retrieve a line from a window's memory

    IF windowData(windowNum%, lineNum%) = 0 THEN
        GetLineFromWindow$ = ""
    ELSE
        GetLineFromWindow$ = textStorage$(windowData(windowNum%, lineNum%))
    END IF
END FUNCTION

SUB LoadFileIntoWindow (fileName$, windowNum%)
    ' Load a file into a window's memory

    OPEN fileName$ FOR INPUT AS #1
    FOR lineNum% = 1 TO 1000
        IF EOF(1) <> 0 THEN
            GOTO FileLoaded
        END IF
        LINE INPUT #1, lineContent$
        SendLineToWindow windowNum%, lineNum%, lineContent$
    NEXT lineNum%

FileLoaded:
    CLOSE #1

    ' Fill the remaining lines with empty strings
    FOR blankLineNum% = lineNum% TO 1000
        SendLineToWindow windowNum%, blankLineNum%, ""
    NEXT blankLineNum%
END SUB

SUB RefreshAllWindows
    ' Redraw all active windows

    CLS
    FOR windowNum% = 1 TO 10
        IF windowActiveStatus(windowNum%) > 0 THEN
            ShowWindow windowNum%
        END IF
    NEXT windowNum%
END SUB

SUB SendLineToWindow (windowNum%, lineNum%, newString$)
    ' Stores a string in a window's memory

    lineContent$ = newString$

    ' Remove trailing spaces from the string
    IF lineContent$ = SPACE$(LEN(lineContent$)) THEN
        lineContent$ = ""
    END IF

    IF LEN(lineContent$) > 0 THEN
TrimRight:
        IF RIGHT$(lineContent$, 1) = " " THEN
            lineContent$ = LEFT$(lineContent$, LEN(lineContent$) - 1)
            GOTO TrimRight
        END IF
    END IF

    ' Update the window memory with the new string
    IF lineContent$ = "" THEN
        IF windowData(windowNum%, lineNum%) > 0 THEN
            textStorage$(windowData(windowNum%, lineNum%)) = "": windowData(windowNum%, lineNum%) = 0
        END IF
    ELSE
        IF windowData(windowNum%, lineNum%) = 0 THEN
            windowData(windowNum%, lineNum%) = GetFreeLineIndex%
        END IF
        textStorage$(windowData(windowNum%, lineNum%)) = lineContent$
    END IF
END SUB

SUB ShowWindow (windowNum%)
    ' Draws a window on the screen

    ' Determine background color based on active window
    IF windowNum% = currentActiveWindow% THEN
        bgColor% = 1
    ELSE
        bgColor% = 0
    END IF

    x% = windowX(windowNum%)
    y% = windowY(windowNum%)
    widt% = windowWidth(windowNum%)
    heigh% = windowHeight(windowNum%)
    title$ = windowTitle$(windowNum%)

    COLOR 11, bgColor%

    ' Create border components
    FOR borderSegment% = 1 TO widt% - 2
        topBottom$ = topBottom$ + CHR$(205)
    NEXT borderSegment%
    borderTop$ = CHR$(201) + topBottom$ + CHR$(187)
    borderBottom$ = CHR$(200) + topBottom$ + CHR$(188)

    ' Draw top border
    LOCATE y%, x%
    PRINT borderTop$

    ' Draw bottom border
    LOCATE y% + heigh% - 1, x%
    PRINT borderBottom$

    ' Draw window content
    FOR lineNum% = 1 TO heigh% - 2
        LOCATE y% + lineNum%, x%
        lineContent$ = GetLineFromWindow$(windowNum%, lineNum% + windowShiftY(windowNum%))
        lineContent$ = lineContent$ + SPACE$(300)
        lineContent$ = RIGHT$(lineContent$, LEN(lineContent$) - windowShiftX(windowNum%))
        lineContent$ = LEFT$(lineContent$, widt% - 2)
        PRINT CHR$(186) + lineContent$ + CHR$(186)
    NEXT lineNum%

    ' Draw window title
    titleX% = INT(x% + (widt% / 2) - (LEN(title$) / 2) - 2)
    LOCATE y%, titleX%
    PRINT "[ "
    titleX% = titleX% + 2

    COLOR 10
    LOCATE y%, titleX%
    PRINT title$

    titleX% = titleX% + LEN(title$)
    COLOR 11
    LOCATE y%, titleX%
    PRINT " ]"
    COLOR 7, 0
END SUB

SUB InitializeSystem
    ' Initialize the screen and shared memory

    WIDTH 80, 50
    VIEW PRINT 1 TO 50

    FOR storageIndex% = 1 TO maxStorage%
        textStorage$(storageIndex%) = ""
    NEXT storageIndex%

    storagePointer% = 1
END SUB