import os
from maya import OpenMaya, OpenMayaUI, cmds

from .create import SplineIK
from .utils.controlShape import CONTROL_SHAPES
from .utils.colour import COLOURS_FROM_STRING


# ----------------------------------------------------------------------------


# import pyside, do qt version check for maya 2017 >
qtVersion = cmds.about(qtVersion=True)
if qtVersion.startswith("4") or type(qtVersion) not in [str, unicode]:
    from PySide.QtGui import *
    from PySide.QtCore import *
    import shiboken
else:
    from PySide2.QtGui import *
    from PySide2.QtCore import *
    from PySide2.QtWidgets import *
    import shiboken2 as shiboken


# ----------------------------------------------------------------------------


FONT = QFont()
FONT.setFamily("Consolas")

BOLT_FONT = QFont()
BOLT_FONT.setFamily("Consolas")
BOLT_FONT.setWeight(100)  


# ----------------------------------------------------------------------------


def mayaWindow():
    """
    Get Maya's main window.
    
    :rtype: QMainWindow
    """
    window = OpenMayaUI.MQtUtil.mainWindow()
    window = shiboken.wrapInstance(long(window), QMainWindow)
    
    return window  


# ----------------------------------------------------------------------------


def divider(parent):
    """
    Create divider ui widget.
    
    :param QWidget parent:
    :rtype: QFrame
    """
    line = QFrame(parent)
    line.setFrameShape(QFrame.HLine)
    line.setFrameShadow(QFrame.Sunken)
    return line


# ----------------------------------------------------------------------------


def getIconPath(name):
    """
    Get an icon path based on file name. All paths in the XBMLANGPATH variable
    processed to see if the provided icon can be found.

    :param str name:
    :return: Icon path
    :rtype: str/None
    """
    for path in os.environ.get("XBMLANGPATH").split(os.pathsep):
        iconPath = os.path.join(path, name)
        if os.path.exists(iconPath):
            return iconPath.replace("\\", "/")


# ----------------------------------------------------------------------------


class LabelWidget(QWidget):
    def __init__(self, parent, label, widget):
        QWidget.__init__(self, parent)
        
        layout = QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(3)
        
        # create label
        self.label = QLabel(self)
        self.label.setText(label)
        self.label.setFont(FONT)
        layout.addWidget(self.label)
        
        # create line edit
        self.widget = widget(self)
        self.widget.setFont(FONT)
        layout.addWidget(self.widget)


class InputWidget(LabelWidget):
    def __init__(self, parent, label):
        LabelWidget.__init__(self, parent, label, QLineEdit)
  
    def text(self):
        return self.widget.text()


class ComboBoxWidget(LabelWidget):
    def __init__(self, parent, label, items, defaultItem=None):
        LabelWidget.__init__(self, parent, label, QComboBox)
        
        # add items
        self.widget.addItems(items)
        
        # set default
        index = items.index(defaultItem)
        self.widget.setCurrentIndex(index)
  
    def currentText(self):
        return self.widget.currentText()


class SpinBoxWidget(LabelWidget):
    def __init__(self, parent, label, defaultValue, minValue, maxValue):
        LabelWidget.__init__(self, parent, label, QSpinBox)
        self.widget.setMinimum(minValue)
        self.widget.setMaximum(maxValue)
        self.widget.setValue(defaultValue)
  
    def value(self):
        return self.widget.value()


# ----------------------------------------------------------------------------


class SelectWidget(QWidget):
    released = Signal()
    def __init__(self, parent, label, button):
        QWidget.__init__(self, parent)
        
        layout = QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(3)
        
        # create label
        self.label = QLabel(self)
        self.label.setText(label)
        self.label.setFont(FONT)
        self.label.setFixedWidth(75)
        layout.addWidget(self.label)
        
        # create line edit
        self.edit = QLineEdit(self)
        self.edit.setFont(FONT)
        layout.addWidget(self.edit)
        
        # create label
        self.button = QPushButton(self)
        self.button.setText(button)
        self.button.setFont(FONT)
        self.button.setFixedWidth(100)
        self.button.released.connect(self.released.emit)
        layout.addWidget(self.button)
        
    # ------------------------------------------------------------------------
        
    def setText(self, text):
        self.edit.setText(text)
        
    def text(self):
        return self.edit.text()


# ----------------------------------------------------------------------------


class ControlWidget(QWidget):
    def __init__(self, parent, label, defaultShape, defaultColour):
        QWidget.__init__(self, parent)

        # variables
        shapes = CONTROL_SHAPES.keys()
        shapes.sort()
        
        colours = COLOURS_FROM_STRING.keys()
        colours.sort()
        
        # create layout
        layout = QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(10)
        
        # create label
        self.label = QLabel(self)
        self.label.setText(label)
        self.label.setFont(FONT)
        layout.addWidget(self.label)

        # create shape
        self.shape = ComboBoxWidget(
            self,
            "Shape:", 
            shapes, 
            defaultShape
        )
        self.shape.label.setFixedWidth(50)
        layout.addWidget(self.shape)
        
        # create colour
        self.colour = ComboBoxWidget(
            self,
            "Colour:", 
            colours, 
            defaultColour
        )
        self.colour.label.setFixedWidth(50)
        layout.addWidget(self.colour)

    # ------------------------------------------------------------------------

    def getShape(self):
        return self.shape.currentText()

    def getColour(self):
        return self.colour.currentText()


# ----------------------------------------------------------------------------


class SplineIKWidget(QWidget):
    def __init__(self, parent):
        QWidget.__init__(self, parent)
        
        # set ui
        self.setParent(parent)        
        self.setWindowFlags(Qt.Window)  

        self.setWindowTitle("Spline IK")      
        self.setWindowIcon(QIcon(getIconPath("SIK_icon.png")))
        self.resize(475, 250)
        
        # create layout
        layout = QVBoxLayout(self)
        layout.setContentsMargins(5, 5, 5, 5)
        layout.setSpacing(3)
        
        # create name input
        self.name = InputWidget(self, "Name")
        self.name.label.setFixedWidth(75)
        layout.addWidget(self.name)
        
        # create curve selector
        self.curve = SelectWidget(self, "Curve", "Select Curve")
        self.curve.released.connect(self.getSelection)
        layout.addWidget(self.curve)
        
        # add divider
        div = divider(self)
        layout.addWidget(div)
        
        # add num joints
        self.joint = SpinBoxWidget(self, "Num Joints", 20, 3, 500)
        layout.addWidget(self.joint)
        
        # add divider
        div = divider(self)
        layout.addWidget(div)
        
        # create vectors
        axis = ["x", "y", "z"]
        
        self.forward = ComboBoxWidget(self, "Forward Axis", axis, "x")
        layout.addWidget(self.forward)
        
        self.up = ComboBoxWidget(self, "Up Axis", axis, "y")
        layout.addWidget(self.up)
        
        self.worldUp = ComboBoxWidget(self, "World Up Axis", axis, "y")
        layout.addWidget(self.worldUp)

        # add divider
        div = divider(self)
        layout.addWidget(div)
        
        self.root = ControlWidget(self, "Root Control", "triangle", "light blue")
        layout.addWidget(self.root)
        
        self.slide = ControlWidget(self, "Slide Control", "sphere", "red")
        layout.addWidget(self.slide)
        
        self.tweak = ControlWidget(self, "Tweak Control", "sphere", "yellow")
        layout.addWidget(self.tweak)
        
        self.tangent = ControlWidget(self, "Tangent Control", "cube", "white")
        layout.addWidget(self.tangent)
        
        # add divider
        div = divider(self)
        layout.addWidget(div)
        
        # create orient
        option = ["Yes", "No"]
        self.orientRoot = ComboBoxWidget(self, "Orient Root to Curve", option, "No")
        layout.addWidget(self.orientRoot)
        
        self.orient = ComboBoxWidget(self, "Orient to Curve", option, "Yes")
        layout.addWidget(self.orient)
        
        # add divider
        div = divider(self)
        layout.addWidget(div)
        
        # create button
        create = QPushButton(self)
        create.pressed.connect(self.doCreate)
        create.setText("Create")
        create.setFont(FONT)
        layout.addWidget(create)
        
    # ------------------------------------------------------------------------    
        
    def getSelection(self):
        """
        Get the current selection and see if the shapes of the first instance
        of the selection are of type 'nurbsCurve' or 'bezierCurve', if the
        criteria are met the line edit of the curve selection widget is 
        updated. If the criteria are not met a ValueError will be raised.
        
        :raises ValueError: if the selection criteria are not met.
        """
        # get selection
        selection = cmds.ls(sl=True)
        if not selection:
            raise ValueError("No selection found!")
         
        # check shapes ( exist )
        shapes = cmds.listRelatives(selection[0], s=True) or []
        if not shapes:
            raise ValueError("No shapes found in selection!")
        
        # check shapes
        for shape in shapes:
            if cmds.nodeType(shape) not in ["nurbsCurve", "bezierCurve"]:
                raise ValueError(
                    "Shapes are not of type 'nurbsCurve' or 'bezierCurve'!"
                )

        # set text
        self.curve.setText(selection[0])
        
    # ------------------------------------------------------------------------
        
    def doCreate(self):
        """
        Read the values of the ui and create a spline ik, if the creation is 
        succesfull the root control of the setup will be created. A ValueError
        will be raised if the input field is empty or no curve is selected.
        
        :raises ValueError: if the input or curve field are empty
        """
        # validate name
        name = self.name.text()
        if not name:
            raise ValueError("No name specified!")

        # validate curve
        curve = self.curve.text()
        if not curve:
            raise ValueError("No curve specified!")

        # create spline ik object
        ik = SplineIK()

        # set control shape and colour
        ik.rootControlShape = self.root.getShape()
        ik.rootControlColour = self.root.getColour()
        ik.slideControlShape = self.slide.getShape()
        ik.slideControlColour = self.slide.getColour()
        ik.controlShape = self.tweak.getShape()
        ik.controlColour = self.tweak.getColour()
        ik.tangentControlShape = self.tangent.getShape()
        ik.tangentControlColour = self.tangent.getColour()
        
        # set orientation
        orient = True if self.orientRoot.currentText() == "Yes" else False
        ik.orientRootToCurve = orient
        
        orient = True if self.orient.currentText() == "Yes" else False
        ik.orientToCurve = orient

        # create spline ik
        control = ik.create(
            name,
            curve,
            self.joint.value(),
            forwardDirection=self.forward.currentText(),
            upDirection=self.up.currentText(),
            worldUpDirection=self.worldUp.currentText(),
        )
        
        # select root
        cmds.select(control)


# ----------------------------------------------------------------------------


def show():
    dialog = SplineIKWidget(mayaWindow())
    dialog.show()