"""
mjConvergeToLinear

Author: Mitchell Jao

Description: When run, keyframes selected will converge to the linearvalue between those keys

--------------

HOTKEYS:
    
To set up hotkeys, add the following runtime commands. You will have to run this script once to initiate.

letsConverge.convergeTowardsPrev()
letsConverge.convergeTowardsCenter()
letsConverge.convergeTowardsNext()
-------------

"""

import maya.cmds as cmds

class mjConverge:
    def __init__(self):
        self.convergeWeight = .1
        self.endConverge = True

    def convergeTowardsCenter(self, *args):
        curveNameList = cmds.keyframe(query=True, name=True, sl=True)
        #print curveNameList 
        #list the names of selected keyframes in graph editor        
        if curveNameList != None:
            #if the curveNameList isn't empty
            for curveName in curveNameList:
                keyIndexList = cmds.keyframe(curveName, query=True, sl=True, iv=True)
                #list the IndexValues of selected keyframes in this curve
                valueChangeList = []
                #empty list for the values that we want to change keyIndexList keys to, must 1:1 with the keyIndexList
                lastKeyIndex = cmds.keyframe(curveName, query = True, keyframeCount = True)
                for keyIndex in keyIndexList:
                    selectedKeyTime = cmds.keyframe(curveName, query=True, index =(keyIndex,keyIndex))
                    selectedKeyVal = cmds.keyframe(curveName, query=True, vc=True, index=(keyIndex,keyIndex))
                    
                    if keyIndex == lastKeyIndex-1:
                        #If its the LAST key
                        if self.endConverge == True:
                            #If we want end converges
                            nextKeyIndex = keyIndex-1
                        else:
                            #If not, lock end keys
                            nextKeyIndex = keyIndex
                    else:
                        #if keyIndex is not a first key
                        nextKeyIndex = keyIndex+1    
                    nextKeyTime = cmds.keyframe(curveName, query=True, index =(nextKeyIndex,nextKeyIndex))
                    nextKeyVal = cmds.keyframe(curveName, query=True, vc=True, index =(nextKeyIndex,nextKeyIndex))
                    
                    if keyIndex == 0:
                        #If its the FIRST key, 
                        if self.endConverge == True:
                            #If we want end converges
                            prevKeyIndex = keyIndex+1
                        else:
                            #If not, lock end keys
                            prevKeyIndex = keyIndex
                    else:
                        #if keyIndex is not a first key
                        prevKeyIndex = keyIndex-1                  
                    prevKeyTime = cmds.keyframe(curveName, query=True, index =(prevKeyIndex,prevKeyIndex))
                    prevKeyVal = cmds.keyframe(curveName, query=True, vc =True, index =(prevKeyIndex,prevKeyIndex))
        
                    ##Get the values and times of necessary keyframe values to do math on
        
                    if (nextKeyTime[0]-prevKeyTime[0]) != 0:
                        linearSlope = (nextKeyVal[0]-prevKeyVal[0])/(nextKeyTime[0]-prevKeyTime[0])
                    else:
                        linearSlope = 0
                    diffafter = selectedKeyTime[0] - prevKeyTime[0]
                    linearVal = diffafter*linearSlope+prevKeyVal[0]
                    #Get the linear value (linearvalue at that time), Math: y=mx+b
                    
                    valueChange = (linearVal-selectedKeyVal[0])*self.convergeWeight+selectedKeyVal[0]
                    #Weighted change of the linear value, versus what it used to be
                    valueChangeList.append(valueChange)
                    
        
                for idx, keyIndex in enumerate(keyIndexList):
                    cmds.keyframe(curveName, edit=True, vc=valueChangeList[idx], index =(keyIndex,keyIndex))
                    #Apply the valueChange to the corresponding keyIndex key
        else:
            #if curveNameList is empty
            print "No keys selected in Graph Editor"

    def convergeTowardsNext(self, *args):
        print 'Lets do this'
        curveNameList = cmds.keyframe(query=True, name=True, sl=True)
        #print curveNameList 
        #list the names of selected keyframes in graph editor
        
        if curveNameList != None:
            #if the curveNameList isn't empty
            for curveName in curveNameList:
                keyIndexList = cmds.keyframe(curveName, query=True, sl=True, iv=True)
                #list the IndexValues of selected keyframes in this curve
                valueChangeList = []
                #empty list for the values that we want to change keyIndexList keys to, must 1:1 with the keyIndexList
                lastKeyIndex = cmds.keyframe(curveName, query = True, keyframeCount = True)
                for keyIndex in keyIndexList:
                    selectedKeyTime = cmds.keyframe(curveName, query=True, index =(keyIndex,keyIndex))
                    selectedKeyVal = cmds.keyframe(curveName, query=True, vc=True, index=(keyIndex,keyIndex))
                    
                    if keyIndex == lastKeyIndex-1:
                        #If its the LAST key
                        nextKeyIndex = keyIndex
                    else:
                        #if keyIndex is not a first key
                        nextKeyIndex = keyIndex+1    
                    nextKeyTime = cmds.keyframe(curveName, query=True, index =(nextKeyIndex,nextKeyIndex))
                    nextKeyVal = cmds.keyframe(curveName, query=True, vc=True, index =(nextKeyIndex,nextKeyIndex))

                    valueChange = (nextKeyVal[0]-selectedKeyVal[0])*self.convergeWeight+selectedKeyVal[0]
                    #Weighted change of the linear value, versus what it used to be
                    valueChangeList.append(valueChange)

                for idx, keyIndex in enumerate(keyIndexList):
                    cmds.keyframe(curveName, edit=True, vc=valueChangeList[idx], index =(keyIndex,keyIndex))
                    #Apply the valueChange to the corresponding keyIndex key

        else:
            #if curveNameList is empty
            print "No keys selected in Graph Editor"

    def convergeTowardsPrev(self, *args):
        curveNameList = cmds.keyframe(query=True, name=True, sl=True)
        if curveNameList != None:
            for curveName in curveNameList:
                keyIndexList = cmds.keyframe(curveName, query=True, sl=True, iv=True)
                valueChangeList = []
                lastKeyIndex = cmds.keyframe(curveName, query = True, keyframeCount = True)
                for keyIndex in keyIndexList:
                    selectedKeyTime = cmds.keyframe(curveName, query=True, index =(keyIndex,keyIndex))
                    selectedKeyVal = cmds.keyframe(curveName, query=True, vc=True, index=(keyIndex,keyIndex))
                    if keyIndex == 0:
                        prevKeyIndex = keyIndex
                    else:
                        prevKeyIndex = keyIndex-1    
                    prevKeyTime = cmds.keyframe(curveName, query=True, index =(prevKeyIndex,prevKeyIndex))
                    prevKeyVal = cmds.keyframe(curveName, query=True, vc=True, index =(prevKeyIndex,prevKeyIndex))
                    valueChange = (prevKeyVal[0]-selectedKeyVal[0])*self.convergeWeight+selectedKeyVal[0]
                    valueChangeList.append(valueChange)
                for idx, keyIndex in enumerate(keyIndexList):
                        cmds.keyframe(curveName, edit=True, vc=valueChangeList[idx], index =(keyIndex,keyIndex))
                        #Apply the valueChange to the corresponding keyIndex key

        else:
            print "No keys selected in Graph Editor"

    def turnOnEndConverge(self, *args):
        self.endConverge = True
    def turnOffEndConverge(self, *args):
        self.endConverge = False
    def changeConvergeWeight(self,*args):
        newConvergeWeight = cmds.floatField('convergeWeightField', q=True, value=True)
        self.convergeWeight=newConvergeWeight
    def showUI(self):
        if cmds.window('mjConvergeUI', exists =True) ==True:
            cmds.deleteUI('mjConvergeUI')

        self.myWindow = cmds.window('mjConvergeUI', widthHeight = (600,800), title = "mjConvergeUI")

        cmds.columnLayout()
        cmds.floatField('convergeWeightField', value = self.convergeWeight, cc= self.changeConvergeWeight,ed=True)
        cmds.checkBox('checkBoxField', label = 'End Converge', value = self.endConverge, onc=self.turnOnEndConverge, ofc=self.turnOffEndConverge)
        cmds.rowLayout(nc=3)
        cmds.button(label = "PREV", command = self.convergeTowardsPrev)
        cmds.button(label = "CENTER", command = self.convergeTowardsCenter)
        cmds.button(label = "NEXT", command = self.convergeTowardsNext)
        cmds.setParent('..')
        
        cmds.showWindow('mjConvergeUI') #makedatshit

letsConverge = mjConverge()
letsConverge.showUI()