"""
A generic Jam-o-Drum input interface for the Jam-o-Drum that uses the OptiPAC
for both spinners and pads.

@author: U{Ben Buchwald <bb2@alumni.cmu.edu>}
Last Updated: 2/27/2006
"""

from direct.showbase.DirectObject import DirectObject

class JamoDrum(DirectObject):
    """
    Class representing input from a Jam-o-Drum. To handle Jam-o-Drum input 
    accept the Panda messages JOD_SPIN_x and JOD_HIT_x where x is a number between
    0 and 3 for the 4 stations. Spin messages also pass a parameter which is the
    angle spun in degrees. Hit messages also pass a parameter which is the force
    the pad was hit with in the range 0.0-1.0 (will probably be fairly low). With
    or without actual Jam-o-Drum hardware this class will automatically respond
    to the keys (j,k,l),(s,d,f),(w,e,r), and (u,i,o) corresponding to spin left 10
    degrees, hit with full force, and spin right 10 degrees respectively for each
    of the stations. You must call L{poll} periodically to receive input from the
    real Jam-o-Drum hardware.
    """
    def __init__(self,useJOD=None,mouseWindow=None,stationMice=[(2,0),(2,1),(1,0),(1,1)],spinnerRatio=8.71):
        """
        @keyword useJOD: connected to actual drumpads and spinners to read from (default: read from config.prc)
        @type useJOD: bool
        @keyword mouseWindow: Panda GraphicsWindow to read spinner data from (default: base.win)
        @type mouseWindow: C{GraphicsWindow}
        @keyword stationMice: list of 4 tuples of mouse index and axis/button (0=x/left, 1=y/rigth) to read for each spinner/pad
        @type stationMice: [(int,int),(int,int),(int,int),(int,int)]
        @keyword spinnerRatio: ratio of spinner size to encoder size
        @type spinnerRatio: float
        """
        if (useJOD==None):
            useJOD = base.config.GetBool("want-jamodrum",False)
        self.useJOD = useJOD
        self.mouseWindow = mouseWindow or base.win
        self.stationMice = stationMice
        self.spinnerRatio = spinnerRatio
        
        self.lastPos = [0,0,0,0]
        
        if (self.useJOD):
            for i in range(len(self.stationMice)):
                mouse = self.stationMice[i]
                self.accept("mousedev%d-mouse%d"%(mouse[0],mouse[1]*2+1),self.hit,[i,1.0])
        
        self.simulate()

        if (self.useJOD):
            props = base.win.getProperties()
            props.setZOrder(2)
            base.win.requestProperties(props)
    # end __init__
    
    def simulate(self,spin=10.0,hit=1.0):
        """
        Accept keyboard keys to simulate Jam-o-Drum input.
        
        @keyword spin: degrees to spin for each keystroke (default: 10.0)
        @type spin: float
        @keyword hit: force to hit for each keystroke (default: 1.0)
        @type hit: float
        """
        self.accept('k',self.hit,[0,hit])
        self.accept('d',self.hit,[1,hit])
        self.accept('e',self.hit,[2,hit])
        self.accept('i',self.hit,[3,hit])

        self.accept('j',self.spin,[0,spin])
        self.accept('l',self.spin,[0,-spin])
        self.accept('s',self.spin,[1,spin])
        self.accept('f',self.spin,[1,-spin])
        self.accept('w',self.spin,[2,-spin])
        self.accept('r',self.spin,[2,spin])
        self.accept('u',self.spin,[3,-spin])
        self.accept('o',self.spin,[3,spin])
    # end simulate
    
    def poll(self):
        """
        Call this each frame to poll actual drumpads and spinners for input.
        If input occurs messages will be sent.
        """
        if (not self.useJOD):
            return
        
        for i in range(4):
            mouse = self.stationMice[i]
            if (mouse[0]<self.mouseWindow.getNumInputDevices()):
                if (mouse[1]):
                    pos = self.mouseWindow.getPointer(mouse[0]).getY()
                else:
                    pos = self.mouseWindow.getPointer(mouse[0]).getX()
                diff = pos-self.lastPos[i]
                self.lastPos[i] = pos
                if (diff!=0):
                    self.spin(i,360.0*diff/256.0/self.spinnerRatio)
    # end poll
    
    def spin(self,station,angle):
        """
        Sends a JOD_SPIN_<station> message
        """
        messenger.send("JOD_SPIN_"+str(station),[angle])
    # end spin
    
    def hit(self,station,force):
        """
        Sends a JOD_HIT_<station> message
        """
        messenger.send("JOD_HIT_"+str(station),[force])
    # end hit
    
# end class JamoDrum
