// public method

        #endregion "public method"

        #region "private method"



        private void _OnGUI_IKPanel(CCDSolverMB cp)
        {
            switch (m_panel)
            {
            case EPanel.Normal: break;

            case EPanel.Continuous: break;

            case EPanel.Manual:
            {
                EUtil.DrawSplitter();
                if (GUILayout.Button(CONT_GO, EditorStyles.toolbarButton))
                {
                    var solver = cp.GetSolver();
                    Undo.RecordObjects(solver.GetJoints(), "IK Follow");
                    solver.Execute();
                }
            }
            break;

            case EPanel.Debug:
            {
                EUtil.DrawSplitter();
                _OnGUI_IKPanel_Debug(cp);
            }
            break;

            default:
                Dbg.LogErr("CCDSolverEditor._OnGUI_IKPanel: unexpected panel mode: {0}", m_panel);
                break;
            }
        }
        private bool _CheckIfBoneContinuous(CCDSolverMB mb)
        {
            var joints = mb.jointList;

            for (int i = joints.Count - 1; i >= 1; --i)
            {
                var j  = joints[i];
                var ju = joints[i - 1];
                if (ju != j.parent)
                {
                    return(false);
                }
            }
            return(true);
        }
        private bool _CheckManualSetJointList(CCDSolverMB cp)
        {
            var lst = cp.jointList;

            for (int i = 0; i < lst.Count; ++i)
            {
                if (lst[i] == null)
                {
                    EUtil.ShowNotification("Joint List has null entry!");
                    return(false);
                }
            }

            if (lst.Count > 0 && lst[lst.Count - 1] != cp.transform)
            {
                EUtil.ShowNotification("Joint List's last entry must be 'this' transform");
                return(false);
            }

            return(true);
        }
        private int m_autoCollectBoneCount = 2; // this is used to auto-collect joints to set m_jointList

        #endregion "data"

        #region "unity event handlers"
        // unity event handlers

        void OnEnable()
        {
            if (!Application.isPlaying)//cannot let editor script to mess with in-game logic
            {
                CCDSolverMB mb = (CCDSolverMB)target;
                if (mb == null)
                {
                    return; //possible after switching scene
                }
                var solver = mb.GetSolver();
                if (solver != null)
                {
                    solver.RefreshConstraint();
                }

                m_autoCollectBoneCount = mb.boneCount;
                m_manualSetJoints      = !_CheckIfBoneContinuous(mb);
            }

            m_markerSize = EDOFloat.DFGet("CCDSolverEditor.m_markerSize", 0.01f);
        }
        private bool _CheckJointListIdentical(CCDSolverMB cp)
        {
            var lst    = cp.jointList;
            var solver = cp.GetSolver();

            if (solver != null)
            {
                var curLst = solver.GetJoints();
                if (lst.Count != curLst.Length)
                {
                    return(false);
                }

                for (int i = 0; i < lst.Count; ++i)
                {
                    if (lst[i] != curLst[i])
                    {
                        return(false);
                    }
                }
            }
            return(true);
        }
        public override void OnInspectorGUI()
        {
            CCDSolverMB cp = (CCDSolverMB)target;

            EditorGUI.BeginChangeCheck();

            EConUtil.DrawActiveLine(cp);

            //constraint target
            cp.Target = (Transform)EditorGUILayout.ObjectField("Target Object", cp.Target, typeof(Transform), true);

            EUtil.DrawSplitter();

            EUtil.PushGUIEnable(cp.IsActiveConstraint);
            {
                // reset button
                GUILayout.BeginHorizontal();
                GUILayout.Space(30f);
                if (EUtil.Button("Recollect IKConstraints", "click this when add new IK-constraint on this IK-link", Color.green))
                {
                    cp.GetSolver(true);
                    EUtil.RepaintSceneView();
                }
                GUILayout.Space(30f);
                GUILayout.EndHorizontal();


                // jointList
                if (!m_manualSetJoints)
                {
                    m_autoCollectBoneCount = EditorGUILayout.IntField(CONT_BoneCnt, m_autoCollectBoneCount);
                }
                else
                {
                    var jointList = cp.jointList;
                    Undo.RecordObject(cp, "JointList");
                    for (int i = 0; i < jointList.Count; ++i)
                    {
                        EditorGUILayout.BeginHorizontal();
                        jointList[i] = (Transform)EditorGUILayout.ObjectField(jointList[i], typeof(Transform), true);
                        if (GUILayout.Button(new GUIContent("+", "add new entry")))
                        {
                            jointList.Insert(i, null);
                            GUIUtility.ExitGUI();
                        }
                        if (GUILayout.Button(new GUIContent("-", "delete this entry")))
                        {
                            jointList.RemoveAt(i);
                            GUIUtility.ExitGUI();
                        }
                        EditorGUILayout.EndHorizontal();
                    }
                }

                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button(m_manualSetJoints ? CONT_ManualSetJoints : CONT_AutoSetJoints, EditorStyles.toolbarButton))
                {
                    m_manualSetJoints = !m_manualSetJoints;
                }

                bool isIdentical = _CheckJointListIdentical(cp);
                EUtil.PushBackgroundColor(isIdentical ? Color.white : Color.blue);
                if (GUILayout.Button(new GUIContent("Apply", "Apply the joint list"), EditorStyles.toolbarButton))
                {
                    if (m_manualSetJoints)
                    {
                        if (_CheckManualSetJointList(cp))
                        {
                            cp._RenewInitInfoAndSolver();
                        }
                    }
                    else
                    {
                        cp._RenewByCollectJointsWithBontCount(m_autoCollectBoneCount);
                    }
                    m_autoCollectBoneCount = cp.boneCount;
                }
                EUtil.PopBackgroundColor();

                EditorGUILayout.EndHorizontal();

                EUtil.DrawSplitter();

                // dist thres
                EditorGUI.BeginChangeCheck();
                float newDistThres = EditorGUILayout.FloatField(CONT_DistThres, cp.distThres);
                if (EditorGUI.EndChangeCheck())
                {
                    if (newDistThres > 0f)
                    {
                        Undo.RecordObject(cp, "Set Dist Thres");
                        cp.distThres = newDistThres;
                    }
                }

                cp.useDamp = EditorGUILayout.Toggle(CONT_Damp, cp.useDamp);
                if (cp.useDamp)
                {
                    cp.globalDamp = EditorGUILayout.FloatField("Global damp", cp.globalDamp);
                }

                cp.useTargetRotation = EditorGUILayout.Toggle(CONT_UseTargetRotation, cp.useTargetRotation);
                cp.maxIteration      = EditorGUILayout.IntField(CONT_MaxIteration, cp.maxIteration);

                cp.revertOpt = (CCDSolver.RevertOption)EditorGUILayout.EnumPopup(CONT_RevertOpt, cp.revertOpt);

                m_markerSize.val = Mathf.Max(0, EditorGUILayout.FloatField(CONT_BoneMarkSize, m_markerSize.val));

                EUtil.PushGUIEnable(!cp.Target);
                {
                    if (GUILayout.Button("Control Mode:  " + (cp.Target ? "Target" : m_panel.ToString()), EditorStyles.toolbarButton))
                    {
                        m_panel = (EPanel)((int)(m_panel + 1) % (int)EPanel.END);
                        cp.GetSolver().Target = cp.transform.position;
                    }
                    _OnGUI_IKPanel(cp);
                }
                EUtil.PopGUIEnable();

                // influence
                GUILayout.Space(5f);
                cp.Influence = EUtil.ProgressBar(cp.Influence, 0, 1f, "Influence: {0:F2}");

                //initInfos
                if (Pref.ShowInitInfos)
                {
                    EditorGUILayout.PropertyField(serializedObject.FindProperty("m_initInfos"), true);
                }
            }
            EUtil.PopGUIEnable();

            if (EditorGUI.EndChangeCheck())
            {
                EditorUtility.SetDirty(cp); //so ConstraintStack.Update can be called in edit-mode
            }
        }
        private void _OnGUI_IKPanel_Debug(CCDSolverMB cp)
        {
            // line 1
            GUILayout.BeginHorizontal();
            {
                if (GUILayout.Button(CONT_ZeroAll, EditorStyles.toolbarButton))
                { //zero-out all rotation
                    var solver = cp.GetSolver();
                    var joints = solver.GetJoints();
                    Undo.RecordObjects(joints, "Zero-out IK joints");
                    foreach (var j in joints)
                    {
                        j.localRotation = Quaternion.identity;
                    }
                }
                if (GUILayout.Button(CONT_Reset, EditorStyles.toolbarButton))
                { // return target to endJoint
                    var solver = cp.GetSolver();
                    solver.Target = cp.transform.position;
                    EUtil.RepaintSceneView();
                }
            }
            GUILayout.EndHorizontal();

            // line 2
            GUILayout.BeginHorizontal();
            {
                bool   bStartedStep = m_dbgStepIE != null;
                Color  c            = bStartedStep ? Color.red : Color.green;
                string s            = bStartedStep ? "StopStep" : "StartStep";
                if (EUtil.Button(s, c))
                {
                    var solver = cp.GetSolver();
                    if (!bStartedStep)
                    {
                        m_dbgStepIE = solver.DBGExecute();
                    }
                    else
                    {
                        solver.dbg_interrupt = true;
                        m_dbgStepIE.MoveNext();
                        m_dbgStepIE  = null;
                        bStartedStep = false;
                    }
                }

                EUtil.PushGUIEnable(bStartedStep);
                {
                    if (GUILayout.Button(CONT_Step))
                    {
                        bool bNotOver = m_dbgStepIE.MoveNext();
                        if (!bNotOver)
                        {
                            m_dbgStepIE  = null;
                            bStartedStep = false;
                        }
                    }
                    if (GUILayout.Button(CONT_Continue))
                    {
                        while (m_dbgStepIE.MoveNext() == true)
                        {
                            ;
                        }
                        m_dbgStepIE  = null;
                        bStartedStep = false;
                    }
                }
                EUtil.PopGUIEnable();
            }
            GUILayout.EndHorizontal();
        }
        void OnSceneGUI()
        {
            CCDSolverMB cp     = (CCDSolverMB)target;
            CCDSolver   solver = cp.GetSolver();

            if (solver == null || solver.Count < 1)
            {
                return;
            }

            if (cp.ShowGizmos)
            {
                var joints = solver.GetJoints();

                Camera    sceneCam = EUtil.GetSceneViewCamera();
                Transform camTr    = sceneCam.transform;

                EUtil.PushHandleColor(Pref.IKBoneLinkColor);
                //1. draw bone line
                for (int i = 0; i < joints.Length - 1; ++i)
                {
                    var p0 = joints[i].position;
                    var p1 = joints[i + 1].position;
                    Handles.DrawAAPolyLine(3f, p0, p1);
                    Handles.DotCap(0, p0, camTr.rotation, m_markerSize.val);
                }
                if (joints.Length > 0)
                {
                    Handles.DotCap(0, joints.Last().position, camTr.rotation, m_markerSize.val);
                }
                EUtil.PopHandleColor();

                //1.5 draw line from end-joint to target pos
                {
                    var p0 = joints.Last().position;
                    var p1 = solver.Target;
                    Handles.DrawDottedLine(p0, p1, 5f);
                }

                //2. call each constraint's OnSceneGUI
                for (int i = 0; i < joints.Length - 1; ++i)
                {
                    var cons = solver.GetConstraint(i);

                    foreach (var con in cons)
                    {
                        if (!con || !con.enabled)
                        {
                            continue;
                        }

                        Editor      e    = EUtil.GetEditor(con);
                        IOnSceneGUI igui = e as IOnSceneGUI;
                        if (igui != null)
                        {
                            igui.OnSceneGUI();
                        }
                    }
                }
            }

            //3. debug draw
            if (!cp.Target)
            {
                if (m_panel != EPanel.Normal)
                {
                    Tools.current = Tool.None;

                    // move handle
                    if (m_panel == EPanel.Continuous)
                    {
                        EditorGUI.BeginChangeCheck();
                    }

                    solver.Target = Handles.PositionHandle(solver.Target, Quaternion.identity);

                    if (m_panel == EPanel.Continuous && EditorGUI.EndChangeCheck())
                    {
                        Undo.RecordObjects(solver.GetJoints(), "IK execute");
                        solver.Execute();
                    }

                    // hotkeys
                    Event e = Event.current;
                    if (e.type == EventType.KeyUp && Tools.viewTool != ViewTool.FPS)
                    {
                        if (e.keyCode == KeyCode.E)
                        {
                            Tools.current = Tool.Rotate; m_panel = EPanel.Normal;
                        }
                        else if (e.keyCode == KeyCode.R)
                        {
                            Tools.current = Tool.Scale; m_panel = EPanel.Normal;
                        }
                        else if (e.keyCode == KeyCode.Q)
                        {
                            Tools.current = Tool.View; m_panel = EPanel.Normal;
                        }
                    }
                }
            }
        }