#
# originally from tk_demo.py by Torbert, 10.8.2008
#
# Tkinter Demo, Version 1.0
#
#   Input: none
# Process: a square with a message tries to run off the screen
#  Output: graphical display updated every 10 milliseconds
#
# Updated now for Schelling neighborhood problem. The data structure is
# a grid of patches, and each patch can hold 0 or more agents in a list.
# Although for this problem, only 0 or 1 agents will be used at a time.

from Tkinter import *
from sys import exit
from random import *

w,h=800,600
gridw=200
gridh=200
centerx=(w-gridw)/2
centery=(h-gridh)/2
x,y,dx,dy=100,50,25,25
density=.4

agentList=[]
patchList=[]
uncontentedAgents=[]
moveFromRow=-1
moveFromCol=-1
moveToRow=-1
moveToCol=-1
checkingagent=0

#myColor="#"+decToHex(randint(0,255))+decToHex(randint(0,255))+decToHex(randint(0,255))

class Agent:
    def __init__(self, x, y, row, col):
        self.myX=x
        self.myY=y
        self.myRow=row
        self.myCol=col
        self.myColor=getColor()
        self.contented=True
        self.agentNeighbors=[]
        self.contentedPatches=[]

class Patch:
    def __init__(self, x, y, row, col):
        self.myX=x
        self.myY=y
        self.myRow=row
        self.myCol=col
        self.myColor=getPatchColor()
        self.occupants=[]

def tick():
	global x
	x+=1
#	x1,y1,x2,y2=canvas.coords(rect)
#	print x1,y1,x2,y2
	canvas.coords(rect,x,y,x+dx,y+dy) # move the objects
	canvas.coords(objt,x+dx/2,y+dy/2)
	canvas.after(10,tick) # animation

def decToHex(val):
    s = "%x" % val
    if len(s) < 2:
        s="0"+s
    return s

def getColor():
    s="#"+decToHex(randint(0,255))+decToHex(randint(0,255))+decToHex(randint(0,255))
    return s

#see rgbchart.com
def getPatchColor():
    s="#"+decToHex(255)+decToHex(255)+decToHex(85) # "#ffff55"
    return s


def click(evnt):
#    global x,y
#    x,y=evnt.x,evnt.y
#    myColor=getColor()
#    canvas.itemconfigure(rect,fill=myColor)
#    canvas.itemconfigure(objt,text='Hey!')

    global checkingagent
    global agentList
    global patchList
    if checkingagent>=len(agentList):
        checkingagent=0
    agent=agentList[checkingagent]
    row=agent.myRow
    col=agent.myCol
    color=agent.myColor
    contented=contentedMeasure(agent,patchList)
    print "checking agent %d at r %d c %d, color=%s, contented=%s" % (checkingagent,row,col,color,contented)
    if not contented:
        moveUncontentedAgent(agent)
    checkingagent+=1    

def rightclick(evnt):
    global agentpatches
    global agentList
    global patchList
    
    agentmoved=moveTheAgent()
    print "Moving agent #%d" % agentmoved
    agent = agentList[agentmoved]
    myx=agent.myX
    myy=agent.myY
    agentpatch = agentpatches[agentmoved]
    canvas.coords(agentpatch,myx,myy,myx+dx,myy+dy) # move the agent
        
   
def quit(evnt):
	exit(0)

def patchAtRowCol(patchList,row,col):
    for patch in patchList:
        if patch.myRow==row and patch.myCol==col:
            return patch
    return None

def agentAtRowCol(agentList,row,col):
    for agent in agentList:
        if agent.myRow==row and agent.myCol==col:
            return agent
    return None

def getPatchNeighbors(agent,patchList):
    cols=gridw/dx
    rows=gridh/dy
    row=agent.myRow
    col=agent.myCol
    patchneighbors=[]
    if row==0 and col==0:
        print "getting neighbors for agent at row %d, col %d" % (row,col)
        print "Length of patch neighbors=%d" % (len(patchneighbors))
    elif row==0 and col==cols-1:
        print "getting neighbors for agent at row %d, col %d" % (row,col)
#        print "Length of patch neighbors=%d" % (len(patchneighbors))
    elif row==rows-1 and col==0:
        print "getting neighbors for agent at row %d, col %d" % (row,col)
        print "Length of patch neighbors=%d" % (len(patchneighbors))
    elif row==rows-1 and col==cols-1:
        print "getting neighbors for agent at row %d, col %d" % (row,col)
        print "Length of patch neighbors=%d" % (len(patchneighbors))
    elif row==0:
        print "getting neighbors for agent at row %d, col %d" % (row,col)
#        print "Length of patch neighbors=%d" % (len(patchneighbors))
    elif row==rows-1:
        print "getting neighbors for agent at row %d, col %d" % (row,col)
#        print "Length of patch neighbors=%d" % (len(patchneighbors))
    elif col==0:
        print "getting neighbors for agent at row %d, col %d" % (row,col)
#        print "Length of patch neighbors=%d" % (len(patchneighbors))
    elif col==cols-1:
        print "getting neighbors for agent at row %d, col %d" % (row,col)
#        print "Length of patch neighbors=%d" % (len(patchneighbors))
    else:
        print "getting neighbors for agent at row %d, col %d" % (row,col)
#        print "Length of patch neighbors=%d" % (len(patchneighbors))
    return patchneighbors


def getNeighborCount(agent,patchList):
    row=agent.myRow
    col=agent.myCol
    patchneighbors=getPatchNeighbors(agent,patchList)
    agent.patchNeighbors=patchneighbors
    count=0
    nonecount=0
    for patch in patchneighbors:
        if patch==None:
            nonecount+=1
        else:
            if len(patch.occupants)>0:
                count+=1
  #      print "agent at row %d, col %d has %d None patches" % (row,col,nonecount) 
    return count

def contentedMeasure(agent,patchList):
    row=agent.myRow
    col=agent.myCol
    patchneighbors=getPatchNeighbors(agent,patchList)
    return False

               
def printContentedness(agentList,patchList,rows,cols):
    for row in range(0,rows):
        for col in range(0,cols):
            patch=patchAtRowCol(patchList,row,col)
#            print "Checking patch at row %d, col %d" % (row,col)
            if len(patch.occupants) > 0:
                agent=patch.occupants[0]
                contented=agent.contented
                count=len(agent.agentNeighbors)
                print "Agent at patch row %d col %d, contented=%s, %d neigbhors" % (row,col,contented,count)

def getContentedPatches(agent,patchList,row,col,rows,cols):
    found = False
    radius = 1
    contentedPatches=[]
    while not found:
        randomindex = randint(0,rows*cols-1)
        r=randomindex/cols
        c=randomindex%cols
        patch=patchAtRowCol(patchList,r,c)
        if len(patch.occupants) == 0:
            found=True
    contentedPatches.append(patch)

    return contentedPatches


def moveUncontentedAgent(agent):
    global patchList
    global rows
    global cols
    global moveFromRow
    global moveFromCol
    global moveToRow
    global moveToCol
    
    print "Moving 1 uncontented agent"

    movefromrow=agent.myRow
    movefromcol=agent.myCol
    contentedPatches=getContentedPatches(agent,patchList,movefromrow,movefromcol,rows,cols)
    movetoindex=randint(0,len(contentedPatches)-1)
    patch=contentedPatches[movetoindex]
    movetorow=patch.myRow
    movetocol=patch.myCol
    print "moving agent at r %d c %d to patch at r %d c %d" % (movefromrow,movefromcol,movetorow,movetocol)
    moveFromRow=movefromrow
    moveFromCol=movefromcol
    moveToRow=movetorow
    moveToCol=movetocol

def moveTheAgent():
    global agentList
    global patchList
    global moveFromRow
    global moveFromCol
    global moveToRow
    global moveToCol
    global rows
    global cols
    print "right mouse button clicked"

    #agent=agentAtRowCol(agentList,moveFromRow,moveFromCol)
    patchFrom=patchAtRowCol(patchList,moveFromRow,moveFromCol)
    patchTo=patchAtRowCol(patchList,moveToRow,moveToCol)
    while len(patchFrom.occupants) > 1:
        patchFrom.occupants.pop()
    agent = patchFrom.occupants.pop()
    found = False
    i=0
    agentmoved=-1
    while not found and i < len(agentList):
        tempagent=agentList[i]
        if tempagent.myRow == agent.myRow and tempagent.myCol==agent.myCol:
            agentmoved=i
            found=True
        else:
            i+=1
    if not found:
        print "AGENT NOT FOUND IN AGENTLIST!!"

    while len(patchTo.occupants) > 0:
        patchTo.occupants.pop()
    agent.myX=patchTo.myX
    agent.myY=patchTo.myY
    agent.myRow=patchTo.myRow
    agent.myCol=patchTo.myCol
    agent.contented=True
    patchTo.occupants.append(agent)
    return agentmoved
        
x=randint(0,255)
print "%d = %s" % (x, decToHex(x))
r=randint(0,255)
g=randint(0,255)
b=randint(0,255)
print "r=%d g=%d b=%d" % (r,g,b)
color="#"+decToHex(r)+decToHex(g)+decToHex(b)
print "color=%s" % color

print "rand color=%s" % getColor()

cols=gridw/dx
rows=gridh/dy
numTotalPatches=cols*rows
numAgents=density*numTotalPatches
usedindexes=[]
for i in range(0,int(numAgents)):
    differentindex=False
    while not differentindex:
        randindex=randint(0,numTotalPatches-1)
        if not randindex in usedindexes:
            usedindexes.append(randindex)
            differentindex=True
    row=randindex/cols
    col=randindex%cols
    x=col*dx+centerx
    y=row*dy+centery
    agent=Agent(x,y,row,col)
    if random() < .5:
#        agent.myColor="#0022DD"  #see www.rgbchart.com  000 034 221, blue
        agent.myColor="#0000FF"
    else:
#        agent.myColor="#FF2233"  #see www.rgbchart.com  255 034 051, red
        agent.myColor="#FF0000"
    agentList.append(agent)
    
for index in range(0,numTotalPatches):
    row=index/cols
    col=index%cols
    x=col*dx+centerx
    y=row*dy+centery
    patch=Patch(x,y,row,col)
    patchList.append(patch)

for agent in agentList:
    row = agent.myRow
    col = agent.myCol
    patch=patchAtRowCol(patchList,row,col)
    agent.myX=patch.myX
    agent.myY=patch.myY
    if patch != None:
        patch.occupants.append(agent)
        print "agent with color %s added to patch at row %d, col %d" % (agent.myColor,row,col)
    else:
        print "No patch at row %d, col %d" % (row,col)



#
# Initialize.
#
root=Tk()
canvas=Canvas(root,width=w,height=h,bg='white')
canvas.pack()
#
# Graphics objects. 
#
#rect=canvas.create_oval(x,y,x+dx,y+dy,fill='yellow',outline='black')
#rect=canvas.create_oval(x,y,x+dx,y+dy,fill='#00ffff',outline='black')

agentpatches=[]
patches=[]
for patch in patchList:
    myx=patch.myX
    myy=patch.myY
    color=patch.myColor
    patches.append(canvas.create_rectangle(myx,myy,myx+dx,myy+dy,fill=color,outline='black'))

rect=canvas.create_oval(x,y,x+dx,y+dy,fill=getColor(),outline='black')
objt=canvas.create_text(x+dx/2,y+dy/2,text='Bye!',fill='white')

for agent in agentList:
    myx=agent.myX
    myy=agent.myY
    color=agent.myColor
#    print "displaying agent at %d, %d, color=%s" % (myx,myy,color)
    agentpatches.append(canvas.create_oval(myx,myy,myx+dx,myy+dy,fill=color,outline='black'))

#
# Callbacks.
#
root.bind('<Button-1>',click)
root.bind('<Button-3>',rightclick)
root.bind('<q>',quit)
canvas.after(10,tick) # animation
#
# Start the continuous graphics display loop.
#
root.mainloop()

