#!/usr/bin/env python
# -*- coding: utf-8 -*-

from Tkinter import *
import tkFont
from graph_SBC import MultiGraph
from wskrand import RNG

QUOTE="""Hoare's Law of Large Problems:
Inside every large problem is a small problem struggling to get out."""

RCSID="$Id: murdoch.pyw,v 1.1 2009/04/10 15:47:57 wilfrid Exp wilfrid $"
shortRCSID=RCSID[5:-1].replace("Wilfrid ","")

GEOMETRY="+10+10"

class Animation(MultiGraph):
    def __init__(self,master,rng=None):
        self.stopgovar=StringVar()
        self.smallvar=StringVar()
        MultiGraph.__init__(self,master,
                            actions=[("Increment",self.increment),
                                     ("RUN","PAUSE",self.stopgovar,self.stopgo),
                                     ("SmallSet","Remove",self.smallvar,self.small),
                                     ("Single",self.animate1),
                                     #("Mult (1)",self.animate2),
                                     #("Mult (2)",self.animate3),
                                     ("Couple",self.animate4)
                                    # ("CFTP",self.animate5),
                                     ])
        self.x0=self.graph[0].x0
        self.y0=self.graph[0].y1
        self.x1=self.graph[0].x1
        self.y1=self.graph[0].y0

        self.t=self.x0
        self.tinc=16
        self.tstore=self.t
        self.tincstore=self.tinc
        
        self.animation=None
        self.current_animation=None
        self.hold=500
        if rng:
            self.rng=rng
        else:
            self.rng=RNG()

        #self.c=["firebrick","chocolate","salmon","saddlebrown","coral"]
        self.c=["red","blue"]
        
    def increment(self):
        animation=self.after(400,self.current_animation)        
    
    def stopgo(self):
        if self.stopgovar.get()=="RUN":
            self.after_cancel(self.animation)
        else:
            self.animation=self.after(400,self.current_animation)

    def animate1(self):
        self.restart()
        self.x=self.rng.uniform(0,1)
        self.current_animation=self.animate1_run
        self.animation=self.after(500,self.current_animation)

    def animate1_run(self):
        self.clean()
        if self.t>self.x1: self.rescale1()
        self.triangle1(self.x)
        self.graph[0].tag_raise("small")
        u,v=self.kernel(self.x)
        self.transit1((self.x,0),(u,0))
        self.transitg((self.x,0),(u,0))
        self.x=u
        self.t=self.t+self.tinc
        if self.stopgovar.get()=="PAUSE":
            self.animation=self.after(self.hold,self.animate1_run)
        #self.animation=self.after(self.hold,self.animate1_run)

    def triangle1(self,x):
        self.graph[0].create_polygon(self.x0,self.y0,
                                     self.x0*(1-x)+self.x1*x,self.y1,
                                     self.x1,self.y0,
                                     fill="red",
                                     stipple="gray50",tag="move")
        self.graph[0].create_line(self.x0*(1-x)+self.x1*x,self.y0,
                                  self.x0*(1-x)+self.x1*x,self.y1,
                                  width=3,
                                  fill="gray45",tag="move")

    def transit1(self,p0,p1,fg="red",blip=0.1):
        x,y=p0
        u,v=p1
        w=(x+u)/2
        ww=(y+v)/2+blip
        self.graph[0].create_line(self.x0*(1-x)+self.x1*x,
                                  self.y0*(1-y)+self.y1*y,
                                  self.x0*(1-w)+self.x1*w,
                                  self.y0*(1-ww)+self.y1*ww,
                                  self.x0*(1-u)+self.x1*u,
                                  self.y0*(1-v)+self.y1*v,
                                  arrow="last",
                                  smooth=1,
                                  width=3,
                                  fill=fg,
                                  tag="move")
        
    def transitg(self,p0,p1,fg="red",blip=0.1):
        x=p0[0]
        u=p1[0]
        self.graph[1].create_line(self.t,
                                  self.y0*(1-x)+self.y1*x,
                                  self.t+self.tinc,
                                  self.y0*(1-u)+self.y1*u,
                                  width=3,
                                  fill=fg)

    def animate2(self):
        self.restart()
        self.x=[0]*len(self.c)
        for i in range(len(self.c)):
            self.x[i]=self.rng.uniform(0,1)
        self.current_animation=self.animate2_run
        self.animation=self.after(100,self.current_animation)

    def animate2_run(self):
        self.clean()
        if self.t>self.x1: self.rescale1()
        for i in range(len(self.x)):
            x=self.x[i]
            self.triangle2(x)
            u,v=self.kernel(x)
            self.transit1((x,0),(u,0),fg=self.c[i])
            self.transitg((x,0),(u,0),fg=self.c[i])
            self.x[i]=u
        self.t=self.t+self.tinc
        self.animation=self.after(self.hold,self.animate2_run)

    def triangle2(self,x):
        self.graph[0].create_line(self.x0,self.y0,
                                  self.x0*(1-x)+self.x1*x,self.y1,
                                  self.x1,self.y0,
                                  fill="purple",
                                  tag="move")
        self.graph[0].create_line(self.x0*(1-x)+self.x1*x,self.y0,
                                  self.x0*(1-x)+self.x1*x,self.y1,
                                  width=3,
                                  fill="gray45",tag="move")

    def small(self):
        if self.smallvar.get()=="Remove":
            self.graph[0].lower(self.graph[0].create_polygon(self.x0,self.y0,
                                     (self.x0+self.x1)/2,(self.y0+self.y1)/2,
                                     self.x1,self.y0,
                                     fill="darkolivegreen",tags="small",stipple="gray50"))
        else:
            self.graph[0].delete("small")

    def animate3(self):
        self.restart()
        self.x=[0]*len(self.c)
        for i in range(len(self.c)):
            self.x[i]=self.rng.uniform(0,1)
        self.current_animation=self.animate3_run
        self.animation=self.after(100,self.current_animation)

    def animate3_run(self):
        self.clean()
        if self.t>self.x1: self.rescale1()
        for i in range(len(self.x)):
            x=self.x[i]
            self.triangle2(x)
            self.graph[0].tag_raise("small")
            u,v=self.kernel(x)
            self.transit1((x,0),(u,v),fg=self.c[i])
            self.transitg((x,0),(u,v),fg=self.c[i])
            self.transit1((u,v),(u,0),fg=self.c[i],blip=0)
            self.x[i]=u
        self.t=self.t+self.tinc
        self.animation=self.after(self.hold,self.animate3_run)

    def animate4(self):
        self.restart()

        self.small()
        self.couple=0
        self.coupled=self.couple
        self.x=[0]*len(self.c)
        for i in range(len(self.c)):
            self.x[i]=self.rng.uniform(0,1)
        self.current_animation=self.animate4_run
        self.animation=self.after(100,self.current_animation)

    def animate4_run(self):
        self.clean()
        if self.t>self.x1: self.rescale1()
        x=self.x[0]
        self.triangle2(x)
        u,v=self.kernel(x)
        if self.couple:
            col="purple"
        else:
            col=self.c[0]
        self.transit1((x,0),(u,v),fg=col)
        self.transitg((x,0),(u,v),fg=col)
        self.transit1((u,v),(u,0),fg=col,blip=0)
        self.x[0]=u
        self.coupled=self.couple
        self.couple = self.couple or ((u>v) and (1-u>v))
        for i in range(1,len(self.x)):
            x=self.x[i]
            self.triangle2(x)
            if not(self.couple):
                u,v=(0,-1)
                while (u>v) and (1-u>v):
                    u,v=self.kernel(x)
            if not(self.coupled):
                self.transit1((x,0),(u,v),fg=self.c[i])
                self.transitg((x,0),(u,v),fg=self.c[i])
                self.transit1((u,v),(u,0),fg=self.c[i],blip=0)
            self.x[i]=u
        self.t=self.t+self.tinc
        if self.stopgovar.get()=="PAUSE":
            self.animation=self.after(self.hold,self.animate4_run)
#        self.animation=self.after(self.hold,self.animate4_run)

    def animate5(self):
        self.restart()

        self.small()
        self.x=[0]*len(self.c)
        for i in range(len(self.c)):
            self.x[i]=self.rng.uniform(0,1)
        self.couple=0
        self.coupled=self.couple
        u,v=self.kernel(0)
        if (u>v) and (1-u>v):
            self.innov=[(u,v),self.kernel(u)]
        else:
            u,v=self.kernel(0)
            if (u>v) and (1-u>v):
                self.innov=[-1,(u,v)]
            else:
                self.innov=[-1,-1]
        self.current_animation=self.animate5_run
        self.animation=self.after(100,self.current_animation)

    def animate5_run(self):
        self.clean()
        self.coupled=0
        self.t=self.x1-len(self.innov)*self.tinc
        #self.graph[1].addtag_withtag("block0","block")
        self.graph[1].itemconfig("block",fill="salmon")
        self.graph[1].itemconfig("move",fill="lemonchiffon")
        while self.t<self.x0:
            self.rescale2()
            self.t=self.x1-len(self.innov)*self.tinc
        #print self.t,self.tinc,self.t+len(self.innov)*self.tinc
        k=0
        while k<len(self.innov):
            self.clean()
            if self.innov[k]==-1:
                for i in range(len(self.x)):
                    x=self.x[i]
                    self.triangle2(x)
                    u,v=self.kernel(x)
                    self.transit1((x,0),(u,v),fg=self.c[i])
                    self.transit1((u,v),(u,0),fg=self.c[i],blip=0)
                    self.x[i]=u
                    self.transitg0()
                self.t=self.t+self.tinc
                self.update_idletasks()
                self.after(100)
                #print "*",self.t
            else:
                x=self.x[0]
                self.triangle2(x)
                u,v=self.innov[k]
                self.transit1((x,0),(u,v),fg="purple")
                self.transit1((u,v),(u,0),fg="purple",blip=0)
                if self.coupled:
                    self.transitg1(x,u)
                else:
                    self.transitg0()
                self.coupled=1
                self.x[0]=u
                self.t=self.t+self.tinc
                self.update_idletasks()
                self.after(100)
                #print "+",self.t
            k=k+1
        #print self.t,self.tinc
        #print self.innov
            
        self.innov=self.animate5_innov(self.innov)
        self.current_animation=self.animate5_run
        self.stopgovar.set("ONE-STEP")
        #self.animation=self.after(100,self.current_animation)

    def animate5_innov(self,innov):
        innov0=[]
        u,v=self.kernel(0)
        while len(innov0)<len(innov) and not( (u>v) and (1-u>v) ):
            innov0.append(-1)
            u,v = self.kernel(u)
        while len(innov0)<len(innov):
            innov0.append((u,v))
            u,v = self.kernel(u)
        if innov0[-1]>-1:
            i=0
            while i<len(innov) and innov[i]==-1:
                innov[i]=(u,v)
                u,v = self.kernel(u)
                i=i+1
        return innov0+innov

    def transitg0(self,fg="gray25"):
        t=self.t
        tinc=self.tinc
        self.graph[1].create_polygon(t,self.y0,
                                     t+tinc,self.y0,
                                     t+tinc,self.y1,
                                     t,self.y1,
                                     fill="red",
                                     tags="block")

    def transitg1(self,x,u):
        self.graph[1].create_line(self.t,
                                  self.y0*(1-x)+self.y1*x,
                                  self.t+self.tinc,
                                  self.y0*(1-u)+self.y1*u,
                                  width=3,
                                  fill="purple",
                                  tags="move")

    def kernel(self,x):
        u,v = (self.rng.uniform(0,1),self.rng.uniform(0,1))
        if x*v>u:
            return x-u,1-v
        elif (1-x)*v>1-u:
            return x+1-u,1-v
        else:
            return u,v

    def restart(self):
        self.clean()
        self.t=self.tstore
        self.tinc=self.tincstore
        self.graph[1].delete(ALL)

    def clean(self):
        if self.animation:
            self.after_cancel(self.animation)
        self.graph[0].delete("move")

    def rescale1(self):
        self.t=self.t/2
        self.tinc=self.tinc/2
        self.graph[1].scale(ALL,self.x0,self.y0,0.5,1)

    def rescale2(self):
        self.tinc=self.tinc/2
        self.graph[1].scale(ALL,self.x1,self.y0,0.5,1)

if __name__ == "__main__":
    master=Tk()

    rng=RNG()
    rng.seed(13355)

    mg=Animation(master,rng=rng)

    mg.animate1()

    font=tkFont.Font(family="Helvetica",size=8,weight="bold")
    Label(master,text=QUOTE,font=font).grid(column=0,row=10,columnspan=16)
    Label(master,text=shortRCSID,font=font,fg="darkslategray").grid(row=11,column=1,columnspan=16)

    master.geometry(GEOMETRY)
    master.mainloop()

    raise SystemExit
