import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject
from pandac.PandaModules import *
from direct.task import Task

import socket

IP = "127.0.0.1"
PORT = 9050
SENDPORT = 9051
BUFLEN = 128
# BUFFLEN was 32 and was working fine until the 4/9 Vista update.  Now the float strings seem to be longer.

class Wiimote(DirectObject):
    
    # Set up local storage for values from wiimote
    # change this if you are taking in other fields
    def __init__(self):
        self.IRsFound = [False, False, False, False]
        self.NormIRX = [0,0,0,0]
        self.NormIRY = [0,0,0,0]
        
        self.NormAccelX = 0
        self.NormAccelY = 0
        self.NormAccelZ = 0
        
        # a/b/up/left/down/right/1/2/+/-/home
        self.Buttons = [False, False, False, False, False, False, False, False, False, False, False]
        
        self.NCNormAccelX = 0
        self.NCNormAccelY = 0
        self.NCNormAccelZ = 0
        
        self.NCJoyX = 0
        self.NCJoyY = 0
        # C/Z
        self.NCButtons = [False, False]
        
        # a/b/x/y/up/left/down/right/ZL/ZR/+/-/home
        self.ClassicButtons = [False, False, False, False, False, False, False, False, False, False, False, False, False]
        self.ClassicTriggerL = 0
        self.ClassicTriggerR = 0
        
        # callback for when new wiimote data comes in
        self.callback=None
        
        # for when no wiimote is available
        self.useMouse = False
        self.emulatedLED = 0
    
    # Set up connection to C# Wiimote listener over UDP
    def initUDP(self):     
        self.server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
        self.server.bind(('', PORT))
        
        sendserver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
        
        message=""
        print "waiting for first contact\n"
        # since UDP is not order-preserving, wait out the one handshake message which will screw up our real-data parser
        while message!="I HAS MANY DATAS. U WANT?": 
            (message, address) = self.server.recvfrom(BUFLEN)
        print "sending reply\n"
        #sendserver.sendto("IM IN UR PORT RECEIVING UR PACKETS.", (IP, SENDPORT))
        sendserver.sendto("I CAN HAS DATAS?", (IP, SENDPORT))
        
        print "receiving data...\n"
        # now that we've handshook, set to nonblocking so our receive
        # task doesn't lock the whole app.
        # just remember to try/catch on future transactions
        self.server.setblocking(False) 
        
        #(message, address) = self.server.recvfrom(BUFLEN)
        #print 'Received packet from %s:%d' % (address[0], address[1])
        #print 'Data: %s' % message
        
    # sets up event recognition for mouse control
    # (in case no wiimote is available)
    def initMouse(self):
        self.useMouse = True
        self.accept("1",self.setLED1)
        self.accept("2",self.setLED2)
        self.accept("3",self.setLED3)
        self.accept("4",self.setLED4)
        self.accept("mouse1",self.setClick)
        self.accept("mouse1-up",self.clearClick)
    
    def setLED1(self):
        self.emulatedLED = 0
        self.IRsFound[self.emulatedLED] = False
    def setLED2(self):
        self.emulatedLED = 1
        self.IRsFound[self.emulatedLED] = False
    def setLED3(self):
        self.emulatedLED = 2
        self.IRsFound[self.emulatedLED] = False
    def setLED4(self):
        self.emulatedLED = 3
        self.IRsFound[self.emulatedLED] = False
    def setClick(self):
        self.IRsFound[self.emulatedLED] = True
        if self.callback is not None:
            self.callback()
    def clearClick(self):
        self.IRsFound[self.emulatedLED] = False
        if self.callback is not None:
            self.callback()
    
    # Specifies a callback method to be invoked when
    # new data arrives.  Since Panda already has an
    # event system, new code should accept
    # "newWiimoteData" events rather than setting
    # a callback.
    def setCallback(self,newCall=None):
        self.callback=newCall
        
    # Receives incoming packets and updates local state
    # change this if you are taking in other fields
    def receiveTask(self,task):
        #print "receive task called"
        if self.useMouse:
            if base.mouseWatcherNode.hasMouse():
                x=base.mouseWatcherNode.getMouseX()
                y=base.mouseWatcherNode.getMouseY()
                if self.NormIRX[self.emulatedLED]!=x or self.NormIRY[self.emulatedLED]!=y:
                    self.NormIRX[self.emulatedLED]=x
                    self.NormIRY[self.emulatedLED]=y
                    # this should be deprecated at some point, as the
                    # Panda messenger system is more appropriate
                    if self.callback is not None:
                        self.callback()
                    
                    messenger.send('newWiimoteData')
        else:
            failed = False
            changed = False
            while not failed:
                try:
                    (message, address) = self.server.recvfrom(BUFLEN)
                    #print 'Received packet from %s:%d' % (address[0], address[1])
                    #print 'Data: %s' % message
                   
                    args = message.split(" ")
                    # IR data packet
                    if args[0].isdigit():
                        dotIndex = int(args[0])
                        self.IRsFound[dotIndex] = (args[1]=="True")
                        self.NormIRX[dotIndex] = float(args[2])
                        self.NormIRY[dotIndex] = float(args[3])
                    # accelerometer data packet
                    elif args[0] == "a":
                        self.NormAccelX = float(args[1])
                        self.NormAccelY = float(args[2])
                        self.NormAccelZ = float(args[3])
                    # buttons data packet
                    elif args[0] == "b":
                        # a/b/up/left/down/right/1/2/+/-/home
                        self.Buttons[0] = (args[1]=="1")
                        self.Buttons[1] = (args[2]=="1")
                        self.Buttons[2] = (args[3]=="1")
                        self.Buttons[3] = (args[4]=="1")
                        self.Buttons[4] = (args[5]=="1")
                        self.Buttons[5] = (args[6]=="1")
                        self.Buttons[6] = (args[7]=="1")
                        self.Buttons[7] = (args[8]=="1")
                        self.Buttons[8] = (args[9]=="1")
                        self.Buttons[9] = (args[10]=="1")
                        self.Buttons[10] = (args[11]=="1")
                    # nunchuk acclelerometer packet
                    elif args[0] == "n":
                        self.NCNormAccelX = float(args[1])
                        self.NCNormAccelY = float(args[2])
                        self.NCNormAccelZ = float(args[3])
                    # nunchuk buttons & joystick packet
                    elif args[0] == "j":
                        self.NCJoyX = float(args[1])
                        self.NCJoyY = float(args[2])
                        # C/Z
                        self.NCButtons = [(args[3]=="True"), (args[4]=="True")]
                    # classic buttons packet
                    elif args[0] == "c":
                        # a/b/x/y/up/left/down/right/ZL/ZR/+/-/home
                        self.ClassicButtons[0] = (args[1]=="1")
                        self.ClassicButtons[1] = (args[2]=="1")
                        self.ClassicButtons[2] = (args[3]=="1")
                        self.ClassicButtons[3] = (args[4]=="1")
                        self.ClassicButtons[4] = (args[5]=="1")
                        self.ClassicButtons[5] = (args[6]=="1")
                        self.ClassicButtons[6] = (args[7]=="1")
                        self.ClassicButtons[7] = (args[8]=="1")
                        self.ClassicButtons[8] = (args[9]=="1")
                        self.ClassicButtons[9] = (args[10]=="1")
                        self.ClassicButtons[10] = (args[11]=="1")
                        self.ClassicButtons[11] = (args[12]=="1")
                        self.ClassicButtons[12] = (args[13]=="1")
                    # classic triggers packet
                    elif args[0] == "t":
                        self.ClassicTriggerL = float(args[1])
                        self.ClassicTriggerR = float(args[2])
                    changed=True
                except:
                    # probably just means there's no data to receive.
                    # if you're having problems, set up a formal except
                    # for error/herror/gaierror/timeout
                    failed=True
                    pass
                
                if changed:
                    # this should be deprecated at some point, as the
                    # Panda messenger system is more appropriate
                    if self.callback is not None:
                        self.callback()
                    
                    messenger.send('newWiimoteData')
            
            #print "continuing..."
        return Task.cont

#class wiimoteTest(DirectObject):
#    def __init__(self):
#       self.wm = Wiimote()
#        self.wm.initUDP()
#        self.wm.setCallback(self.onNewWiimoteData)
#        taskMgr.add(self.wm.receiveTask,"wiimote UDP receiver")
#        print "receive task added"
#        
#            
#    def onNewWiimoteData(self):
#        #print "callback called"
#        if self.wm.IRsFound[0]:
#            print "IR1 seen: %f, %f\n" % (self.wm.NormIRX[0],self.wm.NormIRY[0])
#        if self.wm.IRsFound[1]:
#            print "IR1 seen: %f, %f\n" % (self.wm.NormIRX[1],self.wm.NormIRY[1])
#        if self.wm.IRsFound[2]:
#            print "IR2 seen: %f, %f\n" % (self.wm.NormIRX[2],self.wm.NormIRY[2])
#        if self.wm.IRsFound[3]:
#            print "IR3 seen: %f, %f\n" % (self.wm.NormIRX[3],self.wm.NormIRY[3])
#        if self.wm.Buttons[0]:
#            print "A button detected\n"
#        if self.wm.NCButtons[0]:
#            print "nunchuk C button detected"
#        print "numchuk accel: %f %f %f\n" % (self.wm.NCNormAccelX,self.wm.NCNormAccelY,self.wm.NCNormAccelZ)
#        print "nunchuk joystick: %f %f\n" % (self.wm.NCJoyX, self.wm.NCJoyY)

#myTest = wiimoteTest()
#run()