public void UpdateTweakedValue(ModuleTweakableSubParam subModule)
        {
            for (int i = 0; i < tweakableParams.Count; ++i)
            {
                if (tweakableParams[i] == subModule)
                {
                    int curIdx = -1;
                    int curModuleIdx = -1;
                    while (curModuleIdx != i)
                    {
                        curIdx = tweakableParamModulesData.IndexOf("<", curIdx + 1);
                        curModuleIdx++;
                    }
                    curIdx++;
                    int endIdx = tweakableParamModulesData.IndexOf(">", curIdx);
                    string moduleSection = tweakableParamModulesData.Substring(curIdx, endIdx - curIdx);
                    int curSubIdx = -1;
                    curSubIdx = moduleSection.IndexOf(",", curSubIdx + 1);
                    curSubIdx++;
                    int endSubIdx = moduleSection.IndexOf(",", curSubIdx);

                    if (subModule.tweakedCurve != null)
                        moduleSection = moduleSection.Substring(0, curSubIdx) + SerializeFloatCurve(subModule.tweakedCurve) + moduleSection.Substring(endSubIdx);
                    else
                        moduleSection = moduleSection.Substring(0, curSubIdx) + subModule.tweakedValue.ToString("F4") + moduleSection.Substring(endSubIdx);
                    tweakableParamModulesData = tweakableParamModulesData.Substring(0, curIdx) + moduleSection + tweakableParamModulesData.Substring(endIdx);
                }
            }
        }
        public void ParseMultipleModules(List<ModuleTweakableSubParam> list, string data)
        {
            // Format: <Module(ModuleRCS).thrusterPower,,0.0,5.0,0.1,1>,<.....>,<.....>
            Debug.Log(data);
            string firstPass = data.Replace(">,", ">").Replace(">", "");
            Debug.Log(firstPass);
            string[] modules = firstPass.Split(new char[]{'<'}, StringSplitOptions.RemoveEmptyEntries);
            foreach (string module in modules)
            {
                Debug.Log(module);
                string[] fields = module.Split(new char[]{','}, StringSplitOptions.None);

                //if (m_startState == StartState.Editor)
                {
                    ModuleTweakableSubParam newModule = new ModuleTweakableSubParam();
                    newModule.parentModule = this;
                    tweakableParams.Add(newModule);

                    newModule.targetField = fields[0];
                    newModule.minValue = Convert.ToSingle(fields[2]);
                    newModule.maxValue = Convert.ToSingle(fields[3]);
                    newModule.stepValue = Convert.ToSingle(fields[4]);
                    newModule.setOnlyOnLaunchPad = (Convert.ToInt32(fields[5]) != 0);
                    if (fields[1].StartsWith("["))
                    {
                        newModule.tweakedCurve = DeserializeFloatCurve(fields[1]);
                    }
                    else
                    {
                        newModule.tweakedValue = (fields[1] == "" ? -1 : Convert.ToSingle(fields[1]));
                        newModule.tweakedCurve = null;
                    }
                }
                //else
                //{
                //    ModuleTweakableParam newModule = (this.part.AddModule("ModuleTweakableParam") as ModuleTweakableParam);
                //    newModule.targetField = fields[0];
                //    newModule.minValue = Convert.ToSingle(fields[2]);
                //    newModule.maxValue = Convert.ToSingle(fields[3]);
                //    newModule.stepValue = Convert.ToSingle(fields[4]);
                //    newModule.setOnlyOnLaunchPad = (Convert.ToInt32(fields[5]) != 0);
                //    newModule.tweakedValue = (fields[1] == "" ? -1 : Convert.ToSingle(fields[1]));
                //    newModule.OnStart(m_startState);
                //}
            }
        }
        public void WindowFunc(int id)
        {
            bool isOkClicked = false;
            bool isCancelClicked = false;

            bool isPrevClicked = false;
            bool isNextClicked = false;
            bool isAddClicked = false;
            bool isEditClicked = false;
            bool isRemoveClicked = false;

            GUI.skin = HighLogic.Skin;
            GUILayout.BeginVertical(GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
            {
                if (canvasInvalidated == true)
                    UpdateCurve();
                GUILayout.Label(canvas, GUILayout.Width(200), GUILayout.Height(200));

                bool newXAxisUseLog = GUILayout.Toggle(xAxisUseLog, "Use ln() on X-axis", GUILayout.ExpandWidth(true));
                if (xAxisUseLog != newXAxisUseLog) canvasInvalidated = true;
                xAxisUseLog = newXAxisUseLog;
                GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
                {
                    // Index Selector, Two Input Boxes.
                    isPrevClicked = GUILayout.Button("<-", GUILayout.Width(40));
                    isNextClicked = GUILayout.Button("->", GUILayout.Width(40));
                    if (isPrevClicked == true)
                    {
                        if (m_values.Count != 0)
                        {
                            recordIndex--;
                            if (recordIndex < 0)
                                recordIndex = m_values.Count - 1;
                        }
                        else
                            recordIndex = -1;
                    }
                    if (isNextClicked == true)
                    {
                        if (m_values.Count != 0)
                        {
                            recordIndex++;
                            recordIndex = recordIndex % m_values.Count;
                        }
                        else
                            recordIndex = -1;
                    }

                    if ((isPrevClicked == true || isNextClicked == true) && recordIndex != -1)
                    {
                        recordKey = m_values[recordIndex].x;
                        recordValue = m_values[recordIndex].y;
                        canvasInvalidated = true;
                    }

                    recordKeyStr = recordKey.ToString("F2");
                    recordValueStr = recordValue.ToString("F2");
                    Single.TryParse(GUILayout.TextField(recordKeyStr, GUILayout.ExpandWidth(true)), out recordKey);
                    recordKeyStr = recordKey.ToString("F2");
                    Single.TryParse(GUILayout.TextField(recordValueStr, GUILayout.ExpandWidth(true)), out recordValue);
                    recordValueStr = recordValue.ToString("F2");
                }
                GUILayout.EndHorizontal();
                GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
                {
                    // Add, Edit, Remove Buttons.
                    isAddClicked = GUILayout.Button("Add", GUILayout.ExpandWidth(true));
                    isEditClicked = GUILayout.Button("Edit", GUILayout.ExpandWidth(true));
                    isRemoveClicked = GUILayout.Button("Remove", GUILayout.ExpandWidth(true));
                    if (isAddClicked)
                    {
                        recordIndex = AddKeyValuePair(recordKey, recordValue);
                    }
                    if (isEditClicked && recordIndex != -1)
                    {
                        recordIndex = EditKeyValuePair(recordIndex, recordKey, recordValue);
                    }
                    if (isRemoveClicked && recordIndex != -1)
                    {
                        RemoveKeyValuePair(recordIndex);
                        if (recordIndex == m_values.Count)
                            recordIndex--;
                    }
                }
                GUILayout.EndHorizontal();
                GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
                {
                    // Ok & Cancel.
                    GUILayout.FlexibleSpace();
                    isOkClicked = GUILayout.Button("OK", GUILayout.Width(80));
                    isCancelClicked = GUILayout.Button("Cancel", GUILayout.Width(80));
                }
                GUILayout.EndHorizontal();
            }
            GUILayout.EndVertical();

            GUI.DragWindow(new Rect(0, 0, 2000, 30));

            if (isOkClicked == true || isCancelClicked == true)
            {
                if (isOkClicked == true && m_values.Count != 0)
                    SubmitDataToReceiver();
                if (isCancelClicked == true || (isOkClicked == true && m_values.Count != 0))
                {
                    receiver = null;
                    canvasInvalidated = true;
                    m_values.Clear();
                    DeleteGUI();
                }
            }
        }
        public void AddModuleToSelectedPart(ModuleTweakableParam module, PartModule.StartState state, Part part, string targetField, string min, string max, string step, bool setOnlyOnLaunchPad)
        {
            Debug.Log("AddModuleToSelectedPart()");
            if (state != PartModule.StartState.Editor) return;

            Debug.Log(targetField + ": " + min + " - " + max + ", " + step + " " + (setOnlyOnLaunchPad ? "T" : "F"));
            float minValue = Convert.ToSingle(min);
            float maxValue = Convert.ToSingle(max);
            float stepValue = Convert.ToSingle(step);

            ModuleTweakableSubParam targetModule = null;
            int index = -1;
            for (int i = 0; i < module.tweakableParams.Count; ++i)
            {
                if (module.tweakableParams[i].targetField == targetField)
                {
                    Debug.Log("Already have the module.");
                    targetModule = module.tweakableParams[i];
                    index = i;
                }
            }

            if (minValue == 1 && maxValue == -1 && stepValue == 0)
            {
                if (targetModule == null)
                {
                    Debug.Log("Create a new module.");
                    targetModule = new ModuleTweakableSubParam();
                    targetModule.parentModule = module;

                    targetModule.targetField = targetField;
                    targetModule.minValue = minValue;
                    targetModule.maxValue = maxValue;
                    targetModule.stepValue = stepValue;
                    targetModule.setOnlyOnLaunchPad = setOnlyOnLaunchPad;

                    targetModule.tweakedCurve = null;

                    targetModule.OnStart(PartModule.StartState.Editor);

                    module.tweakableParams.Add(targetModule);
                    if (module.tweakableParamModulesData != "")
                        module.tweakableParamModulesData = module.tweakableParamModulesData.TrimEnd(',') + ",";
                    module.tweakableParamModulesData += "<" +
                        targetModule.targetField + "," +
                        targetModule.parentModule.SerializeFloatCurve(targetModule.tweakedCurve) + "," +
                        targetModule.minValue.ToString("F4") + "," +
                        targetModule.maxValue.ToString("F4") + "," +
                        targetModule.stepValue.ToString("F4") + "," +
                        (targetModule.setOnlyOnLaunchPad ? "1" : "0") +
                        ">,";
                }
                else
                {
                    targetModule.minValue = minValue;
                    targetModule.maxValue = maxValue;
                    targetModule.stepValue = stepValue;

                    targetModule.ClearCurvePoints();

                    int curIdx = -1;
                    int curPos = -1;
                    while (curIdx != index)
                    {
                        curPos = module.tweakableParamModulesData.IndexOf("<", curPos + 1);
                        curIdx++;
                        if (curPos == -1)
                            break;
                    }

                    if (curIdx == index)
                    {
                        int endPos = module.tweakableParamModulesData.IndexOf(">", curPos) + 1;
                        module.tweakableParamModulesData =
                            module.tweakableParamModulesData.Substring(0, curPos) +
                            "<" +
                            targetModule.targetField + "," +
                            targetModule.parentModule.SerializeFloatCurve(targetModule.tweakedCurve) + "," +
                            targetModule.minValue.ToString("F4") + "," +
                            targetModule.maxValue.ToString("F4") + "," +
                            targetModule.stepValue.ToString("F4") + "," +
                            (targetModule.setOnlyOnLaunchPad ? "1" : "0") +
                            ">"
                            + module.tweakableParamModulesData.Substring(endPos);
                    }
                }
            }
            else
            {
                if (targetModule == null)
                {
                    Debug.Log("Create a new module.");
                    targetModule = new ModuleTweakableSubParam();
                    targetModule.parentModule = module;

                    targetModule.targetField = targetField;
                    targetModule.minValue = minValue;
                    targetModule.maxValue = maxValue;
                    targetModule.stepValue = stepValue;
                    targetModule.setOnlyOnLaunchPad = setOnlyOnLaunchPad;

                    targetModule.tweakedValue = maxValue;

                    targetModule.OnStart(PartModule.StartState.Editor);

                    module.tweakableParams.Add(targetModule);
                    if (module.tweakableParamModulesData != "")
                        module.tweakableParamModulesData = module.tweakableParamModulesData.TrimEnd(',') + ",";
                    module.tweakableParamModulesData += "<" +
                        targetModule.targetField + "," +
                        targetModule.tweakedValue.ToString("F4") + "," +
                        targetModule.minValue.ToString("F4") + "," +
                        targetModule.maxValue.ToString("F4") + "," +
                        targetModule.stepValue.ToString("F4") + "," +
                        (targetModule.setOnlyOnLaunchPad ? "1" : "0") +
                        ">,";
                }
                else
                {
                    targetModule.minValue = minValue;
                    targetModule.maxValue = maxValue;
                    targetModule.stepValue = stepValue;

                    if (targetModule.tweakedValue > maxValue)
                        targetModule.tweakedValue = maxValue;
                    else if (targetModule.tweakedValue < minValue)
                        targetModule.tweakedValue = minValue;

                    int curIdx = -1;
                    int curPos = -1;
                    while (curIdx != index)
                    {
                        curPos = module.tweakableParamModulesData.IndexOf("<", curPos + 1);
                        curIdx++;
                        if (curPos == -1)
                            break;
                    }

                    if (curIdx == index)
                    {
                        int endPos = module.tweakableParamModulesData.IndexOf(">", curPos) + 1;
                        module.tweakableParamModulesData =
                            module.tweakableParamModulesData.Substring(0, curPos) +
                            "<" +
                            targetModule.targetField + "," +
                            targetModule.tweakedValue.ToString("F4") + "," +
                            targetModule.minValue.ToString("F4") + "," +
                            targetModule.maxValue.ToString("F4") + "," +
                            targetModule.stepValue.ToString("F4") + "," +
                            (targetModule.setOnlyOnLaunchPad ? "1" : "0") +
                            ">"
                            + module.tweakableParamModulesData.Substring(endPos);
                    }
                }
            }
        }
        public void SetReceiver(ModuleTweakableSubParam receiver)
        {
            this.receiver = receiver;
            m_values.Clear();
            for (int i = 0; i < receiver.tweakedCurve.Count / 2; i++)
            {
                AddKeyValuePair(receiver.tweakedCurve[i * 2], receiver.tweakedCurve[i * 2 + 1]);
            }

            if (m_values.Count != 0)
            {
                recordIndex = 0;
                recordKey = m_values[recordIndex].x;
                recordValue = m_values[recordIndex].y;
            }
            else
            {
                recordIndex = -1;
            }

            canvasInvalidated = true;
        }