@name towers_of_hanoi
@inputs 
@outputs E:entity
@persist Game:table
@trigger none

if (first()) {
    
    E = entity()
    
    Game["Delay", number] = 15
    Game["NumPegs", number] = 3
    Game["NumDisks", number] = 10

    local N = 0
    for (I = 1, Game["NumPegs", number]) {
        N++
        
        holoCreate(N)
        holoParent(N, entity())
        holoPos(N, entity():toWorld(vec(Game["NumDisks", number]*2.5 * I - 50, 0, Game["NumDisks", number] * 2.5)))
        holoScaleUnits(N, vec(2, 2, Game["NumDisks", number] * 2.5))    
        holoModel(N, "cylinder")
        holoMaterial(N, "sprops/textures/sprops_wood2")
        holoColor(N, vec(225,200,200))
        
        holoCreate(N+200)
        holoParent(N+200, N)
        holoPos(N+200, holoEntity(N):pos())
        holoScaleUnits(N+200, vec(2, 2, Game["NumDisks", number] * 2.5))    
        holoModel(N+200, "cylinder")
        holoMaterial(N+200, "models/wireframe")
        holoColor(N+200, vec4(225,200,200,100))        
        
        local Ent = holoEntity(N)
        Ent["NumDisks", number] = I == 1 ? Game["NumDisks", number] : 0
    }
    
    local N = Game["NumDisks", number] + Game["NumPegs", number] + 1
    local DiskMaxD = vec(Game["NumDisks", number]*2, Game["NumDisks", number]*2, 2)
    local DiskMinD = vec(5, 5, 2)

    for (I = 1, Game["NumDisks", number]) {
        N--
        
        holoCreate(N)
        holoParent(N, entity())
        holoPos(N, holoEntity(1):toWorld(vec(0, 0, I*2 - Game["NumDisks", number]*2.5/2 - 1)))
        holoScaleUnits(N, mix(DiskMinD, DiskMaxD, I / Game["NumDisks", number]))    
        holoModel(N, "rcylinder")  
        holoMaterial(N, "sprops/textures/sprops_wood2")
        holoColor(N, hsv2rgb((120 / Game["NumDisks", number])*I, 0.5, 0.5))
        
        
           holoCreate(N+100)
        holoParent(N+100, N)
        holoPos(N+100, holoEntity(N):pos())
        holoScaleUnits(N+100, mix(DiskMinD, DiskMaxD, I / Game["NumDisks", number]))    
        holoModel(N+100, "rcylinder")  
        holoMaterial(N+100, "models/wireframe")
        holoColor(N+100, hsv2rgb((120 / Game["NumDisks", number])*I, 0.5, 0.5))
    }
    
    function void makeMove( String:string, Undo ) {
        local Array = String:explode(",")
        
        local Disk = Array[1, string]:toNumber() + Game["NumPegs", number]
        local From = holoEntity(Array[Undo ? 3 : 2, string]:toNumber())
        local To = holoEntity(Array[Undo ? 2 : 3, string]:toNumber())
        
        From["NumDisks", number] = From["NumDisks", number] - 1
        To["NumDisks", number] = To["NumDisks", number] + 1
        
        holoPos(Disk, To:toWorld(vec(0, 0, To["NumDisks", number]*2 - Game["NumDisks", number]*2.5/2 - 1)))
    }
    
    runOnKeys(owner(), 1)
    runOnFile(1)
    fileLoad("hanoi" + Game["NumDisks", number] + Game["NumPegs", number] + ".txt")
}

if (keyClk(owner())) {
    if (owner():keyPressed("up") & Game["Moves", array]:count()) {
        timer("StartGame", 15)
        runOnKeys(owner(), 0)
    }
}

if (fileClk()) {
    local File = fileRead()

    if (File != "") {
        Game["Moves", array] = vonDecode(File)
        Game["MoveCount", number] = Game["Moves", array]:count()
    
        #if (Game["Moves", array]:count()) { timer("StartGame", 1000) }
        #print("Loading from file...")
    } else {
        runOnHTTP(1)

        # Recursive Towers of Hanoi
        local Program = ""
    
        Program += "def Hanoi( Disks, Start=1, End="+Game["NumPegs", number]+" ):\n"
        Program += "    if Disks:\n"
        Program += "        Hanoi( Disks - 1, Start, 6 - Start - End )\n"
        Program += "        print( '%d,%d,%d' % (Disks, Start, End) ) \n"
        Program += "        Hanoi( Disks - 1, 6 - Start - End, End )\n"
        
        Program += "Hanoi( Disks="+Game["NumDisks", number]+" )\n"
        
        httpRequest("http://www.rextester.com/rundotnet/api?LanguageChoiceWrapper=5&Program=" + httpUrlEncode(Program))
        #print("Loading from http...")
    }
}

if (httpClk()) {
    local JSON = httpData()
    Game["Moves", array] = JSON:matchFirst("\"Result\":\"(.*)\",\"Stats\""):explode("\\n")
    Game["Moves", array]:removeString(Game["Moves", array]:count())
    Game["MoveCount", number] = Game["Moves", array]:count()

    #if (Game["Moves", array]:count()) { timer("StartGame", 1000) }
    fileWrite("hanoi" + Game["NumDisks", number] + Game["NumPegs", number] + ".txt", vonEncode(Game["Moves", array]))
}

if (clk("StartGame")) {
    if (Game["Move", number] < Game["MoveCount", number]) {
        timer("StartGame", Game["Delay", number])

        Game["Move", number] = Game["Move", number] + 1
        makeMove(Game["Moves", array][Game["Move", number], string], 0)
        setName("Move: " + Game["Move", number])  
    }    
}
