import pyglet,sys,os,math,time
from pyglet.gl import *


class ExtremeType(object):
    def __new__(cls, cmpr, rep, *args, **kwds):
        if cmpr is -1:
            it = cls.__dict__.get('__UniversalMinimum__')
            if it is not None: return it
            cls.__UniversalMinimum__ = it = object.__new__(cls)
        else: #if cmpr is 1:
            it = cls.__dict__.get('__UniversalMaximum__')
            if it is not None: return it
            cls.__UniversalMaximum__ = it = object.__new__(cls)
        assert type(rep) is str
        it.__comparison = cmpr
        it.__repr = rep
        return it

    def getcomparison(self):
        return self.__comparison

    def __cmp__(self, other):
        if isinstance(other, ExtremeType):
           return cmp(self.__comparison, other.getcomparison())
        return self.__comparison

    def __repr__(self):
        return self.__repr

# these two values will always compare as less/greater than anything else of any type
uMax = ExtremeType(1, 'UniversalMaximum')
uMin = ExtremeType(-1, 'UniversalMinimum')


class CoreVar:
    """a non-updated animated value optimization. faster calcs and quick creation
       __init__(ftime,dist,animtype)
           ftime    = total number of frames
           dist     = final value (start value is 0)
           animtype = 0 [+ 1 for smooth start][+ 2 for smooth end]
                        [+ 3 for trigonometric smoothness]
       instance(frame=0) or instance[frame=0]
           frame = the current frame in the animation (between 0 and dist initialized)"""

    def __init__(self,ftime,dist,animtype):
        self.dist = dist; self.ftime = float(ftime)
        self.lambda1 = self.lambda2 = self.crethlp = None #for coverity, default the possibly undefined attributes
        if animtype == 0: #linear
            self.famt = self.dist/float(ftime)
            self.callret = lambda frame: frame*self.famt
        elif animtype == 1: #smooth start
            self.famt = self.dist/float(ftime**2)
            self.callret = lambda frame: (frame**2)*self.famt
        elif animtype == 2: #smooth end
            self.famt = -self.dist/float(ftime**2)
            self.callret = lambda frame: self.famt*((frame-self.ftime)**2)+self.dist
        elif animtype == 3: #smooth start & end
            self.famt = 2*self.dist/float(ftime**2)
            self.lambda1 = lambda frame: (frame**2)*self.famt
            self.lambda2 = lambda frame: -self.famt*((frame-self.ftime)**2)+self.dist
            self.crethlp = lambda frame: (frame <= self.ftime/2) and self.lambda1 or self.lambda2 #lambda1 is an object, so never 0
            self.callret = lambda frame: self.crethlp(frame)(frame)
        elif animtype == 4: #trig-based smooth start
            self.famt = self.dist
            self.ftime = math.pi/float(2*ftime)
            self.callret = lambda frame: self.famt*(1-math.cos(frame*self.ftime))
        elif animtype == 5: #trig-based smooth end
            self.famt = self.dist
            self.ftime = math.pi/float(2*ftime)
            self.callret = lambda frame: self.famt*math.sin(frame*self.ftime)
        elif animtype == 6: #trig-based smooth start & end
            self.famt = self.dist/2.0
            self.ftime = math.pi/float(ftime)
            self.callret = lambda frame: self.famt*(1-math.cos(frame*self.ftime))

    def __call__(self,frame=0): return self.callret(frame)

    def __getitem__(self,frame=0): return self.callret(frame)


class LinCtr:
    """init([maxix,minix=0,startix=0,startdir=1],[bounce=F,repeat=T,active=T])
       an updated linear counter - works with a CoreVar. update once per frame.
       __init__(posargs,ctrlargs=[])
           posargs = [maxix,startix=0,startdir=1,minix=0]
               index range = [minix,maxix) w/o bounce, [minix,maxix] w/ bounce
               startix = start index (can be a fraction)
               startdir = increment value (usually use +1 or -1, or 0 to pause)
           ctrlargs = [bounce=False,repeat=False,active=True]
               bounce: change dir when pos is >= max (or <= min with repeat on)
               repeat: with bounce off, when pos >= max, go to min and continue
               active: self-maintained var indicating when to update LinCtr
       update(timestep=1.0) #call once per frame
           timestep = amount of time (in frames) that has passed since last update
       instance() returns the current index value. no computation cost."""

    def __init__(self,posargs,ctrlargs=[]): self.posargsunpack(*posargs); self.ctrlargsunpack(*ctrlargs)

    def posargsunpack(self,maxix,startix=0,startdir=1,minix=0): self.max = float(maxix); self.min = float(minix); self.pos = float(startix); self.dir = startdir

    def ctrlargsunpack(self,bounce=False,repeat=False,active=True): self.bounce = bounce; self.repeat = repeat; self.active = active

    def update(self,timestep=1.0):
        if self.active:
            self.pos += timestep*self.dir
            if self.pos >= self.max:
                self.pos = self.max
                if self.bounce: self.dir = -1
                elif self.repeat: self.pos = 0
                else: self.active = False
            if self.pos <= self.min:
                self.pos = self.min
                if self.repeat: self.dir = 1
                else: self.active = False

    def __call__(self): return self.pos


class AnimVar:
    """([ftime,ix,dir,minix],dist,animtype,[b=F,r=T,a=T],[delay,tie,looptype])
       an updated handler for a CoreVar and a LinCtr. update each frame.
       __init__(posargs,dist,animtype,ctrlargs=[],loopargs=[])
           posargs = [ftime,startix=0,startdir=1,minix=0]
               (args are same as LinCtr posargs)
           dist = final value (start value is 0)
           animtype = 0 [+ 1 for smooth start][+ 2 for smooth end]
                        [+ 3 for trigonometric smoothness]
           ctrlargs = [bounce=False,repeat=False,active=True]
               (args are same as LinCtr ctrlargs)
           loopargs = [delay=0.0,tie=None,looptype=0]
               delay = amount of frames to wait before resuming update execution.
               tie = an AnimVar, Var, or Sprite, which prevents update until inactive.
               looptype = 0 (usually - for vars that loop internally)
                        = 1 (for the first var in a sequence of looping vars)
                            (this var can't have ftime==1, or looping will break)
                        = 2+(for the remaining vars in a sequence of looping vars)
                        = -1(for a var that runs when another var IS active)
       update(timestep=1.0) #call once per frame
           timestep = amount of time (in frames) that has passed since last update
       instance() returns the current index value. no computation cost.
       active() returns True if the current var is running (loop seqs are always True)"""

    def __init__(self,posargs,dist,animtype,ctrlargs=[],loopargs=[]):
        self.data = CoreVar(posargs[0],dist,animtype); self.cframe = LinCtr(posargs,ctrlargs)
        self.offset = dist; self.loopargsunpack(posargs,ctrlargs,*loopargs)

    def loopargsunpack(self,posargs,ctrlargs,delay=0.0,tie=None,looptype=0):
        self.delay = delay; self.tie = tie #the higher the var level, the more dangerous it is to tie.
        self.update = looptype >= 0 and getattr(self,"update%d" % min(2,looptype)) or self.updatealt #faster to use min than default condition in getattr
        if looptype > 0: self.cframe.active = False; self.remdelay = delay; self.cframeargs = (posargs,ctrlargs)

    def loopto(self,tie): self.tie = tie #the first var in the loop should tie to the last var in the loop (after creation)

    def update0(self,timestep=1.0): #standard update method
        if self.tie: #until the tied var is finished, this var waits
            if self.tie.active(): return
            self.tie = None
        if self.delay != 0.0: self.delay = max(0.0,self.delay - timestep); return
        self.cframe.update(timestep)

    def update1(self,timestep=1.0): #update for the first in a looped series
        if not self.cframe.active: #var re-activates when tie is stopped
            if self.tie.active(): return
            self.cframe = LinCtr(*self.cframeargs); self.cframe.active = True; self.delay = self.remdelay
        if self.delay != 0.0: self.delay = max(0.0,self.delay - timestep); return
        self.cframe.update(timestep)

    def update2(self,timestep=1.0): #update for the rest in a looped series
        if not self.cframe.active: #var re-activates when tie is running
            if not self.tie.active(): return
            self.cframe = LinCtr(*self.cframeargs); self.cframe.active = True; self.delay = self.remdelay
        if self.tie.active(): return #var waits until tie is finished
        if self.delay != 0.0: self.delay = max(0.0,self.delay - timestep); return
        self.cframe.update(timestep)

    def updatealt(self,timestep=1.0): #standard update method, but waits for tie to be True
        if self.tie: #until the tied var is active, this var waits
            if not self.tie.active(): return
            self.tie = None
        if self.delay != 0.0: self.delay = max(0.0,self.delay - timestep); return
        self.cframe.update(timestep)

    def __call__(self): return self.data[self.cframe()]

    def addend(self): return not self.cframe.bounce or self.cframe.repeat #bounce=T & repeat=F doesn't go anywhere...

    def active(self): return self.cframe.active


class Var:
    """main variable class. handles all single AnimVars of a particular var.
       __init__(start=0,lbound=uMin,ubound=uMax)
           start = the start offset of the variable
           lbound, ubound = lower/upper bound for the var's values
       add(posargs,dist,animtype=0,ctrlargs=[],loopargs=[]) #checks for ftime==0 or dist==0
           posargs = [ftime,minix=0,startix=0,startdir=1]
               (args are same as LinCtr posargs)
           dist = final distance (after ftime frames)
           animtype = 0 [+ 1 for smooth start][+ 2 for smooth end]
                        [+ 3 for trigonometric smoothness]
           ctrlargs = [bounce=False,repeat=False,active=True]
               (args are same as LinCtr ctrlargs)
           loopargs = [delay=0.0,tie=None,looptype=0]
               (args are same as AnimVar loopargs)
       cut(animvar=None) #cut an animation short (as much as it has already animated stays)
       end(animvar=None) #finish an animation instantly (jumps to end immediately)
           animvar = an AnimVar object in the var (must exist)
       update(timestep=1.0) #call once per frame
           timestep = amount of time (in frames) that has passed since last update
       final() #find the final value of the Var (once all AnimVars finish)
       active() returns True if the current var is running (loop seqs are always True)"""

    def __init__(self,start=0,lbound=uMin,ubound=uMax): self.offset = start; self.lb = lbound; self.ub = ubound; self.data = []

    def add(self,posargs,dist,animtype=0,ctrlargs=[],loopargs=[]): #returns AnimVar for using in ties
        if not posargs[0] or not dist: return None
        coreadd = AnimVar(posargs,dist,animtype,ctrlargs,loopargs)
        self.data.append(coreadd)
        return coreadd
    
    def movto(self,posargs,fval,animtype=0,ctrlargs=[],loopargs=[]): #returns AnimVar for using in ties - like add but moves to absolute position
        return self.add(posargs,fval-self.rawfinal(),animtype,ctrlargs,loopargs)

    def cut(self,animvar=None):
        if animvar: self.offset += self.data.pop(self.data.index(animvar))() #stop one AnimVar only (by 'name')
        else: self.offset = max(self.lb,min(self.ub,sum([av() for av in self.data],self.offset))); self.data = [] #stop all AnimVars

    def end(self,animvar=None):
        if animvar: self.offset += animvar.addend() and self.data.pop(self.data.index(animvar)).offset or 0 #end one AnimVar only (by 'name')
        else: self.offset = max(self.lb,min(self.ub,sum([av.offset for av in self.data if av.addend()],self.offset))); self.data = [] #end all AnimVars

    def final(self): return max(self.lb,min(self.ub,sum([av.offset for av in self.data if av.addend()],self.offset)))

    def rawfinal(self): return sum([av.offset for av in self.data if av.addend()],self.offset) #no clipping, for calculations that need the real final value

    def __call__(self): return max(self.lb,min(self.ub,sum([av() for av in self.data],self.offset)))

    def update(self,timestep=1.0):
        for av in self.data:
            av.update(timestep)
            if not av.active() and av.addend(): self.offset += av.offset
        self.data = [av for av in self.data if av.active()]

    def active(self): return bool(len(self.data))


class SpriteAnim:
    """This is the class you should care about. When we create sprites that can feasibly
       be animated in any way (position, rotation, scale, transparency, tint), use this class.

       Certain animation functions have cryptic arguments used at the lower levels.
       This section explains them. An arg=value means that a list of shorter length
       will have that default value. so posargs=[10,5,-1] has minix = 0.
           posargs = [maxix,startix=0,startdir=1,minix=0]
               index range = [minix,maxix) w/o bounce, [minix,maxix] w/ bounce
                   (minix is typically zero, as animation occurs from 0 to n frames)
               startix = start index (can be a fraction)
               startdir = increment value (usually use +1 or -1, or 0 to pause)
           ctrlargs = [bounce=False,repeat=False,active=True]
               bounce: change dir when pos is >= max (or <= min with repeat on)
               repeat: with bounce off, when pos >= max, go to min and continue
               active: self-maintained var indicating when to update LinCtr
           loopargs = [delay=0.0,tie=None,looptype=0]
               delay = time in frames to initially wait before animating.
               tie = an AnimVar, Var, or SpriteAnim to wait for. When it finishes animating, this var will update.
                   (the lower-level the var (AV < V < SA), the more likely an infinite wait is avoided.)
               ignore looptype: as of this version only 0 is supported, and is the default value."""

    def __init__(self,sprite):
        self.sprite = sprite
        self.ax = Var(sprite.x)
        self.ay = Var(sprite.y)
        self.arot = Var(sprite.rotation)
        self.ascale = Var(sprite.scale)
        self.aalpha = Var(sprite.opacity,0,255)
        r,g,b = sprite.color
        self.artint = Var(r,0,255)
        self.agtint = Var(g,0,255)
        self.abtint = Var(b,0,255)

        #list of all animvars to update every frame
        self.animlist = [self.ax,self.ay,self.arot,self.ascale,self.aalpha,self.artint,self.agtint,self.abtint]

    def draw(self): self.sprite.draw()

    def update(self,dt):
        for var in self.animlist:
            if var.active(): var.update(dt)
        self.sprite.x = self.ax()
        self.sprite.y = self.ay()
        self.sprite.rotation = self.arot()
        self.sprite.scale = self.ascale()
        self.sprite.opacity = self.aalpha()
        self.sprite.color = (self.artint(),self.agtint(),self.abtint())

    def active(self): #if the sprite needs to be updated, this will return True.
        for var in self.animlist:
            if var.active(): return True
        return False

    def move(self,cxamt,cyamt,posargs,animtype=0,ctrlargs=[],loopargs=[]): #returns a lower-level AnimVar, which doubles as a bool test or tie value
        rtn = self.ax.add(posargs,cxamt,animtype,ctrlargs,loopargs) #checks are in Var, so no motion is handled (returns None)
        return self.ay.add(posargs,cyamt,animtype,ctrlargs,loopargs) or rtn #returns None if both are None, otherwise a valid AnimVar

    def moveto(self,cxpos,cypos,posargs,animtype=0,ctrlargs=[],loopargs=[]):
        rtn = self.ax.movto(posargs,cxpos,animtype,ctrlargs,loopargs)
        return self.ay.movto(posargs,cypos,animtype,ctrlargs,loopargs) or rtn

    def rotate(self,cwdegs,posargs,animtype=0,ctrlargs=[],loopargs=[]): #rotates a certain number of degrees (additive)
        return self.arot.add(posargs,cwdegs,animtype,ctrlargs,loopargs)

    def rotto(self,cwdegpos,posargs,animtype=0,ctrlargs=[],loopargs=[]): #rotates to a certain angle (not additive)
        return self.arot.movto(posargs,cwdegpos,animtype,ctrlargs,loopargs)

    def scale(self,scale,posargs,animtype=0,ctrlargs=[],loopargs=[]): #scales to a value (not additive) (scale=1.0 is 100% scale)
        return self.ascale.movto(posargs,scale,animtype,ctrlargs,loopargs)

    def fadein(self,posargs,ctrlargs=[],loopargs=[]):
        return self.aalpha.movto(posargs,255,0,ctrlargs,loopargs)

    def fadeout(self,posargs,ctrlargs=[],loopargs=[]):
        return self.aalpha.movto(posargs,0,0,ctrlargs,loopargs)

    def tint(self,fill,posargs,animtype=0,ctrlargs=[],loopargs=[]):
        rtn = (fill[0] != None) and self.artint.movto(posargs,fill[0],animtype,ctrlargs,loopargs) or None
        rtn = (fill[1] != None) and self.agtint.movto(posargs,fill[1],animtype,ctrlargs,loopargs) or rtn
        rtn = (fill[2] != None) and self.abtint.movto(posargs,fill[2],animtype,ctrlargs,loopargs) or rtn
        return (len(fill) >= 4 and fill[3] != None) and self.aalpha.movto(posargs,fill[3],animtype,ctrlargs,loopargs) or rtn

    def addtint(self,fillamt,posargs,animtype=0,ctrlargs=[],loopargs=[]): #adds to tint (fill AnimVars are safe due to clipping mins/maxs)
        rtn = (fillamt[0] != None) and self.artint.add(posargs,fillamt[0],animtype,ctrlargs,loopargs) or None
        rtn = (fillamt[1] != None) and self.agtint.add(posargs,fillamt[1],animtype,ctrlargs,loopargs) or rtn
        rtn = (fillamt[2] != None) and self.abtint.add(posargs,fillamt[2],animtype,ctrlargs,loopargs) or rtn
        return (len(fillamt) >= 4 and fillamt[3] != None) and self.aalpha.add(posargs,fillamt[3],animtype,ctrlargs,loopargs) or rtn


class Widget:
    """"""

    DEG2RAD = math.pi/180.0 #for degree-based rotation interface

    def __init__(self,sprite,radius,tttext="...",ttwidth=7): #creates a root node. use root.attach(new node) to add a child node
        self.sprite = sprite
        self.sanim = SpriteAnim(sprite)

        #defaults for the root node. used more in children
        self.parent = None
        self.child = []
        self.length = Var(0)
        self.angle = Var(0)
        self.oldlen = self.oldangle = 0
        self.parx = self.pary = 0

        #collision test variables
        self.radius = radius
        self.selected = False

        #tooltip support
        self.tttext = tttext
        self.ttwidth = ttwidth

    def attach(self,cnode,angle,length):
        cnode.length.offset = length
        cnode.angle.offset = angle
        cnode.parent = self
        self.child.append(cnode)
        cnode.parx = self.sprite.x + length * math.cos(angle)
        cnode.pary = self.sprite.y + length * math.sin(angle)
        return cnode #returns the new attached node (so it can be created in the attach call, and still accessed after).

    def update(self,dt,force=False): #updates the widget hierarchy. only call on the root node.
        if self.length.active(): self.length.update(dt)
        length = self.length()
        if length != self.oldlen:
            self.oldlen = length
            force = True

        if self.angle.active(): self.angle.update(dt)
        angle = self.angle()
        #if self.parent: angle += self.parent.angle()
        if angle != self.oldangle or force: #recalculate offset from parent on angle or length change (or parent change)
            self.oldangle = angle
            angle *= self.DEG2RAD
            if self.parent:
                self.parx = self.parent.sprite.x + length * math.cos(angle)
                self.pary = self.parent.sprite.y + length * math.sin(angle)
            force = True

        if force or self.sanim.active(): #the SpriteAnim will overwrite parent offsets when updated, so make sure to add them back in.
            self.sanim.update(dt)
            self.sprite.x += self.parx
            self.sprite.y += self.pary
        for cn in self.child: cn.update(dt,True)

    def draw(self):
        self.sprite.draw()
        for cn in self.child: cn.draw()

    def hitenter(self,x,y):
        if ((self.sprite.x - x)**2 + (self.sprite.y - y)**2)**0.5 <= self.radius:
            self.selected = True
            return True
        else: #if not clicking on this widget, check its children
            for cn in self.child:
                if cn.hitenter(x,y): return True
        return False

    def hitexit(self,x,y):
        if self.selected:
            self.selected = False
            return True
        else:
            for cn in self.child:
                if cn.hitexit(x,y): return True
        return False

    """def hitenter(self,x,y):
        if ((self.sprite.x - x)**2 + (self.sprite.y - y)**2)**0.5 <= self.radius:
            self.selected = True
        else: #if not clicking on this widget, check its children
            for cn in self.child: cn.hitenter(x,y)

    def hitexit(self,x,y):
        if self.selected:
            self.selected = False
        else:
            for cn in self.child: cn.hitexit(x,y)"""

    def hitdrag(self,x,y,dx,dy):
        if self.selected:
            #print "dragging",self
            if self.parent: #children rotate around their parents
                newangle = math.atan2(y - self.parent.sprite.y,x - self.parent.sprite.x)
                self.angle.offset = newangle/self.DEG2RAD
                length = self.length()
                self.sprite.x -= self.parx; self.sprite.y -= self.pary
                self.parx = self.parent.sprite.x + length * math.cos(newangle)
                self.pary = self.parent.sprite.y + length * math.sin(newangle)
                self.sprite.x += self.parx; self.sprite.y += self.pary
            else: #root pans. use parx,pary to store the offset, since they aren't used for anything else in the root
                self.parx += dx; self.pary += dy
                self.sprite.x += dx; self.sprite.y += dy
            return True
        else:
            for cn in self.child:
                if cn.hitdrag(x,y,dx,dy): return True
        return False

    def gethitobj(self,x,y):
        if ((self.sprite.x - x)**2 + (self.sprite.y - y)**2)**0.5 <= self.radius:
            return self
        else: #if not clicking on this widget, check its children
            for cn in self.child:
                val = cn.gethitobj(x,y)
                if val: return val
        return None

    def deletehierarchy(self):
        self.sprite.delete()
        for cn in self.child: cn.deletehierarchy()


class FuncWidget(Widget):
    """A widget that will call a function when left-clicked."""

    def __init__(self,sprite,radius,tttext,ttwidth,func,args=[]):
        Widget.__init__(self,sprite,radius,tttext,ttwidth)
        self.func = func #func is a function that takes one argument (the widget that called it), for animation purposes
        self.fargs = args

    def hitexit(self,x,y):
        if self.selected:
            self.func(self,*self.fargs) #call the function when releasing the mouse button (std practice)
            self.selected = False
            return True
        else:
            for cn in self.child:
                if cn.hitexit(x,y): return True
        return False

    def hitdrag(self,x,y,dx,dy): #function widgets should not be draggable (conflicting usage)
        if not self.selected: #if not clicking on this widget, check its children
            for cn in self.child:
                if cn.hitdrag(x,y,dx,dy): return True
        return False

class RotateWidget(Widget):

    def __init__(self,sprite,radius,tttext,ttwidth,func):
        Widget.__init__(self,sprite,radius,tttext,ttwidth)
        self.func = func #func is the function called to rotate the image in the background

    def hitenter(self,x,y):
        if ((self.sprite.x - x)**2 + (self.sprite.y - y)**2)**0.5 <= self.radius:
            self.selected = True
            return True
        else: #if not clicking on this widget, check its children
            for cn in self.child:
                if cn.hitenter(x,y): return True
        return False

    def hitdrag(self,x,y,dx,dy):
        if self.selected:
            newangle = math.atan2(y - self.parent.sprite.y,x - self.parent.sprite.x)
            newadeg = (newangle/self.DEG2RAD) % 90
            #if newadeg < 10 or newadeg > 80:
            newangle = ((newangle/self.DEG2RAD + 45)//90*90)*self.DEG2RAD
            diffangle = (newangle - self.angle.offset*self.DEG2RAD)/self.DEG2RAD
            #print newangle, diffangle, self.angle.offset
            self.func(diffangle)
            self.angle.offset = newangle/self.DEG2RAD
            length = self.length()
            self.sprite.x -= self.parx; self.sprite.y -= self.pary
            self.parx = self.parent.sprite.x + length * math.cos(newangle)
            self.pary = self.parent.sprite.y + length * math.sin(newangle)
            self.sprite.x += self.parx; self.sprite.y += self.pary
            for wid in self.parent.child:
                if wid != self: wid.angle.offset += diffangle
        else:
            for cn in self.child:
                if cn.hitdrag(x,y,dx,dy): return True
        return False

    def hitexit(self,x,y):
        if self.selected:
            #self.func(self,*self.fargs) #call the function when releasing the mouse button (std practice)
            self.selected = False
            for i in xrange(len(self.parent.child)):
                wid = self.parent.child[i]
                curangle = wid.angle.offset
                newangle = i*360/6.
                if newangle < 0: newangle += 360
                if newangle - curangle > 180: newangle -= 360
                if newangle - curangle < -180: newangle += 360
                wid.angle.movto([0.3],newangle,2)
            return True
        else:
            for cn in self.child:
                if cn.hitexit(x,y): return True
        return False


class EditWidget(Widget):

    def hitdrag(self,x,y,dx,dy): #function widgets should not be draggable (conflicting usage)
        if not self.selected: #if not clicking on this widget, check its children
            for cn in self.child:
                if cn.hitdrag(x,y,dx,dy): return True
        return False


if __name__ == "__main__":
    window = pyglet.window.Window(640,480)

    image = pyglet.image.load("Eye-con.png")
    hw,hh = image.width/2,image.height/2
    image.anchor_x = hw; image.anchor_y = hh

    sprite = pyglet.sprite.Sprite(image,320,240)
    sprite.scale = 80/340.
    sprite2 = pyglet.sprite.Sprite(image)
    sprite2.scale = 50/340.
    sprite3 = pyglet.sprite.Sprite(image)
    sprite3.scale = 20/340.
    sprite4 = pyglet.sprite.Sprite(image)
    sprite4.scale = 50/340.

    widget = Widget(sprite,40)
    wlvl1 = widget.attach(Widget(sprite2,25),30,95)
    w2lvl1 = widget.attach(Widget(sprite4,25),105,95)
    wlvl2 = wlvl1.attach(Widget(sprite3,10),200,40)

    #animation test (disable when testing collision dragging)
    wlvl1.angle.add([5],360,0,[False,True])
    w2lvl1.angle.add([5],360,0,[False,True])
    wlvl2.angle.add([5],-360,0,[False,True])

    @window.event
    def on_mouse_press(x,y,button,modifiers):
        widget.hitenter(x,y)
    @window.event
    def on_mouse_drag(x,y,dx,dy,buttons,modifiers):
        widget.hitdrag(x,y,dx,dy)
    @window.event
    def on_mouse_release(x,y,button,modifiers):
        widget.hitexit(x,y)

    def mainupdate(dt):
        widget.update(dt)
        window.clear()
        widget.draw()
    pyglet.clock.schedule_interval(mainupdate,1/60.0)
    pyglet.app.run()

'''if __name__ == "__main__":
    window = pyglet.window.Window(640,480)
    image = pyglet.image.load("PixelEye.png")
    image.anchor_x = image.anchor_y = 65 #scale and rotate anims are centered around the image's anchor point (anchor_x,anchor_y)
    sprite = pyglet.sprite.Sprite(image,65+80,65+0)
    sprite.rotation = -90
    sprite.opacity = 64
    spriteanim = SpriteAnim(sprite)
    #move by (350,0) in 2 seconds w/ trigonometric smoothness on start and end (6),
    #and change direction when done (bounce=True), and repeat when done with one full loop (repeat=True)
    spriteanim.move(350,0,[2],6,[True,True])
    #move by (0,350) in 2 seconds, starting at the frame (1) second into the animation, etc. etc.
    #because these animations are using bounce=True, they take twice as long to complete one loop (4 seconds)
    spriteanim.move(0,350,[2,1],6,[True,True])
    #rotate by 360 degrees clockwise in two seconds, with a linear animation, repeating when done.
    #since the rotation starts and ends at the same degree value (0,360), no noticable jumps occurs on repeat.
    spriteanim.rotate(360,[2],0,[False,True])
    #scale to 150% and back (bounce) in 2 seconds, quadratically smooth on start and end, repeating when done.
    #scale will move to the value specified, even if other temporary scale animations are currently running.
    spriteanim.scale(1.5,[1],3,[True,True])
    #fadein and fadeout are simple functions to fully bring an image in or out. bounce and repeat make them flash.
    spriteanim.fadein([4],[True,True])
    #you can have multiple animations running at the same time. this jitters the x motion around the circle a bit
    #spriteanim.move(20,0,[0.2],0,[True,True])

    #spr2 = pyglet.sprite.Sprite(image,200,0) #you need a new sprite for each new spriteanim, but not a new image
    #sa2 = SpriteAnim(spr2)

    def mainupdate(dt):
        spriteanim.update(dt)
        window.clear()
        spriteanim.draw()
    pyglet.clock.set_fps_limit(60)
    pyglet.clock.schedule_interval(mainupdate,1/60.0)
    pyglet.app.run()'''

