import maya.cmds as cmds
import maya.OpenMaya as OpenMaya
import maya.OpenMayaUI as OpenMayaUI
import maya.OpenMayaRender as OpenMayaRender

from PySide import QtCore, QtGui
from shiboken import wrapInstance

import math 

class KeyboardEvents(QtCore.QObject):
    def __init__(self, view3D = OpenMayaUI.M3dView.active3dView()):
        super(KeyboardEvents, self).__init__()

        self.view = view3D
        self.K_Ctrl = False
        self.K_Esc = False
        self.K_Shift = False
        self.K_Alt = False
        self.K_Enter = False
    
    def eventFilter(self, obj, event):

        if event.type() == QtCore.QEvent.Type.KeyPress:

            if event.key() == QtCore.Qt.Key_Control:
                self.K_Ctrl = True
                self.view.refresh(True, True)

            if event.key() == QtCore.Qt.Key_Shift:
                self.K_Shift = True
                self.view.refresh(True, True)

            if event.key() == QtCore.Qt.Key_Escape:
                self.K_Esc = True
                self.view.refresh(True, True)

            if event.key() == QtCore.Qt.Key_Alt:
                self.K_Alt = True
                self.view.refresh(True, True)

            if event.key() == QtCore.Qt.Key_Return:
                self.K_Enter = True
                self.view.refresh(True, True)


        if event.type() == QtCore.QEvent.Type.KeyRelease:

            if event.key() == QtCore.Qt.Key_Control:
                self.K_Ctrl = False
                self.view.refresh(True, True)

            if event.key() == QtCore.Qt.Key_Shift:
                self.K_Shift = False
                self.view.refresh(True, True)

            if event.key() == QtCore.Qt.Key_Escape:
                self.K_Esc = False
                self.view.refresh(True, True)

            if event.key() == QtCore.Qt.Key_Alt:
                self.K_Alt = False
                self.view.refresh(True, True)

            if event.key() == QtCore.Qt.Key_Return:
                self.K_Enter = False
                self.view.refresh(True, True)

class MouseEvents(QtCore.QObject):
    def __init__(self, view3D = OpenMayaUI.M3dView.active3dView()):
        super(MouseEvents, self).__init__()

        self.view = view3D
        self.M_Button_Left = False
        self.M_Button_Right = False
        self.M_Move = False
        self.M_posX = 0
        self.M_posY = 0
        self.editMode = False

    
    def eventFilter(self, obj, event):

        if event.type() == QtCore.QEvent.Type.MouseButtonPress:

            if event.button() == 1:
                self.M_posX = event.pos().x()
                self.M_posY = event.pos().y()
                self.M_Button_Left = True
                self.view.refresh(True, True)

            if event.button() == 2:
                self.M_posX = event.pos().x()
                self.M_posY = event.pos().y()
                self.M_Button_Right = True
                self.view.refresh(True, True)

        if event.type() == QtCore.QEvent.Type.MouseButtonRelease:

            if event.button() == 1:
                self.M_Button_Left = False
                self.view.refresh(True, True)

            if event.button() == 2:
                self.M_Button_Right = False
                self.view.refresh(True, True)

        if event.type()==QtCore.QEvent.Type.MouseMove:

            self.M_posX=event.pos().x()
            self.M_posY=event.pos().y()
            self.M_Move=True
            self.view.refresh(True, True)

        if self.editMode:
            return True

class ViewportPainter(object):

    def __init__ (self):

        self.callback = None
        self.currentModelPanel = None
        self.unit = 1.0
        self.glFT = None
        self.qt_Active_View = None
        self.qt_Maya_Window = None

        self.view3D = OpenMayaUI.M3dView.active3dView()
        self.userKeyboardEvents = KeyboardEvents(self.view3D)
        self.userMouseEvents = MouseEvents(self.view3D)

        self.initializeGL()
        self.initializeCallback()


    def initializeGL(self):

        #scene measure units
        unit = cmds.currentUnit(q=1, linear=1)
        if unit == "m":
            self.unit = float(self.unit) * 100.0

        self.glFT = OpenMayaRender.MHardwareRenderer.theRenderer().glFunctionTable()

    def initializeCallback(self):

        #get current model panel
        self.currentModelPanel = cmds.getPanel(wf = 1)
        if "modelPanel" not in self.currentModelPanel:
            self.currentModelPanel = cmds.getPanel(vis = 1)
            for i in self.currentModelPanel:
                if "modelPanel" in i:
                    self.currentModelPanel = i


        #try removing old callbacks from memory
        try:
            OpenMayaUI.MUiMessage.removeCallback(self.callBack)
        except:
            pass

        #create a callback that is registered after a frame is drawn with a 3D content but before 2D content
        self.callback = OpenMayaUI.MUiMessage.add3dViewPostRenderMsgCallback(self.currentModelPanel, self.update)
        self.view3D.refresh(True, True)

        #create QT maya window event filter
        main_window_ptr = OpenMayaUI.MQtUtil.mainWindow()
        self.qt_Maya_Window = wrapInstance(long(main_window_ptr), QtCore.QObject)
        self.qt_Maya_Window.installEventFilter(self.userKeyboardEvents) 

        #create viewport event filter
        active_view_ptr = self.view3D.widget()
        self.qt_Active_View = wrapInstance(long(active_view_ptr), QtCore.QObject)
        self.qt_Active_View.installEventFilter(self.userMouseEvents)

        cmds.inViewMessage( amg='<hl>Tool:</hl> Use <hl>"Esc"</hl> to cancel the tool', pos='botLeft', fade=True )

        print "Initialized..."

    def uninitializeCallback(self):
        OpenMayaUI.MUiMessage.removeCallback(self.callback) #remove 3dView Render Callback

        self.qt_Maya_Window.removeEventFilter(self.userKeyboardEvents) #remove QT Callback
        self.qt_Active_View.removeEventFilter(self.userMouseEvents) #remove QT Callback

        OpenMayaUI.M3dView.active3dView().scheduleRefresh()   

        print "Uninitialized..."



    def getMouseIntersect(self):

        sourcePnt = OpenMaya.MPoint(0,0,0)
        rayDir = OpenMaya.MVector(0,0,0)
        maximumDistance = 9999999999
        viewHeight = self.view3D.portHeight()
        
        hitNormal = OpenMaya.MVector()
        
        intersectedObject = None
        intersectedPoint = OpenMaya.MFloatPoint()
        intersectedFace = 0

        hitFace = OpenMaya.MScriptUtil()
        hitFace.createFromInt(0)
        hitFacePtr = hitFace.asIntPtr()

        hitDistance = OpenMaya.MScriptUtil(0.0)
        hitDistancePtr = hitDistance.asFloatPtr()

        self.view3D.viewToWorld(int(self.userMouseEvents.M_posX), int(viewHeight - self.userMouseEvents.M_posY), sourcePnt, rayDir)

        
        direction = OpenMaya.MFloatVector(rayDir.x, rayDir.y, rayDir.z).normal()

        iter = OpenMaya.MItDependencyNodes(OpenMaya.MFn.kMesh)

        while not iter.isDone():

            node =iter.thisNode()
            dagPath = OpenMaya.MDagPath.getAPathTo(node)

            hitPoint = OpenMaya.MFloatPoint()
            source = OpenMaya.MFloatPoint(sourcePnt.x, sourcePnt.y, sourcePnt.z)
            direction = OpenMaya.MFloatVector(direction.x,direction.y,direction.z)

            if dagPath.isVisible():
                mesh = OpenMaya.MFnMesh(dagPath)
                intersected = mesh.closestIntersection(source, direction, None, None, False, OpenMaya.MSpace.kWorld, 9999999999, True, None, hitPoint, hitDistancePtr, hitFacePtr, None, None, None, 0.0001)
                
                if intersected:
                    intersectionDistance = hitDistance.getFloat(hitDistancePtr)
                    if intersectionDistance < maximumDistance:
                        maximumDistance = intersectionDistance
                        intersectedPoint = hitPoint
                        intersectedFace =  OpenMaya.MScriptUtil(hitFacePtr).asInt()
                        mesh.getClosestNormal(OpenMaya.MPoint(intersectedPoint),hitNormal,OpenMaya.MSpace.kWorld)
                        intersectedObject = dagPath.fullPathName()

            iter.next()

        if intersectedPoint.x + intersectedPoint.y + intersectedPoint.z == 0:
            return None, None, None
        else:
            return intersectedPoint, intersectedFace, intersectedObject


    def update(self, *args):
        pass