#!/usr/bin/env python
'''
Demonstrate different mouse modes
'''

# from panda3d.core import loadPrcFileData
#
# loadPrcFileData("", "notify-level-x11display debug")
# loadPrcFileData("", "notify-level-windisplay debug")
#
# loadPrcFileData("", "load-display p3tinydisplay")

from panda3d.core import WindowProperties, TextNode
from direct.task.TaskManagerGlobal import taskMgr
from direct.gui.OnscreenText import OnscreenText
from direct.task import Task
from direct.showbase.ShowBase import ShowBase

import sys

class App(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        self.base = self
        self.setup()

    def genLabelText(self, text, i):
        text = OnscreenText(text = text, pos = (-1.3, .5-.05*i), fg=(0,1,0,1),
                      align = TextNode.ALeft, scale = .05)
        return text


    def setup(self):
        # Disable the camera trackball controls.
        self.disableMouse()

        # control mapping of mouse movement to box movement
        self.mouseMagnitude = 1

        self.rotateX, self.rotateY = 0, 0

        self.genLabelText("[0] Absolute mode, [1] Relative mode, [2] Confined mode", 0)

        self.base.accept('0', lambda: self.setMouseMode(WindowProperties.M_absolute))
        self.base.accept('1', lambda: self.setMouseMode(WindowProperties.M_relative))
        self.base.accept('2', lambda: self.setMouseMode(WindowProperties.M_confined))

        self.genLabelText("[C] Manually re-center mouse on each tick", 1)
        self.base.accept('C', lambda: self.toggleRecenter())
        self.base.accept('c', lambda: self.toggleRecenter())

        self.genLabelText("[S] Show mouse", 2)
        self.base.accept('S', lambda: self.toggleMouse())
        self.base.accept('s', lambda: self.toggleMouse())

        self.base.accept('escape', sys.exit, [0])

        self.mouseText = self.genLabelText("", 5)
        self.deltaText = self.genLabelText("", 6)
        self.positionText = self.genLabelText("", 8)

        self.lastMouseX, self.lastMouseY = None, None

        self.hideMouse = False

        self.setMouseMode(WindowProperties.M_absolute)
        self.manualRecenterMouse = True

        # make a box to move with the mouse
        self.model = self.loader.loadModel("box")
        self.model.reparentTo(self.render)

        self.cam.setPos(0, -5, 0)
        self.cam.lookAt(0, 0, 0)

        self.mouseTask = taskMgr.add(self.mouseTask, "Mouse Task")

    def setMouseMode(self, mode):
        print("Changing mode to %s" % mode)

        self.mouseMode = mode

        wp = WindowProperties()
        wp.setMouseMode(mode)
        self.base.win.requestProperties(wp)

        # these changes may require a tick to apply
        self.base.taskMgr.doMethodLater(0, self.resolveMouse, "Resolve mouse setting")

    def resolveMouse(self, t):
        wp = self.base.win.getProperties()

        actualMode = wp.getMouseMode()
        if self.mouseMode != actualMode:
            print("ACTUAL MOUSE MODE: %s" % actualMode)

        self.mouseMode = actualMode

        self.rotateX, self.rotateY = -.5, -.5
        self.lastMouseX, self.lastMouseY = None, None
        self.recenterMouse()

    def recenterMouse(self):
        self.base.win.movePointer(0,
              int(self.base.win.getProperties().getXSize() / 2),
              int(self.base.win.getProperties().getYSize() / 2))


    def toggleRecenter(self):
        print("Toggling re-center behavior")
        self.manualRecenterMouse = not self.manualRecenterMouse

    def toggleMouse(self):
        print("Toggling mouse visibility")

        self.hideMouse = not self.hideMouse

        wp = WindowProperties()
        wp.setCursorHidden(self.hideMouse)
        self.base.win.requestProperties(wp)

    def mouseTask(self, task):
        mw = self.base.mouseWatcherNode

        hasMouse = mw.hasMouse()
        if hasMouse:
            # get the window manager's idea of the mouse position
            x, y = mw.getMouseX(), mw.getMouseY()

            if self.lastMouseX is not None:
                # get the delta
                if self.manualRecenterMouse:
                    # when recentering, the position IS the delta
                    # since the center is reported as 0, 0
                    dx, dy = x, y
                else:
                    dx, dy = x - self.lastMouseX, y - self.lastMouseY
            else:
                # no data to compare with yet
                dx, dy = 0, 0

            self.lastMouseX, self.lastMouseY = x, y

        else:
            x, y, dx, dy = 0, 0, 0, 0

        if self.manualRecenterMouse:
            # move mouse back to center
            self.recenterMouse()
            self.lastMouseX, self.lastMouseY = 0, 0

        # scale position and delta to pixels for user
        w, h = self.win.getSize()

        self.mouseText.setText("Mode: {0}, Recenter: {1}  |  Mouse: {2}, {3}  |  hasMouse: {4}".format(
             self.mouseMode, self.manualRecenterMouse,
             int(x*w), int(y*h),
             hasMouse))
        self.deltaText.setText("Delta: {0}, {1}".format(
             int(dx*w), int(dy*h)))

        # rotate box by delta
        self.rotateX += dx * 10 * self.mouseMagnitude
        self.rotateY += dy * 10 * self.mouseMagnitude

        self.positionText.setText("Model rotation: {0}, {1}".format(
             int(self.rotateX*1000)/1000., int(self.rotateY*1000)/1000.))

        self.model.setH(self.rotateX)
        self.model.setP(self.rotateY)
        return Task.cont

app = App()
app.run()