#@name tetris
@inputs 
@outputs State:string NextL:vector2
@persist [DEF_Game DEF_Colors DEF_Pieces]:table [HOLO_Used HOLO_Unused]:array
@persist [CUR_Game CUR_Piece]:table CUR_Piece_Loc:vector2 CUR_Piece_Col:vector GameOver IDX
@trigger none

if (first()) {
    
    
    #------------------------
    #-- GAME: Default
    DEF_Game = table(
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
    )

    
    #------------------------
    #-- PIECE: Colors
    DEF_Colors = table(
        0 = hsv2rgb(  0, 0, 1), #-- white
        1 = hsv2rgb(180, 1, 1), #-- i, cyan
        2 = hsv2rgb(240, 1, 1), #-- j, blue
        3 = hsv2rgb( 30, 1, 1), #-- l, orange
        4 = hsv2rgb( 60, 1, 1), #-- o, yellow
        5 = hsv2rgb(120, 1, 1), #-- s, green
        6 = hsv2rgb(300, 1, 1), #-- t, magenta
        7 = hsv2rgb(360, 1, 1)  #-- z, red
    )


    #------------------------
    #-- PIECE: Shapes 
    DEF_Pieces = table(
        1 = table( #-- i
            array(0, 0, 0, 0),
            array(1, 1, 1, 1),
            array(0, 0, 0, 0),
            array(0, 0, 0, 0)
        ),
        2 = table( #-- j
            array(1, 0, 0),
            array(1, 1, 1),
            array(0, 0, 0)    
        ),
        3 = table( #-- l
            array(0, 0, 1),
            array(1, 1, 1),
            array(0, 0, 0)
        ),
        4 = table( #-- o
            array(1, 1),
            array(1, 1)
        ),
        5 = table( #-- s
            array(0, 1, 1),
            array(1, 1, 0),
            array(0, 0, 0)
        ),
        6 = table( #-- t
            array(0, 1, 0),
            array(1, 1, 1),
            array(0, 0, 0)
        ),
        7 = table( #-- z
            array(1, 1, 0),
            array(0, 1, 1),
            array(0, 0, 0)
        )
    )


    #------------------------
    #-- PIECE: Rotate    
    function table rotatePiece( Piece:table, Left ) {
        local Len = Piece:count()
        local New = table()
            
        for (I = 1, Len) {
            for (J = 1, Len) {
                New[J, array] = New[J, array] ?: array()
    
                if (Left) {
                    New[J, array][I, number] = Piece[I, array][Len - J + 1, number]
                }
                else {
                    New[J, array][I, number] = Piece[Len - I + 1, array][J, number]                
                }
            }
        }
        
        return New
    }


    #------------------------
    #-- PIECE: Spawn
    function spawnPiece() {
        local Color = 6#randint(1, 7)
        local Piece = DEF_Pieces[Color, table]:clone()
        
        local Len = Piece:count()
        for (I = 1, Len) {
            for (J = 1, Len) {
                Piece[I, array][J, number] = Piece[I, array][J, number] * Color
            }
        }
        
        CUR_Piece = Piece
        CUR_Piece_Loc = vec2(0, 1)
        CUR_Piece_Col = DEF_Colors[Color, vector]
    }


    #------------------------
    #-- GAME: Reset
    function resetGame() {
        CUR_Game = DEF_Game:clone()    
    }
    
    
    #------------------------
    #-- GAME: Print 
    function printGame( G:table ) {

        printColor(vec(255, 255, 255), "      Tetris")
        for (R = 1, 20) {
            local A = G[R, array]
            printColor(
                DEF_Colors[A[1, number], vector],  A[1, number]  + " ",
                DEF_Colors[A[2, number], vector],  A[2, number]  + " ",
                DEF_Colors[A[3, number], vector],  A[3, number]  + " ",
                DEF_Colors[A[4, number], vector],  A[4, number]  + " ",
                DEF_Colors[A[5, number], vector],  A[5, number]  + " ",
                DEF_Colors[A[6, number], vector],  A[6, number]  + " ",
                DEF_Colors[A[7, number], vector],  A[7, number]  + " ",
                DEF_Colors[A[8, number], vector],  A[8, number]  + " ",
                DEF_Colors[A[9, number], vector],  A[9, number]  + " ",
                DEF_Colors[A[10, number], vector], A[10, number] + " "
            ) 
        }
    }
    
    
    #------------------------
    #-- GAME: Can place piece
    function string canPlacePiece(G:table, Piece:table, Y, X) {
        local Len = Piece:count()
        
        for (PY = 1, Len) {
            for (PX = 1, Len) {
                if (Piece[PY, array][PX, number]) {
                    local NextY = Y + PY
                    local NextX = X + PX
                    
                    if (NextX < 0 | NextX > 10) {
                        return "_offscreen"
                    }    
                    if (NextY > 20 | G[NextY, array][NextX, number]) {
                        return "_blocked"
                    }
                }
            }
        }

        return "_canplace"
    }    


    #------------------------
    #-- GAME: Place piece
    function placePiece(G:table, Piece:table, Y, X) {
        local Len = Piece:count()
        
        for (PY = 1, Len) {
            for (PX = 1, Len) {
                if (Piece[PY, array][PX, number]) {
                    local NextY = Y + PY
                    local NextX = X + PX
                    
                    G[NextY, array][NextX, number] = Piece[PY, array][PX, number]    
                }        
            }
        }

        #-- remove lines and update score  
    }


    #------------------------
    #-- Testing
    resetGame()
    spawnPiece()

    for (I = 1, 4) {
        IDX++
        
        holoCreate(IDX)
        holoParent(IDX, entity())   
        holoPos(IDX, entity():toWorld(vec(CUR_Piece_Loc)*6)) 
        holoColor(IDX, CUR_Piece_Col)
        holoModel(IDX, "rcube")
        holoMaterial(IDX, "models/shiny")
        holoScale(IDX, vec(2, 5.5, 5.5)/12)
    }
}


interval(100)

if (!GameOver) {
    Dir   = vec2(1, 1)
    NextL = CUR_Piece_Loc + Dir #clamp(CUR_Piece_Loc + vec2(1, 0), vec2(), vec2(20, 10)) # ROW, COL
    State = canPlacePiece(CUR_Game, CUR_Piece, NextL[1], NextL[2])
    
    while (State == "_offscreen") {
        NextL += vec2(0, -Dir[2])
        State = canPlacePiece(CUR_Game, CUR_Piece, NextL[1], NextL[2]) 
    }
 
    if (State != "_canplace") {
        placePiece(CUR_Game, CUR_Piece, CUR_Piece_Loc[1], CUR_Piece_Loc[2])
        spawnPiece()
        
        for (I = 1, 4) {
            IDX++
            
            holoCreate(IDX)
            holoParent(IDX, entity())   
            holoPos(IDX, entity():toWorld(vec(0, CUR_Piece_Loc[2]*6, -CUR_Piece_Loc[1]*6)))
            holoColor(IDX, CUR_Piece_Col)
            holoModel(IDX, "rcube")
            holoMaterial(IDX, "models/shiny")
            holoScale(IDX, vec(2, 5.5, 5.5)/12)
        }

        printGame(CUR_Game)
        
        local State = canPlacePiece(CUR_Game, CUR_Piece, CUR_Piece_Loc[1], CUR_Piece_Loc[2])
        if (State == "_blocked") {
            printColor(DEF_Colors[0, vector], "GAME OVER")
            GameOver = 1
        }
    }
    else {
        CUR_Piece_Loc = NextL
        
        local Len = CUR_Piece:count()
        local Hn  = IDX
        for (Y = 1, Len) {
            for (X = 1, Len) {
                if (CUR_Piece[Y, array][X, number]) {
                    holoPos(Hn, entity():toWorld(vec(0, (CUR_Piece_Loc[2] + X)*6, -(CUR_Piece_Loc[1] + Y)*6)))
                    Hn--
                }
            }
        }

        #local Real = CUR_Game:clone()
        
        #placePiece(Real, CUR_Piece, CUR_Piece_Loc[1], CUR_Piece_Loc[2])
        #printGame(Real)
    }
}
