#@name array_test1
@persist [GenListInc GenSubListInc GenListCount] [GenSubList]:array [GenList]:table [GenLegal GenIllegal]:table Offsets:table
@persist [KingPosW KingPosB]

#-- variables
local N = -16
local S =  16
local E =   1
local W =  -1

Offsets = table(
    "P" = array(N, 2*N, N+W, N+E),
    "N" = array(2*N+E, N+2*E, S+2*E, 2*S+E, 2*S+W, S+2*W, N+2*W, 2*N+W),
    "B" = array(N+E, S+E, S+W, N+W),
    "R" = array(N, E, S, W),
    "Q" = array(N, E, S, W, N+E, S+E, S+W, N+W),
    "K" = array(N, E, S, W, N+E, S+E, S+W, N+W)
)

#-- functions
function array array:doMove( IndexA, IndexB, Target:string ) {
    if (This[IndexA, string] == "K") { KingPosW = IndexB }
    if (This[IndexA, string] == "k") { KingPosB = IndexB }
        
    This[IndexB, string] = This[IndexA, string] # to
    This[IndexA, string] = "."                  # from
    
    return This
}

function array array:undoMove( IndexA, IndexB, Target:string  ) {
    if (This[IndexB, string] == "K") { KingPosW = IndexA }
    if (This[IndexB, string] == "k") { KingPosB = IndexA }

    This[IndexA, string] = This[IndexB, string] #from
    This[IndexB, string] = Target               # to    
    
    return This
}



function array array:getMovesAtIndex( Index, SuperPiece:string ) {
    local MoveList = array()
    
    #-- setup
    local BitIndex = Index + ((Index - 1) && -8) - 1

    local PieceAtIndex = SuperPiece == "" ? This[Index, string] : SuperPiece
    local TeamAtIndex = inrange(toByte(PieceAtIndex), 65, 90)

    #-- black pawns go south instead of north
    local NegOffset = PieceAtIndex == "p" ? -1 : 1
    local CanDouble = TeamAtIndex ? inrange(Index, 49, 56) : inrange(Index, 9, 16)

    #-- pawns, kings and knights can only make one move
    local LoopOnce = PieceAtIndex:findRE("[PNKpnk]")

    #-- loop through bit offsets for a particular piece
    foreach (K, Offset:number = Offsets[PieceAtIndex:upper(), array]) {
        local NextBitIndex = BitIndex

        while (1) {
            #-- add the offset to the index to get a new position on the board
            NextBitIndex = NextBitIndex + Offset*NegOffset

            #-- break if the shifted index falls outside the valid side of the 0x88 array
            if (NextBitIndex && 0x88) { break }

            #-- convert the bit index back into 0x40, get the occupant and team
            local NextIndex = ((NextBitIndex + (NextBitIndex && 7)) >> 1) + 1
            local NextPiece = This[NextIndex, string]
            local NextTeam  = inrange(toByte(NextPiece), 65, 90)

            #-- break if the index isn't empty and the occupant is the same team
            if (NextPiece != "." & NextTeam == TeamAtIndex) { break }

            #-- pawn has a few special rules
            if (PieceAtIndex == "P" | PieceAtIndex == "p") {
                if (K > 2 & NextPiece == ".") { break }
                if (K < 3 & NextPiece != ".") { break }
                if (K == 2 & (!CanDouble | This[Index - 8*NegOffset, string] != ".")) { break }
            }

            #-- we've made it this far, so the move is pseudo-legal and can be added
            if (SuperPiece == "") {
                MoveList:pushNumber(NextIndex)
            }
            else {
                if (NextPiece != ".") { MoveList:pushNumber(NextIndex) }  
            }
            #MoveList[MoveList:count() + 1, number] = NextIndex

            #-- break if pawn, king or knight, or if the index isn't empty and the occupant is an enemy
            if (LoopOnce | (NextPiece != "." & NextTeam != TeamAtIndex)) { break }
        }
    }

    return MoveList
}


#---------------------------------------------------------------
#-- returns false if the king is not safe at the given index
#[function number array:getKingSafe(Index, Team:string) {
    local TeamAtIndex = inrange(toByte(This[Index, string]), 65, 90)
    foreach (K, OffsetList:array = Offsets) {
        if (This:getMovesAtIndex(Index, TeamAtIndex ? K : K:lower()):find(!TeamAtIndex ? K : K:lower())) {
            return 0
        }
    }
    return 1
}]#



#[function number array:genMoves( Player:string ) {
    local PieceList = Player == "w" ? "[PNRBKQ]" : "[pnrbkq]"

    while (perf() & GenListInc < 64) {
        GenListInc++
        if (This[GenListInc, string]:findRE(PieceList)) {
            local List = This:getMovesAtIndex(GenListInc)
            if (List:count()) {
                GenList[GenListInc, array] = List
                GenListCount += List:count()
            }
        }
    }
    
    return GenListInc == 64
}]#


function number array:genMoves( Player:string ) {
    local PieceList = Player == "w" ? "[PNRBKQ]" : "[pnrbkq]"

    while (perf(95) & GenListInc < 64) {
        if (GenSubListInc == 0) {
            GenListInc++
            if (This[GenListInc, string]:findRE(PieceList)) {
                GenSubList = This:getMovesAtIndex(GenListInc, "")
            }
        }
    
        if (GenSubListInc < GenSubList:count()) {
            GenSubListInc++
            
            #-- move data
            local IndexA = GenListInc
            local IndexB = GenSubList[GenSubListInc, number]
            local OPiece = This[IndexB, string]
            
            #-- do move
            if (This[IndexA, string] == "K") { KingPosW = IndexB }
            if (This[IndexA, string] == "k") { KingPosB = IndexB }
    
            This[IndexB, string] = This[IndexA, string] # to
            This[IndexA, string] = "."                  # from

            #-- check king safety
            local Table = GenLegal
            foreach (K, OffsetList:array = Offsets) {
                local List = This:getMovesAtIndex(KingPosW, K)
                if (List:count()) {
                    Table = GenIllegal
                    break
                }
            }
            
            Table[IndexA, array] = Table[IndexA, array] ?: array()
            Table[IndexA, array]:pushNumber(IndexB)
            
#[            if (IsSafe) { 
                GenLegal[IndexA, array] = GenLegal[IndexA, array] ?: array()
                GenLegal[IndexA, array]:pushNumber(IndexB) 
            }
            else { 
                GenLegal[IndexA, array] = GenLegal[IndexA, array] ?: array()
                GenLegal[IndexA, array]:pushNumber(IndexB) 
            }]#
               
            #-- undo move
            if (This[IndexB, string] == "K") { KingPosW = IndexA }
            if (This[IndexB, string] == "k") { KingPosB = IndexA }

            This[IndexA, string] = This[IndexB, string] #from
            This[IndexB, string] = OPiece               # to
            
            #print( "Move: ", IndexA, IndexB, OPiece)
            #print( "wtf", format("%02d%02d", GenListInc, GenSubList[GenSubListInc, number]))
        }
        
        if (GenSubListInc == GenSubList:count()) {
            GenSubListInc = 0
            GenSubList:clear()  
        }
    }
    
    return GenListInc == 64
}







function array fenToBoard( Fen:string ) {
    #-- "rnbkqbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBKQBNR/ w KQkq"
    local Exp = Fen:explode("/")
    local Str = ""

    for (F = 1, 8) {
        local RStr = Exp[F, string]
        for (R = 1, RStr:length()) {
            local Rep = RStr[R]:toNumber()
            if (Rep) { Str += ".":repeat(Rep) } else { Str += RStr[R] }

            if (RStr[R] == "K") { KingPosW = Str:length() }
            if (RStr[R] == "k") { KingPosB = Str:length() }
        }
    }

    #local Data = Exp:popString():trim():explode(" ")

    #PlyTurn = Data[1, string]
    #CastleW = Data[2, string]:sub(1, 2)
    #CastleB = Data[2, string]:sub(3, 4)
    #Chessboard = Str
    
    local Board = array()
    for (I = 1, 64) {
        Board[I, string] = Str[I]
    }
     
    return Board   
}



#print( Test:getMovesAtIndex(16))
