' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Meta:
'

OPTION _EXPLICIT
$RESIZE:ON
_TITLE "Soft Array Editor"
_DELAY .1
RANDOMIZE TIMER

REM $Include: 'SoftArrays.bi'

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Visual Nodes (optional)
'

TYPE Vector
    x AS DOUBLE
    y AS DOUBLE
END TYPE

' Visual node structure
TYPE VisualNodesStructure
    Reference AS LONG '    Points to a node.
    BoxCenter AS Vector
    BoxHeight AS DOUBLE
    BoxWidth AS DOUBLE
    CornerNE AS Vector
    CornerNW AS Vector
    CornerSE AS Vector
    CornerSW AS Vector
    AntennaN AS Vector
    AntennaS AS Vector
    AntennaE AS Vector
    AntennaW AS Vector
    Visible AS INTEGER
END TYPE

DIM MaxVisualNodess AS INTEGER
DIM SHARED VisualNodesCount AS INTEGER
MaxVisualNodess = 512
VisualNodesCount = 0

' Visual node storage
DIM SHARED VisualNodes(MaxVisualNodess) AS VisualNodesStructure

' Visual node extras
DIM SHARED SelectionIndex(5) AS INTEGER

DIM SHARED ScrollPosition AS Vector
DIM SHARED Origin AS Vector

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Pre-Main
'

DIM SHARED ScreenHandle AS LONG
DIM SHARED ScreenHandleTemp AS LONG
ScreenHandle = _NEWIMAGE(800, 600, 32)
SCREEN ScreenHandle
_DELAY .1
_SCREENMOVE _MIDDLE
COLOR _RGBA(255, 255, 255, 255)

Origin.x = -.33 * _WIDTH / 2
Origin.y = .9 * _HEIGHT / 2
ScrollPosition.x = 0
ScrollPosition.y = 0

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Main
'

CALL DemoArray3D

CALL DemoTree

CALL DemoTreeEdit

CALL DemoArithmetic

CALL DemoList

CALL DemoLambda

CALL DemoMerge

END


' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Demo cases:
'

SUB DemoMerge
    DIM TheName1 AS STRING
    DIM TheName2 AS STRING
    TheName1 = "top part"
    TheName2 = "bottom part"

    DIM a AS LONG
    DIM b AS LONG
    a = NewSoftArray(TheName1)
    a = LinkEast(a, NewTextNode("lambda"))
    b = LinkEast(a, NewIntegerNode(4))
    b = LinkSouth(b, NewIntegerNode(6))
    a = NewUnitArray("cow", NewIntegerNode(5))
    a = NewSoftArray(TheName2)
    a = LinkEast(a, NewTextNode("cos"))
    b = LinkEast(a, NewTextNode("*"))
    b = LinkEast(b, NewIntegerNode(3))
    b = LinkSouth(b, NewTextNode("[1]"))
    b = LinkSouth(b, CopyIntegerNode(Content("cow")))
    b = LinkSouth(b, NewTextNode("[2]"))

    CALL UserMain(1, 0, 0)

    a = SoftArrayID(TheName2)
    b = SoftArray(a).HeadNode
    SoftArrayRegister(a) = -1
    IdentityRegister(b) = -1

    a = Nodes(SoftArray(SoftArrayID(TheName1)).HeadNode).East
    b = Nodes(b).East
    Nodes(b).West = -1
    Nodes(b).North = a
    Nodes(a).South = b

    CALL UserMain(1, 1, 0)
    a = EvalAllSoftArrays(1)
    CALL UserMain(1, 0, 1)
END SUB

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

SUB DemoLambda
    DIM TheName AS STRING
    TheName = "Lambda Test"

    DIM a AS LONG
    DIM b AS LONG
    a = NewUnitArray("cow", NewIntegerNode(7))
    a = NewSoftArray(TheName)
    a = LinkEast(a, NewTextNode("lambda"))
    b = LinkEast(a, NewIntegerNode(4))
    b = LinkSouth(b, NewIntegerNode(6))
    a = LinkSouth(a, NewTextNode("cos"))
    b = LinkEast(a, NewTextNode("*"))
    b = LinkEast(b, NewIntegerNode(3))
    b = LinkSouth(b, NewTextNode("[1]"))
    b = LinkSouth(b, CopyIntegerNode(Content("cow")))
    b = LinkSouth(b, NewTextNode("[2]"))

    CALL UserMain(0, 1, 0)
    a = EvalAllSoftArrays(1)
    CALL UserMain(1, 0, 1)
END SUB

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

SUB DemoList
    DIM TheName AS STRING
    TheName = "List Test"

    DIM a AS LONG
    DIM b AS LONG
    a = NewSoftArray(TheName)
    a = LinkEast(a, NewTextNode("cos"))
    b = LinkEast(a, NewTextNode("three"))
    b = LinkSouth(b, NewTextNode("four"))
    b = LinkSouth(b, NewTextNode("five"))
    b = LinkSouth(b, NewTextNode("six"))
    b = LinkSouth(b, NewTextNode("seven"))
    a = LinkSouth(a, NewTextNode("cos"))
    b = LinkEast(a, NewIntegerNode(3))
    b = LinkSouth(b, NewIntegerNode(4))
    b = LinkSouth(b, NewIntegerNode(5))
    b = LinkSouth(b, NewIntegerNode(6))
    b = LinkSouth(b, NewIntegerNode(7))

    CALL UserMain(0, 1, 0)
    a = EvalAllSoftArrays(1)
    CALL UserMain(1, 0, 1)
END SUB

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

SUB DemoArithmetic
    DIM ListNames(2) AS STRING
    ListNames(1) = "Arithmetic Test1"
    ListNames(2) = "Arithmetic Test2"

    DIM a AS LONG
    DIM b AS LONG
    a = NewSoftArray(ListNames(1))
    a = LinkEast(a, NewTextNode("*"))
    b = LinkEast(a, NewIntegerNode(3))
    b = LinkSouth(b, NewIntegerNode(4))
    a = LinkSouth(b, NewTextNode("cos"))
    b = LinkEast(a, NewTextNode("+"))
    b = LinkEast(b, NewIntegerNode(4))
    b = LinkSouth(b, NewIntegerNode(7))
    b = LinkSouth(a, NewIntegerNode(2))

    CALL UserMain(0, 1, 0)
    a = EvalAllSoftArrays(1)
    CALL UserMain(1, 0, 0)

    a = NewSoftArray(ListNames(2))
    a = LinkEast(a, NewTextNode("/"))
    b = LinkEast(a, NewIntegerNode(3))
    a = LinkSouth(b, NewTextNode("cos"))
    b = LinkEast(a, NewTextNode("+"))
    b = LinkEast(b, NewIntegerNode(4))
    b = LinkSouth(b, NewDoubleNode(DoubleData(Nodes(Nodes(HeadId(ListNames(1))).East).Reference)))

    CALL UserMain(0, 1, 0)
    a = EvalAllSoftArrays(1)
    CALL UserMain(1, 0, 1)
END SUB

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

SUB DemoTreeEdit
    DIM TheName AS STRING
    TheName = "Tree Edit Test"

    DIM a AS LONG
    DIM b AS LONG
    DIM c AS LONG
    a = NewSoftArray(TheName)
    a = LinkEast(a, NewTextNode("QB64 Buddy"))
    a = LinkEast(a, NewTextNode("Handle"))
    b = LinkEast(a, NewTextNode("flukiluke"))
    a = LinkSouth(a, NewTextNode("Name"))
    b = LinkEast(a, NewTextNode("Luke C."))
    a = LinkSouth(a, NewTextNode("Country"))
    b = LinkEast(a, NewTextNode("Australia"))
    c = LinkEast(b, NewTextNode("Locality"))
    b = LinkEast(c, NewTextNode("Down Under"))
    a = LinkSouth(a, NewTextNode("Birthyear"))
    b = LinkEast(a, NewIntegerNode(1523))
    c = LinkSouth(b, NewTextNode("May???"))

    CALL UserMain(1, 0, 0)

    a = InsertEast(SeekText("Down Under", HeadId(TheName), 1), NewTextNode("Get it?"))
    a = InsertSouth(SeekText("QB64 Buddy", HeadId(TheName), 1), NewTextNode("QB64 Enemy"))
    a = EditIntegerReference(StepUsing(SeekText("Birthyear", HeadId(TheName), 1), "e"), 1855)
    a = DeleteNodes(SeekText("Name", HeadId(TheName), 1))

    ' Query tests
    'PRINT "Inserting `Get it?' into list..."
    'PRINT "Adding new entry to bottom of list..."
    'PRINT "Editing Birthyear..."
    'PRINT "Deleting Name..."
    'PRINT

    CALL UserMain(0, 1, 1)
END SUB

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

SUB DemoTree
    DIM TheName AS STRING
    TheName = "Tree of Friends"

    DIM a AS LONG
    DIM b AS LONG
    DIM c AS LONG
    DIM d AS LONG
    a = NewSoftArray(TheName)
    a = LinkEast(a, NewTextNode("QB64 Buddy")): d = a
    a = LinkEast(a, NewTextNode("Handle"))
    b = LinkEast(a, NewTextNode("SMcNeill"))
    a = LinkSouth(a, NewTextNode("Name"))
    b = LinkEast(a, NewTextNode("Steve SMcNeill"))
    a = LinkSouth(a, NewTextNode("Country"))
    b = LinkEast(a, NewTextNode("USA"))
    c = LinkEast(b, NewTextNode("Locality"))
    b = LinkEast(c, NewTextNode("Virginia"))
    a = LinkSouth(a, NewTextNode("Birthyear"))
    b = LinkEast(a, NewIntegerNode(1973))
    c = LinkSouth(b, NewTextNode("May?"))
    a = LinkSouth(d, NewTextNode("QB64 Buddy")): d = a
    a = LinkEast(a, NewTextNode("Handle"))
    b = LinkEast(a, NewTextNode("FellippeHeitor"))
    a = LinkSouth(a, NewTextNode("Name"))
    b = LinkEast(a, NewTextNode("Fellippe Heitor"))
    a = LinkSouth(a, NewTextNode("Country"))
    b = LinkEast(a, NewTextNode("Brazil"))
    c = LinkEast(b, NewTextNode("Locality"))
    b = LinkEast(c, NewTextNode("My <3"))
    c = LinkEast(b, NewTextNode("JK, it's ___."))
    a = LinkSouth(a, NewTextNode("Birthyear"))
    b = LinkEast(a, NewIntegerNode(1983))
    c = LinkSouth(b, NewTextNode("Sep?"))
    b = LinkSouth(c, NewTextNode("... or was it May?"))
    a = LinkSouth(d, NewTextNode("QB64 Buddy")): d = a
    a = LinkEast(a, NewTextNode("Handle"))
    b = LinkEast(a, NewTextNode("DanTurtle"))

    '' Query tests
    'PRINT "Height:";
    'PRINT SquareArrayHeight(SoftArrayID(TheName))
    'PRINT "Steve's locality: ";
    'PRINT Literal$(StepUsing(HeadId(TheName), "eesseee"))
    'PRINT "Fellippe's locality: ";
    'PRINT Literal$(StepUsing(HeadId(TheName), "esesseee"))
    'PRINT "Fellippe's birth month: ";
    'PRINT Literal$(StepUsing(JumpFrom(StepUsing(HeadId(TheName), "ese"), "s", 3), "es"))
    'PRINT "Width of Fellippe's Country branch:";
    'PRINT Measure(SeekText("Country", HeadId(TheName), 2), "e")
    'PRINT "Height under Fellippe's Birthyear branch:";
    'PRINT Measure(Nodes(SeekText("Birthyear", HeadId(TheName), 2)).East, "s")
    'PRINT

    CALL UserMain(0, 1, 1)
END SUB

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
SUB DemoArray3D
    DIM TheName AS STRING
    TheName = "Three-Dimensional Array"

    DIM i AS INTEGER
    DIM j AS INTEGER
    DIM k AS INTEGER
    DIM a AS LONG
    DIM b AS LONG

    DIM TestArray3D(4, 2, 3) AS STRING
    TestArray3D(1, 1, 1) = "one.one.one"
    TestArray3D(1, 1, 2) = "one.one.two"
    TestArray3D(1, 1, 3) = "one.one.three"
    TestArray3D(1, 2, 1) = "one.two.one"
    TestArray3D(1, 2, 2) = "one.two.two"
    TestArray3D(1, 2, 3) = "one.two.three"
    TestArray3D(2, 1, 1) = "two.one.one"
    TestArray3D(2, 1, 2) = "two.one.two"
    TestArray3D(2, 1, 3) = "two.one.three"
    TestArray3D(2, 2, 1) = "two.two.one"
    TestArray3D(2, 2, 2) = "two.two.two"
    TestArray3D(2, 2, 3) = "two.two.three"
    TestArray3D(3, 1, 1) = "three.one.one"
    TestArray3D(3, 1, 2) = "three.one.two"
    TestArray3D(3, 1, 3) = "three.one.three"
    TestArray3D(3, 2, 1) = "three.two.one"
    TestArray3D(3, 2, 2) = "three.two.two"
    TestArray3D(3, 2, 3) = "three.two.three"
    TestArray3D(4, 1, 1) = "four.one.one"
    TestArray3D(4, 1, 2) = "four.one.two"
    TestArray3D(4, 1, 3) = "four.one.three"
    TestArray3D(4, 2, 1) = "four.two.one"
    TestArray3D(4, 2, 2) = "four.two.two"
    TestArray3D(4, 2, 3) = "four.two.three"

    ' Load 3D array as Soft array
    a = NewSoftArray(TheName)
    FOR i = 1 TO UBOUND(TestArray3D, 1)
        FOR j = 1 TO UBOUND(TestArray3D, 2)
            FOR k = 1 TO UBOUND(TestArray3D, 3)
                IF ((i = 1) AND (j = 1) AND (k = 1)) THEN
                    a = LinkEast(a, NewTextNode(TestArray3D(i, j, k)))
                    b = a
                ELSE
                    IF (k = 1) THEN
                        a = LinkSouth(a, NewTextNode(TestArray3D(i, j, k)))
                        b = a
                    ELSE
                        b = LinkEast(b, NewTextNode(TestArray3D(i, j, k)))
                    END IF
                END IF
            NEXT
        NEXT
    NEXT

    'PRINT "Query tests:"
    'PRINT PrintSoftArray$(HeadId(TheName))
    'PRINT "Height:"; SquareArrayHeight(SoftArrayID(TheName))
    'PRINT "Width:"; SquareArrayWidth(SoftArrayID(TheName))
    'PRINT
    'PRINT "Typical FOR loop:"
    'FOR k = 1 TO Bottom(TheName)
    '    PRINT Literal$(JumpFrom(Content(TheName), "s", k))
    'NEXT
    'PRINT

    CALL UserMain(0, 1, 1)
END SUB

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Begin GUI/Graphical component (optional)
' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

SUB UserMain (q AS INTEGER, r AS INTEGER, s AS INTEGER)
    DIM a AS LONG
    DIM k AS INTEGER
    DIM t AS STRING

    IF (q = 1) THEN
        CLS
        PRINT PrintAllSoftArrays$(1)
        CALL Halt
    END IF

    IF (r = 1) THEN
        k = ClearSelections(1)
        CALL BuildAllVisualArrays
        CALL UserLoop
    END IF

    IF (s = 1) THEN
        a = DeleteAllSoftArrays(1)
        k = ClearSelections(1)
        ScrollPosition.x = 0
        ScrollPosition.y = 0
        t = MemoryProbe$("")
    END IF
END SUB

SUB UserLoop
    DIM x0 AS DOUBLE
    DIM y0 AS DOUBLE
    DIM xx AS DOUBLE
    DIM yy AS DOUBLE
    DIM a AS LONG
    DIM b AS LONG
    DIM c AS LONG
    DIM k AS INTEGER
    DIM i AS INTEGER
    DIM j AS INTEGER
    DIM kh AS INTEGER
    DIM AltWires AS INTEGER
    DIM CtrlKey AS INTEGER
    DIM MW AS INTEGER
    DIM MB1 AS INTEGER
    DIM t AS STRING

    DO WHILE _MOUSEINPUT: LOOP ' Clear mouse buffer.

    DO

        x0 = _MOUSEX
        y0 = _MOUSEY
        x0 = (x0 - _WIDTH / 2)
        y0 = (-y0 + _HEIGHT / 2)
        i = MouseOver(x0, y0)

        MB1 = 0

        DO WHILE _MOUSEINPUT
            xx = _MOUSEX
            yy = _MOUSEY

            xx = (xx - _WIDTH / 2)
            yy = (-yy + _HEIGHT / 2)

            IF ((_MOUSEBUTTON(1) = 0) AND (MB1 = 0)) THEN
                i = MouseOver(xx, yy)
                j = i
            END IF

            IF ((_MOUSEBUTTON(1) = -1) AND (MB1 = 0)) THEN
                i = MouseOver(xx, yy)
                j = i
                MB1 = -1
            END IF

            IF (MB1 = -1) THEN
                i = j
                IF (i <> -1) THEN
                    ScrollPosition.x = ScrollPosition.x + (xx - VisualNodes(i).BoxCenter.x)
                    ScrollPosition.y = ScrollPosition.y + (yy - VisualNodes(i).BoxCenter.y)
                    CALL MoveVisualNodesRecur(i, VisualNodes(i).Reference, xx - VisualNodes(i).BoxCenter.x, yy - VisualNodes(i).BoxCenter.y)
                    CALL NewSelection(i)
                END IF
            END IF

            IF ((_MOUSEBUTTON(1) = 0) AND (MB1 = -1)) THEN
                i = -1
                j = i
                MB1 = 0
                CALL BuildAllVisualArrays
            END IF

            IF (_MOUSEBUTTON(2) = -1) THEN
                ScrollPosition.x = ScrollPosition.x + (xx - x0)
                ScrollPosition.y = ScrollPosition.y + (yy - y0)
                CALL MoveAllVisualNodes(xx - x0, yy - y0)
                x0 = xx
                y0 = yy
            END IF

            MW = _MOUSEWHEEL
            IF (MW <> 0) THEN
                CALL MoveAllVisualNodes(0, 30 * MW)
                ScrollPosition.y = ScrollPosition.y + 30 * MW
            END IF

        LOOP

        IF (i <> -1) THEN
            IF (xx > VisualNodes(i).BoxCenter.x - VisualNodes(i).BoxWidth / 2) AND (xx < VisualNodes(i).BoxCenter.x + VisualNodes(i).BoxWidth / 2) THEN
                IF (yy > VisualNodes(i).BoxCenter.y - VisualNodes(i).BoxHeight / 2) AND (yy < VisualNodes(i).BoxCenter.y + VisualNodes(i).BoxHeight / 2) THEN
                    CALL NewSelection(i)
                END IF
            END IF
        END IF

        kh = _KEYHIT

        ' Press-and-hold keys
        IF (_KEYDOWN(100305)) OR (_KEYDOWN(100306)) THEN
            CtrlKey = 1
        ELSE
            CtrlKey = 0
        END IF

        IF (_KEYDOWN(ASC("w"))) OR (_KEYDOWN(ASC("W"))) THEN
            AltWires = 1
        ELSE
            AltWires = 0
        END IF

        ' State-changing.
        SELECT CASE kh
            CASE 13 ' enter
                IF (SelectionIndex(1) <> -1) THEN
                    a = Evaluate(VisualNodes(SelectionIndex(1)).Reference)
                    CALL BuildAllVisualArrays
                    i = ClearSelections(1)
                END IF
            CASE 27 ' esc
                EXIT SUB
            CASE 21248 ' delete
                IF (SelectionIndex(1) <> -1) THEN
                    a = DeleteNodes(VisualNodes(SelectionIndex(1)).Reference)
                    CALL BuildAllVisualArrays
                    i = ClearSelections(1)
                END IF
            CASE ASC("v"), ASC("V")
                IF (SelectionIndex(1) <> -1) THEN
                    a = SeverSoftArray(VisualNodes(SelectionIndex(1)).Reference)
                    CALL BuildAllVisualArrays
                END IF
            CASE ASC("c"), ASC("C")
                IF (SelectionIndex(1) <> -1) THEN
                    a = CopyNodes(VisualNodes(SelectionIndex(1)).Reference)
                    CALL BuildAllVisualArrays
                END IF
            CASE ASC("i"), ASC("I")
                IF (CtrlKey = 1) THEN
                    IF (SelectionIndex(1) <> -1) THEN
                        a = ConvertToInteger(VisualNodes(SelectionIndex(1)).Reference, 0)
                        CALL DefineVisualNode(SelectionIndex(1), VisualNodes(SelectionIndex(1)).Reference, VisualNodes(SelectionIndex(1)).BoxCenter.x, VisualNodes(SelectionIndex(1)).BoxCenter.y)
                    END IF
                ELSE
                    a = NewUnitArray("integer" + LTRIM$(RTRIM$(STR$(INT(RND * 10000)))), NewIntegerNode(0))
                    CALL BuildAllVisualArrays
                END IF
            CASE ASC("d"), ASC("D")
                IF (CtrlKey = 1) THEN
                    IF (SelectionIndex(1) <> -1) THEN
                        a = ConvertToDouble(VisualNodes(SelectionIndex(1)).Reference, 0)
                        CALL DefineVisualNode(SelectionIndex(1), VisualNodes(SelectionIndex(1)).Reference, VisualNodes(SelectionIndex(1)).BoxCenter.x, VisualNodes(SelectionIndex(1)).BoxCenter.y)
                    END IF
                ELSE
                    a = NewUnitArray("double" + LTRIM$(RTRIM$(STR$(INT(RND * 10000)))), NewDoubleNode(0))
                    CALL BuildAllVisualArrays
                END IF
            CASE ASC("t"), ASC("T")
                IF (CtrlKey = 1) THEN
                    IF (SelectionIndex(1) <> -1) THEN
                        a = ConvertToText(VisualNodes(SelectionIndex(1)).Reference, "text")
                        CALL DefineVisualNode(SelectionIndex(1), VisualNodes(SelectionIndex(1)).Reference, VisualNodes(SelectionIndex(1)).BoxCenter.x, VisualNodes(SelectionIndex(1)).BoxCenter.y)
                    END IF
                ELSE
                    a = NewUnitArray("text" + LTRIM$(RTRIM$(STR$(INT(RND * 10000)))), NewTextNode("."))
                    CALL BuildAllVisualArrays
                END IF
            CASE ASC("e"), ASC("E")
                IF (CtrlKey = 1) THEN
                    FOR k = 1 TO UBOUND(SoftArrayRegister)
                        IF (SoftArrayRegister(k) <> -1) THEN
                            IF (SoftArray(k).Label = Literal$(VisualNodes(SelectionIndex(1)).Reference)) THEN
                                a = SoftArray(k).HeadNode
                                b = Nodes(a).East
                                Nodes(b).West = -1
                                SoftArrayRegister(k) = -1
                                IdentityRegister(a) = -1
                                a = VisualNodes(SelectionIndex(2)).Reference
                                c = Nodes(a).East
                                IF (c <> -1) THEN
                                    c = DeleteNodes(c)
                                END IF
                                Nodes(a).East = b
                                Nodes(b).West = a
                                EXIT FOR
                            END IF
                        END IF
                    NEXT
                ELSE
                    IF (SelectionIndex(1) <> -1) THEN
                        SELECT CASE Nodes(VisualNodes(SelectionIndex(1)).Reference).Species
                            CASE "integer"
                                a = InsertEast(VisualNodes(SelectionIndex(1)).Reference, NewIntegerNode(0))
                            CASE "text"
                                a = InsertEast(VisualNodes(SelectionIndex(1)).Reference, NewTextNode("e"))
                            CASE "double"
                                a = InsertEast(VisualNodes(SelectionIndex(1)).Reference, NewDoubleNode(0))
                        END SELECT
                    END IF
                END IF
                CALL BuildAllVisualArrays
            CASE ASC("s"), ASC("S")
                IF (CtrlKey = 1) THEN
                    FOR k = 1 TO UBOUND(SoftArrayRegister)
                        IF (SoftArrayRegister(k) <> -1) THEN
                            IF (SoftArray(k).Label = Literal$(VisualNodes(SelectionIndex(1)).Reference)) THEN
                                a = SoftArray(k).HeadNode
                                b = Nodes(a).East
                                Nodes(b).West = -1
                                SoftArrayRegister(k) = -1
                                IdentityRegister(a) = -1
                                a = VisualNodes(SelectionIndex(2)).Reference
                                c = Nodes(a).South
                                IF (c <> -1) THEN
                                    c = DeleteNodesRecur(c)
                                END IF
                                Nodes(a).South = b
                                Nodes(b).North = a
                                EXIT FOR
                            END IF
                        END IF
                    NEXT
                ELSE
                    IF (SelectionIndex(1) <> -1) THEN
                        SELECT CASE Nodes(VisualNodes(SelectionIndex(1)).Reference).Species
                            CASE "integer"
                                a = InsertSouth(VisualNodes(SelectionIndex(1)).Reference, NewIntegerNode(0))
                            CASE "text"
                                a = InsertSouth(VisualNodes(SelectionIndex(1)).Reference, NewTextNode("s"))
                            CASE "double"
                                a = InsertSouth(VisualNodes(SelectionIndex(1)).Reference, NewDoubleNode(0))
                        END SELECT
                    END IF
                END IF
                CALL BuildAllVisualArrays
            CASE ASC("x"), ASC("X")
                IF (SelectionIndex(1) <> -1) THEN
                    _KEYCLEAR
                    LOCATE (_HEIGHT / 16) / 2, (_WIDTH / 8) / 2 - 8
                    PRINT "Enter new value:" '+ CHR$(10) + SPACE$(20);
                    LOCATE (_HEIGHT / 16) / 2 + 1, (_WIDTH / 8) / 2 - 8
                    LINE INPUT t
                    SELECT CASE Nodes(VisualNodes(SelectionIndex(1)).Reference).Species
                        CASE "integer"
                            Nodes(VisualNodes(SelectionIndex(1)).Reference).Reference = NewIntegerData(INT(VAL(t)))
                        CASE "text"
                            a = VisualNodes(SelectionIndex(1)).Reference
                            Nodes(a).Reference = NewTextData(t)
                            FOR k = 1 TO UBOUND(SoftArrayRegister)
                                IF (SoftArrayRegister(k) <> -1) THEN
                                    IF (SoftArray(k).HeadNode = a) THEN
                                        SoftArray(k).Label = t
                                    END IF
                                END IF
                            NEXT
                        CASE "double"
                            Nodes(VisualNodes(SelectionIndex(1)).Reference).Reference = NewDoubleData(VAL(t))
                    END SELECT
                    CALL DefineVisualNode(SelectionIndex(1), VisualNodes(SelectionIndex(1)).Reference, VisualNodes(SelectionIndex(1)).BoxCenter.x, VisualNodes(SelectionIndex(1)).BoxCenter.y)
                    CALL BuildAllVisualArrays
                END IF
        END SELECT

        ' Display-changing.
        SELECT CASE kh
            CASE 18688 ' pgup
                CALL MoveAllVisualNodes(0, -.9 * _HEIGHT / 2)
                ScrollPosition.y = ScrollPosition.y - .9 * _HEIGHT / 2
            CASE 20736 ' pgdn
                CALL MoveAllVisualNodes(0, .9 * _HEIGHT / 2)
                ScrollPosition.y = ScrollPosition.y + .9 * _HEIGHT / 2
            CASE ASC("m"), ASC("M")
                CLS
                PRINT MemoryProbe$("")
                CALL Halt
            CASE ASC("l"), ASC("L")
                IF (CtrlKey = 1) THEN
                    OPEN "SoftArrays-" + LTRIM$(RTRIM$(STR$(TIMER))) + ".txt" FOR OUTPUT AS #1
                    PRINT #1, PrintAllSoftArrays$(1)
                    CLOSE #1
                END IF
                CALL UserMain(1, 0, 0)
        END SELECT

        _KEYCLEAR

        IF (_RESIZE = -1) THEN
            IF ((_RESIZEWIDTH > 320) AND (_RESIZEHEIGHT > 240)) THEN
                _DELAY .01
                ScreenHandleTemp = ScreenHandle
                ScreenHandle = _NEWIMAGE(_RESIZEWIDTH, _RESIZEHEIGHT, 32)
                SCREEN ScreenHandle
                _FREEIMAGE ScreenHandleTemp
            END IF
        END IF

        CLS

        CALL DrawAllVisualNodes(AltWires)

        IF (i <> -1) THEN
            IF (VisualNodes(i).Reference <> -1) THEN
                CALL DrawSingleNode(i, _RGBA(255, 25, 50, 255), _RGBA(255, 255, 0, 255))
            END IF
        END IF

        COLOR _RGBA(200, 200, 200, 255)
        CALL lprintstring(1, 3, "Selection history:")
        FOR k = 1 TO UBOUND(SelectionIndex)
            IF (SelectionIndex(k) <> -1) THEN
                IF (VisualNodes(SelectionIndex(k)).Reference <> -1) THEN
                    CALL lprintstring(1, 3 + k, LTRIM$(RTRIM$(STR$(k))) + ") " + Literal$(VisualNodes(SelectionIndex(k)).Reference) + " (@ " + LTRIM$(RTRIM$(STR$(VisualNodes(SelectionIndex(k)).Reference))) + ")")
                END IF
            END IF
        NEXT

        COLOR _RGBA(200, 200, 200, 255)
        i = UBOUND(SelectionIndex) + 4
        i = i + 1: CALL lprintstring(1, i, "Display:")
        i = i + 1: CALL lprintstring(1, i, "  Mouse1 = scroll recur")
        i = i + 1: CALL lprintstring(1, i, "  Mouse2 = scroll all")
        i = i + 1: CALL lprintstring(1, i, "  W      = all wires")
        i = i + 1: CALL lprintstring(1, i, "Report:")
        i = i + 1: CALL lprintstring(1, i, "  L      = print all")
        i = i + 1: CALL lprintstring(1, i, "  Ctrl+L = write file")
        i = i + 1: CALL lprintstring(1, i, "  M      = mem probe")
        i = i + 1: CALL lprintstring(1, i, "Nodes:")
        i = i + 1: CALL lprintstring(1, i, "  E      = append east")
        i = i + 1: CALL lprintstring(1, i, "  S      = append south")
        i = i + 1: CALL lprintstring(1, i, "  X      = edit content")
        i = i + 1: CALL lprintstring(1, i, "  Ctrl+I = to integer")
        i = i + 1: CALL lprintstring(1, i, "  Ctrl+T = to text")
        i = i + 1: CALL lprintstring(1, i, "  Ctrl+D = to double")
        i = i + 1: CALL lprintstring(1, i, "Soft arrays:")
        i = i + 1: CALL lprintstring(1, i, "  I      = new integer")
        i = i + 1: CALL lprintstring(1, i, "  T      = new text")
        i = i + 1: CALL lprintstring(1, i, "  D      = new double")
        i = i + 1: CALL lprintstring(1, i, "  C      = copy recur")
        i = i + 1: CALL lprintstring(1, i, "  Enter  = evaluate")
        i = i + 1: CALL lprintstring(1, i, "  Delete = delete")
        i = i + 1: CALL lprintstring(1, i, "  V      = sever")
        i = i + 1: CALL lprintstring(1, i, "  Ctrl+S = ins (1)s(2)")
        i = i + 1: CALL lprintstring(1, i, "  Ctrl+E = ins (1)e(2)")

        CALL rprintstring(3, "Scroll position:")
        CALL rprintstring(4, "(" + LTRIM$(RTRIM$(STR$(ScrollPosition.x))) + "," + LTRIM$(RTRIM$(STR$(ScrollPosition.y))) + ")")

        COLOR _RGBA(255, 100, 255, 255)
        CALL cprintstring(_HEIGHT / 2, "--Soft Array Editor--")
        CALL cprintstring(-_HEIGHT / 2 + 16, "Press ESC to finish.")
        COLOR _RGBA(255, 255, 255, 255)

        _DISPLAY

        _LIMIT 30
    LOOP

END SUB

SUB Halt
    _DELAY .01
    _KEYCLEAR
    DO: LOOP WHILE _MOUSEINPUT
    COLOR _RGBA(255, 255, 100, 255)
    PRINT "Press any key..."
    COLOR _RGBA(255, 255, 255, 255)
    _DISPLAY
    DO: LOOP UNTIL INKEY$ <> ""
    CLS
    _DISPLAY
END SUB

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Visual Nodes
'

SUB DefineVisualNode (i AS INTEGER, x AS LONG, cx AS DOUBLE, cy AS DOUBLE)
    DIM h AS DOUBLE
    DIM w AS DOUBLE
    h = 22
    w = 12 + 8 * LEN(Literal$(x))
    VisualNodes(i).Reference = x
    VisualNodes(i).BoxCenter.x = cx
    VisualNodes(i).BoxCenter.y = cy
    VisualNodes(i).BoxHeight = h
    VisualNodes(i).BoxWidth = w
    VisualNodes(i).CornerNE.x = cx + .5 * w
    VisualNodes(i).CornerNE.y = cy + .5 * h
    VisualNodes(i).CornerNW.x = cx - .5 * w
    VisualNodes(i).CornerNW.y = cy + .5 * h
    VisualNodes(i).CornerSE.x = cx + .5 * w
    VisualNodes(i).CornerSE.y = cy - .5 * h
    VisualNodes(i).CornerSW.x = cx - .5 * w
    VisualNodes(i).CornerSW.y = cy - .5 * h
    VisualNodes(i).AntennaN.x = cx
    VisualNodes(i).AntennaN.y = cy + .5 * h + 3
    VisualNodes(i).AntennaS.x = cx
    VisualNodes(i).AntennaS.y = cy - .5 * h - 3
    VisualNodes(i).AntennaE.x = cx + .5 * w + 3
    VisualNodes(i).AntennaE.y = cy
    VisualNodes(i).AntennaW.x = cx - .5 * w - 3
    VisualNodes(i).AntennaW.y = cy
END SUB

FUNCTION VisualNodeIndexFromReference (x AS LONG)
    DIM TheReturn AS INTEGER
    DIM j AS INTEGER
    TheReturn = -1
    FOR j = 1 TO VisualNodesCount
        IF (VisualNodes(j).Reference = x) THEN
            TheReturn = j
            EXIT FOR
        END IF
    NEXT
    VisualNodeIndexFromReference = TheReturn
END FUNCTION

SUB BuildAllVisualArrays
    DIM k AS LONG
    DIM y0 AS DOUBLE
    VisualNodesCount = 0
    y0 = Origin.y + ScrollPosition.y
    FOR k = UBOUND(SoftArrayRegister) TO 1 STEP -1
        IF (SoftArrayRegister(k) <> -1) THEN
            CALL BuildVisualArray(SoftArray(k).HeadNode, Origin.x + ScrollPosition.x, y0)
            y0 = y0 - 30
        END IF
    NEXT
END SUB

SUB BuildVisualArray (x AS LONG, x0 AS DOUBLE, y0 AS DOUBLE)
    DIM s AS LONG
    DIM e AS LONG
    DIM dxtmp AS DOUBLE
    s = Nodes(x).South
    e = Nodes(x).East
    VisualNodesCount = VisualNodesCount + 1
    CALL DefineVisualNode(VisualNodesCount, x, x0, y0)
    y0 = y0 - 30
    IF (e <> -1) THEN
        dxtmp = 8 * .5 * (LEN(Literal$(x)) + LEN(Literal$(e)))
        IF (dxtmp < 30) THEN dxtmp = 30
        CALL BuildVisualArray(e, x0 + dxtmp, y0)
    END IF
    IF (s <> -1) THEN
        CALL BuildVisualArray(s, x0, y0)
    END IF
END SUB

SUB MoveAllVisualNodes (dx AS DOUBLE, dy AS DOUBLE)
    DIM k AS INTEGER
    FOR k = 1 TO VisualNodesCount
        CALL MoveSingleVisualNode(k, dx, dy)
    NEXT
END SUB

SUB MoveVisualNodesRecur (i AS INTEGER, x AS LONG, dx AS DOUBLE, dy AS DOUBLE)
    DIM s AS LONG
    DIM e AS LONG
    DIM k AS INTEGER
    DIM f AS INTEGER
    s = Nodes(x).South
    e = Nodes(x).East
    IF (e <> -1) THEN
        f = 0
        FOR k = 1 TO VisualNodesCount
            IF (VisualNodes(k).Reference = e) THEN
                f = 1
                EXIT FOR
            END IF
        NEXT
        IF (f = 1) THEN
            CALL MoveVisualNodesRecur(k, e, dx, dy)
        END IF
    END IF
    IF (s <> -1) THEN
        f = 0
        FOR k = 1 TO VisualNodesCount
            IF (VisualNodes(k).Reference = s) THEN
                f = 1
                EXIT FOR
            END IF
        NEXT
        IF (f = 1) THEN
            CALL MoveVisualNodesRecur(k, s, dx, dy)
        END IF
    END IF
    CALL MoveSingleVisualNode(i, dx, dy)
END SUB

SUB MoveSingleVisualNode (i AS INTEGER, dx AS DOUBLE, dy AS DOUBLE)
    CALL DefineVisualNode(i, VisualNodes(i).Reference, VisualNodes(i).BoxCenter.x + dx, VisualNodes(i).BoxCenter.y + dy)
END SUB

SUB DrawAllVisualNodes (i AS INTEGER)
    DIM k AS INTEGER
    FOR k = 1 TO VisualNodesCount
        CALL DrawWiresSE(k)
        IF (i = 1) THEN
            CALL DrawWiresNW(k)
        END IF
        SELECT CASE Nodes(VisualNodes(k).Reference).Species
            CASE "integer"
                CALL DrawSingleNode(k, _RGBA(255, 255, 255, 255), _RGBA(155, 55, 55, 255))
            CASE "text"
                CALL DrawSingleNode(k, _RGBA(255, 255, 255, 255), _RGBA(55, 155, 55, 255))
            CASE "double"
                CALL DrawSingleNode(k, _RGBA(255, 255, 255, 255), _RGBA(55, 55, 155, 255))
            CASE ELSE
                CALL DrawSingleNode(k, _RGBA(255, 255, 255, 255), _RGBA(55, 55, 55, 255))
        END SELECT
    NEXT
END SUB

SUB DrawSingleNode (x AS LONG, c1 AS _UNSIGNED LONG, c2 AS _UNSIGNED LONG)
    CALL clinebf(VisualNodes(x).CornerNE.x, VisualNodes(x).CornerNE.y, VisualNodes(x).CornerSW.x, VisualNodes(x).CornerSW.y, c2)
    COLOR c1, c2
    CALL bprintstring(VisualNodes(x).CornerNW.x + 6, VisualNodes(x).CornerNW.y - 4, Literal$(VisualNodes(x).Reference))
    CALL clineb(VisualNodes(x).CornerNE.x + 0, VisualNodes(x).CornerNE.y + 0, VisualNodes(x).CornerSW.x - 0, VisualNodes(x).CornerSW.y - 0, _RGB32(155, 155, 155, 255))
    COLOR _RGBA(255, 255, 255, 255), 0
END SUB

SUB DrawWiresSE (x AS INTEGER)
    DIM i AS LONG
    DIM k AS INTEGER
    DIM s AS LONG
    DIM e AS LONG
    i = VisualNodes(x).Reference
    s = Nodes(i).South
    e = Nodes(i).East
    IF (s <> -1) THEN
        k = VisualNodeIndexFromReference(s)
        CALL cline(VisualNodes(x).AntennaS.x, VisualNodes(x).AntennaS.y, VisualNodes(k).BoxCenter.x, VisualNodes(k).BoxCenter.y, _RGBA(155, 155, 155, 255))
        CALL ccircle(VisualNodes(x).AntennaS.x, VisualNodes(x).AntennaS.y, 3, _RGBA(155, 155, 155, 255))
    END IF
    IF (e <> -1) THEN
        k = VisualNodeIndexFromReference(e)
        CALL cline(VisualNodes(x).AntennaE.x, VisualNodes(x).AntennaE.y, VisualNodes(k).BoxCenter.x, VisualNodes(k).BoxCenter.y, _RGBA(155, 155, 155, 255))
        CALL ccircle(VisualNodes(x).AntennaE.x, VisualNodes(x).AntennaE.y, 3, _RGBA(155, 155, 155, 255))
    END IF
END SUB

SUB DrawWiresNW (x AS INTEGER)
    DIM i AS LONG
    DIM k AS INTEGER
    DIM n AS LONG
    DIM w AS LONG
    i = VisualNodes(x).Reference
    n = Nodes(i).North
    w = Nodes(i).West
    IF (n <> -1) THEN
        k = VisualNodeIndexFromReference(n)
        CALL cline(VisualNodes(x).AntennaN.x, VisualNodes(x).AntennaN.y, VisualNodes(k).BoxCenter.x, VisualNodes(k).BoxCenter.y, _RGBA(255, 55, 55, 255))
        CALL ccircle(VisualNodes(x).AntennaN.x, VisualNodes(x).AntennaN.y, 3, _RGBA(255, 55, 55, 255))
    END IF
    IF (w <> -1) THEN
        k = VisualNodeIndexFromReference(w)
        CALL cline(VisualNodes(x).AntennaW.x, VisualNodes(x).AntennaW.y, VisualNodes(k).BoxCenter.x, VisualNodes(k).BoxCenter.y, _RGBA(255, 55, 55, 255))
        CALL ccircle(VisualNodes(x).AntennaW.x, VisualNodes(x).AntennaW.y, 3, _RGBA(255, 55, 55, 255))
    END IF
END SUB

FUNCTION MouseOver (x0 AS DOUBLE, y0 AS DOUBLE)
    DIM TheReturn AS INTEGER
    DIM k AS INTEGER
    TheReturn = -1
    FOR k = 1 TO VisualNodesCount
        IF (x0 > VisualNodes(k).BoxCenter.x - VisualNodes(k).BoxWidth / 2) AND (x0 < VisualNodes(k).BoxCenter.x + VisualNodes(k).BoxWidth / 2) THEN
            IF (y0 > VisualNodes(k).BoxCenter.y - VisualNodes(k).BoxHeight / 2) AND (y0 < VisualNodes(k).BoxCenter.y + VisualNodes(k).BoxHeight / 2) THEN
                TheReturn = k
                EXIT FOR
            END IF
        END IF
    NEXT
    MouseOver = TheReturn
END FUNCTION

SUB NewSelection (x AS INTEGER)
    DIM k AS INTEGER
    IF (SelectionIndex(1) <> x) THEN
        FOR k = UBOUND(SelectionIndex) TO 2 STEP -1
            SelectionIndex(k) = SelectionIndex(k - 1)
        NEXT
        SelectionIndex(1) = x
    END IF
END SUB

FUNCTION ClearSelections (x AS INTEGER)
    DIM TheReturn AS INTEGER
    DIM k AS INTEGER
    FOR k = x TO UBOUND(SelectionIndex)
        SelectionIndex(k) = -1
    NEXT
    TheReturn = -1
    ClearSelections = TheReturn
END SUB

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Cartesian graphics
'

SUB cline (x1 AS DOUBLE, y1 AS DOUBLE, x2 AS DOUBLE, y2 AS DOUBLE, col AS _UNSIGNED LONG)
    LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col
END SUB

SUB clineb (x1 AS DOUBLE, y1 AS DOUBLE, x2 AS DOUBLE, y2 AS DOUBLE, col AS _UNSIGNED LONG)
    LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col, B
END SUB

SUB clinebf (x1 AS DOUBLE, y1 AS DOUBLE, x2 AS DOUBLE, y2 AS DOUBLE, col AS _UNSIGNED LONG)
    LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col, BF
END SUB

SUB ccircle (x1 AS DOUBLE, y1 AS DOUBLE, rad AS DOUBLE, col AS _UNSIGNED LONG)
    CIRCLE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), rad, col
END SUB

SUB cpset (x1 AS DOUBLE, y1 AS DOUBLE, col AS _UNSIGNED LONG)
    PSET (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col
END SUB

SUB cpaint (x1 AS DOUBLE, y1 AS DOUBLE, col1 AS _UNSIGNED LONG, col2 AS _UNSIGNED LONG)
    PAINT (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), col1, col2
END SUB

SUB bprintstring (x1 AS DOUBLE, y1 AS DOUBLE, a AS STRING)
    _PRINTSTRING (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), a
END SUB

SUB cprintstring (y1 AS DOUBLE, a AS STRING)
    _PRINTSTRING (_WIDTH / 2 - (LEN(a) * 8) / 2, -y1 + _HEIGHT / 2), a
END SUB

SUB lprintstring (x1 AS DOUBLE, y1 AS DOUBLE, a AS STRING)
    'LOCATE y1, x1: PRINT a
    _PRINTSTRING ((x1 - 1) * 8, (y1 - 1) * 16), a
END SUB

SUB rprintstring (y1 AS DOUBLE, a AS STRING)
    _PRINTSTRING (_WIDTH - (LEN(a) * 8), (y1 - 1) * 16), a
END SUB

' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' End GUI/Graphical component (optional)
' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

REM $Include: 'SoftArrays.bm'
