/*
 * CurveEditor.cs
 * Copyright (c) 2007-2009 kbinani
 *
 * This file is part of LipSync.
 *
 * LipSync is free software; you can redistribute it and/or
 * modify it under the terms of the BSD License.
 *
 * LipSync is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.Serialization;
using System.Windows.Forms;

using Boare.Lib.AppUtil;
using LipSync;

namespace CurveEditor {

    public partial class CurveEditor : UserControl {
        public delegate void CurveEditedEventHandler();

        public enum MouseDownMode {
            Nothing,
            MovePoint,
        }

        private class ScaleAndOffset {
            public float Scale;
            public float Offset;

            public ScaleAndOffset( float scale, float offset ) {
                Scale = scale;
                Offset = offset;
            }
        }

        Dictionary<string, BezierChain> m_list = new Dictionary<string, BezierChain>();
        Dictionary<string, ScaleAndOffset> m_list_minmax = new Dictionary<string, ScaleAndOffset>();
        bool m_drag_ = false;
        bool m_drag_with_space_key = false;
        Point m_drag_start;
        float m_old_xoffset;
        float m_old_yoffset;
        const int LABEL_WIDTH = 15;
        const int LIST_WIDTH = 60;
        string m_selected = "";
        int m_picked_point_id = -1;
        PickedSide m_picked_side;
        bool m_scale_locked = false;
        ControlType m_last_added_type = ControlType.None;
        List<Command> m_commands = new List<Command>();
        int m_command_position = -1;
        PointF m_before_edit;
        //bool m_moved = false;
        //bool m_mouse_upped = true;
        bool m_change_xscale = false;
        bool m_change_yscale = false;
        int m_change_origin;
        float m_old_scale;
        float CHANGE_SCALE_ORDER = 70f;
        bool m_spacekey_down = false;
        Cursor HAND;
        bool _number_visible = false;
        PointF _number;
        Font _font;
        Point _mouse_position;
        ContextMenuStrip _cmenu;
        ToolStripMenuItem _cmenuNumericInput;
        bool m_rescaley_enabled = true;
        XLabel m_xlabel = XLabel.None;
        YLabel m_ylabel = YLabel.None;
        bool m_change_xscale_with_wheel = true;
        bool m_change_yscale_with_wheel = true;
        bool m_show_list = true;
        float m_max_xscale = 100f;
        float m_min_xscale = 1e-4f;
        float m_max_yscale = 100f;
        float m_min_yscale = 1e-4f;
        Color m_origin_scale_line = Color.FromArgb( 44, 44, 44 );
        Color m_scale_line = Color.FromArgb( 94, 94, 94 );
        Color m_sub_scale_line = Color.FromArgb( 110, 110, 110 );
        Color m_cHandle_master = Color.FromArgb( 240, 144, 160 );
        Color m_cControl_master = Color.FromArgb( 255, 130, 0 );
        Color m_cHandle_normal = Color.FromArgb( 255, 130, 0 );
        Color m_cControl_normal = Color.FromArgb( 51, 192, 64 );
        Color m_data_point = Color.Black;
        Color m_data_point_hilight = Color.Red;
        int m_data_point_size = 2;
        int m_control_point_size = 2;
        PointType m_data_point_type;
        PointType m_control_point_type;
        Color m_label_back = Color.FromArgb( 172, 172, 172 );
        Color m_list_back = Color.FromArgb( 143, 143, 143 );
        float m_xscale = 1f;
        float m_yscale = 1f;
        float m_xoffset = 0f;
        float m_yoffset = 0f;
        int m_place_count_x = 1;
        int m_place_count_y = 1;
        bool m_scroll_enabled = true;
        bool m_mouse_moved = false;
        MouseDownMode m_mouse_down_mode = MouseDownMode.Nothing;

        /// <summary>
        /// カーブのデータ点・制御点などが編集された時に発生します
        /// </summary>
        public event CurveEditedEventHandler CurveEdited;

        /// <summary>
        /// パブリックコンストラクタ
        /// </summary>
        public CurveEditor() {
            InitializeComponent();
            this.MouseWheel += new MouseEventHandler( CurveEditor_MouseWheel );
            this.SetStyle( ControlStyles.DoubleBuffer, true );
            this.SetStyle( ControlStyles.UserPaint, true );
            this.SetStyle( ControlStyles.AllPaintingInWmPaint, true );
            byte[] foo = new byte[] { 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x20, 0x20, 0x0, 0x0, 0x10, 0x0, 0x10, 0x0, 0xe8, 0x2, 0x0, 0x0, 0x16, 0x0, 
                                      0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x80, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 
                                      0x80, 0x0, 0x80, 0x0, 0x80, 0x0, 0x0, 0x80, 0x80, 0x0, 0xc0, 0xc0, 0xc0, 0x0, 0x80, 0x80, 0x80, 0x0, 0xff, 0x0, 
                                      0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0x0, 0xff, 
                                      0xff, 0x0, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xf0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 
                                      0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xf0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
                                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 
                                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
                                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xf8, 
                                      0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xcf, 0xf3, 0xff, 0xff, 0x87, 0xe1, 0xff, 0xff, 0x7, 
                                      0xe0, 0xff, 0xff, 0x7, 0xe0, 0xff, 0xff, 0x87, 0xe1, 0xff, 0xff, 0xcf, 0xf3, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xf8, 
                                      0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
                                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
                                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, };
            using ( MemoryStream ms = new MemoryStream( foo ) ) {
                HAND = new Cursor( ms );
            }
            _font = new Font( "MS UI Gothic", 10 );
        }

        public bool GetYScaleAndYOffset( string ID, out float y_scale, out float y_offset ) {
            if ( m_list_minmax.ContainsKey( ID ) ) {
                y_scale = m_list_minmax[ID].Scale;
                y_offset = m_list_minmax[ID].Offset;
                return true;
            } else {
                y_scale = 1f;
                y_offset = 0f;
                return false;
            }
        }

        public void SetYScaleAndYOffset( string ID, float y_scale, float y_offset ) {
            if ( m_list_minmax.ContainsKey( ID ) ) {
                m_list_minmax[ID].Scale = y_scale;
                m_list_minmax[ID].Offset = y_offset;
                this.Invalidate();
            }
        }
        
        public static string _( string s ) {
            return Messaging.GetMessage( s );
        }

        public Size GraphSize {
            get {
                int width, height;
                if ( XLabel != XLabel.None ) {
                    height = this.Height - LABEL_WIDTH;
                } else {
                    height = this.Height;
                }
                if ( YLabel != YLabel.None ) {
                    width = this.Width - LABEL_WIDTH;
                } else {
                    width = this.Width;
                }
                if ( ShowList ) {
                    width -= LIST_WIDTH;
                }
                return new Size( width, height );
            }
        }

        /// <summary>
        /// 縦軸スケールの変更を許可するかどうかを取得または設定します
        /// </summary>
        public bool RescaleYEnabled {
            get {
                return m_rescaley_enabled;
            }
            set {
                m_rescaley_enabled = value;
            }
        }

        bool Drag {
            get {
                return m_drag_;
            }
            set {
                m_drag_ = value;
                if ( m_drag_ ) {
                    this.Cursor = HAND;
                } else {
                    this.Cursor = Cursors.Default;
                }
            }
        }

        /// <summary>
        /// 現在カーブエディタに設定されているデータを全て破棄し，初期化します
        /// </summary>
        public void Clear() {
            m_list.Clear();
            m_list_minmax.Clear();
            m_commands.Clear();
            m_command_position = -1;
            m_selected = "";
            m_picked_point_id = -1;
        }

        /// <summary>
        /// Undo, Redo用のバッファを全て破棄し，初期化します
        /// </summary>
        public void ClearBuffer() {
            m_commands.Clear();
            m_command_position = -1;
        }

        /// <summary>
        /// 横軸の目盛の種類を取得または設定します
        /// </summary>
        public XLabel XLabel {
            get {
                return m_xlabel;
            }
            set {
                m_xlabel = value;
            }
        }

        /// <summary>
        /// 縦軸の目盛の種類を取得または設定します
        /// </summary>
        public YLabel YLabel {
            get {
                return m_ylabel;
            }
            set {
                m_ylabel = value;
            }
        }

        /// <summary>
        /// マウスホイールで横軸のスケールを変更するかどうかを取得または設定します
        /// </summary>
        public bool ChangeXScaleWithWheel {
            get {
                return m_change_xscale_with_wheel;
            }
            set {
                m_change_xscale_with_wheel = value;
            }
        }

        /// <summary>
        /// マウスホイールで縦軸のスケールを変更するかどうかを取得または設定します
        /// </summary>
        public bool ChangeYScaleWithWheel {
            get {
                return m_change_yscale_with_wheel;
            }
            set {
                m_change_yscale_with_wheel = value;
            }
        }

        /// <summary>
        /// 編集対象となるカーブのリストを，画面右側に表示するかどうかを取得または設定します
        /// </summary>
        public bool ShowList {
            get {
                return m_show_list;
            }
            set {
                m_show_list = value;
            }
        }

        /// <summary>
        /// 横軸のスケールとして設定できる最大値を取得または設定します
        /// </summary>
        public float MaxXScale {
            get {
                return m_max_xscale;
            }
            set {
                if ( value > 0 && value > m_min_xscale ) {
                    m_max_xscale = value;
                }
            }
        }

        /// <summary>
        /// 横軸のスケールとして設定できる最小値を取得または設定します
        /// </summary>
        public float MinXScale {
            get {
                return m_min_xscale;
            }
            set {
                if ( value > 0 && value < m_max_xscale ) {
                    m_min_xscale = value;
                }
            }
        }

        /// <summary>
        /// 縦軸のスケールとして設定できる最大値を取得または設定します
        /// </summary>
        public float MaxYScale {
            get {
                return m_max_yscale;
            }
            set {
                if ( value > 0 && value > m_min_yscale ) {
                    m_max_yscale = value;
                }
            }
        }

        /// <summary>
        /// 縦軸のスケールとして設定できる最小値を取得または設定します
        /// </summary>
        public float MinYScale {
            get {
                return m_min_yscale;
            }
            set {
                if ( value > 0 && value < m_max_yscale ) {
                    m_min_yscale = value;
                }
            }
        }

        void CurveEditor_MouseWheel( object sender, MouseEventArgs e ) {
            Keys modifier = Control.ModifierKeys;
            if ( modifier == Keys.Control ) {
                float dx = e.Delta / XScale;
                XOffset -= dx;
                this.Invalidate();
            } else if ( modifier == Keys.Shift ) {
                float dy = e.Delta / YScale;
                YOffset -= dy;
                if ( m_selected != "" ) {
                    m_list_minmax[m_selected].Offset = YOffset;
                }
                this.Invalidate();
            } else if ( modifier == Keys.None ) {
                float count = e.Delta / 120f;
                float n_xscale;
                float n_yscale;
                float order;
                if ( count < 0 ) {
                    order = (float)Math.Pow( 0.83, Math.Abs( count ) );
                } else {
                    order = (float)(1f / Math.Pow( 0.83, Math.Abs( count ) ));
                }
                if ( m_change_xscale_with_wheel ) {
                    n_xscale = m_xscale * order;
                } else {
                    n_xscale = m_xscale;
                }
                if ( m_change_yscale_with_wheel ) {
                    n_yscale = m_yscale * order;
                } else {
                    n_yscale = m_yscale;
                }
                if ( m_xscale != n_xscale || m_yscale != n_yscale ) {
                    bool changed = false;
                    if ( MinXScale <= n_xscale && n_xscale <= MaxXScale ) {
                        XOffset = e.X * (1f / n_xscale - 1f / XScale) + XOffset;
                        XScale = n_xscale;
                        changed = true;
                    }
                    if ( MinYScale <= n_yscale && n_yscale <= MaxYScale ) {
                        YOffset = (this.Height - e.Y) * (1f / n_yscale - 1f / YScale) + YOffset;
                        YScale = n_yscale;
                        changed = true;
                    }
                    if ( changed ) {
                        this.Invalidate();
                    }
                }
            }
        }

        /// <summary>
        /// 編集対象となるカーブを，このコントロールに追加します
        /// </summary>
        /// <param name="ID"></param>
        /// <param name="curve"></param>
        /*public void Add( string ID, Color curve ) {
            m_list.Add( ID, new BezierChain( curve ) );
        }*/

        public void Add( string ID, BezierChain chain ) {
            m_list.Add( ID, chain );
            m_list_minmax.Add( ID, new ScaleAndOffset( 1f, 0f ) );
        }

        //todo:動かしうるxの範囲を探知できるようにする？pt1-4それぞれが動く場合。
        /// <summary>
        /// 4つの制御点からなるベジエ曲線が、x軸について陰かどうかを判定する
        /// </summary>
        /// <param name="pt1">始点</param>
        /// <param name="pt2">制御点1</param>
        /// <param name="pt3">制御点2</param>
        /// <param name="pt4">終点</param>
        /// <returns></returns>
        public static bool IsBezierImplicit( float pt1, float pt2, float pt3, float pt4 ) {
            double a = pt4 - 3 * pt3 + 3 * pt2 - pt1;
            double b = 2 * pt3 - 4 * pt2 + 2 * pt1;
            double c = pt2 - pt1;
            if ( a == 0 ) {
                if ( c >= 0 && b + c >= 0 ) {
                    return true;
                } else {
                    return false;
                }
            } else if ( a > 0 ) {
                if ( -b / (2 * a) <= 0 ) {
                    if ( c >= 0 ) {
                        return true;
                    } else {
                        return false;
                    }
                } else if ( 1 <= -b / (2 * a) ) {
                    if ( a + b + c >= 0 ) {
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    if ( c - b * b / (4 * a) >= 0 ) {
                        return true;
                    } else {
                        return false;
                    }
                }
            } else {
                if ( -b / (2 * a) <= 0.5 ) {
                    if ( a + b + c >= 0 ) {
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    if ( c >= 0 ) {
                        return true;
                    } else {
                        return false;
                    }
                }
            }
        }

        #region 描画設定
        public Color MainScaleLine {
            get {
                return m_origin_scale_line;
            }
            set {
                m_origin_scale_line = value;
            }
        }


        public Color ScaleLine {
            get {
                return m_scale_line;
            }
            set {
                m_scale_line = value;
            }
        }


        public Color SubScaleLine {
            get {
                return m_sub_scale_line;
            }
            set {
                m_sub_scale_line = value;
            }
        }


        public Color HandleMaster {
            get {
                return m_cHandle_master;
            }
            set {
                m_cHandle_master = value;
            }
        }


        public Color ControlMaster {
            get {
                return m_cControl_master;
            }
            set {
                m_cControl_master = value;
            }
        }


        public Color HandleNormal {
            get {
                return m_cHandle_normal;
            }
            set {
                m_cHandle_normal = value;
            }
        }


        public Color ControlNormal {
            get {
                return m_cControl_normal;
            }
            set {
                m_cControl_normal = value;
            }
        }

        /// <summary>
        /// データポイントの描画色を取得または設定します
        /// </summary>
        public Color DataPoint {
            get {
                return m_data_point;
            }
            set {
                m_data_point = value;
            }
        }

        /// <summary>
        /// 選択されたデータポイントの描画色を取得または設定します
        /// </summary>
        public Color DataPointHilight {
            get {
                return m_data_point_hilight;
            }
            set {
                m_data_point_hilight = value;
            }
        }

        public int DataPointSize {
            get {
                return m_data_point_size;
            }
            set {
                m_data_point_size = value;
            }
        }


        public int ControlPointSize {
            get {
                return m_control_point_size;
            }
            set {
                m_control_point_size = value;
            }
        }


        public PointType DataPointType {
            get {
                return m_data_point_type;
            }
            set {
                m_data_point_type = value;
            }
        }


        public PointType ControlPointType {
            get {
                return m_control_point_type;
            }
            set {
                m_control_point_type = value;
            }
        }


        public Color LabelBackground {
            get {
                return m_label_back;
            }
            set {
                m_label_back = value;
            }
        }


        public Color ListBackground {
            get {
                return m_list_back;
            }
            set {
                m_list_back = value;
            }
        }
        #endregion

        /// <summary>
        /// 横軸のスケールを取得または設定します[pixel/(任意の単位)]
        /// </summary>
        public float XScale {
            get {
                return m_xscale;
            }
            set {
                if ( !m_scale_locked && MinXScale <= value && value <= MaxXScale ) {
                    m_xscale = value;
                }
            }
        }

        /// <summary>
        /// 縦軸のスケールを取得または設定します[pixel/(任意の単位)]
        /// </summary>
        public float YScale {
            get {
                return m_yscale;
            }
            set {
                if ( !m_scale_locked && MinYScale <= value && value <= MaxYScale ) {
                    m_yscale = value;
                }
            }
        }

        /// <summary>
        /// 画面左端におけるx軸の値を取得または設定します
        /// </summary>
        public float XOffset {
            get {
                return m_xoffset;
            }
            set {
                m_xoffset = value;
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public float YOffset {
            get {
                return m_yoffset;
            }
            set {
                m_yoffset = value;
            }
        }

        /// <summary>
        /// グラフ上のX座標を，コントロール上の座標(pixel)に換算します
        /// </summary>
        /// <param name="sX"></param>
        /// <returns></returns>
        private int cX( float sX ) {
            return (int)((sX + XOffset) * XScale);
        }

        /// <summary>
        /// グラフ上のY座標を，コントロール上の座標(pixel)に換算します
        /// </summary>
        /// <param name="sY"></param>
        /// <returns></returns>
        private int cY( float sY ) {
            return this.Height - (int)((sY + YOffset) * YScale);
        }

        private Point cPoint( PointF sPoint ) {
            return new Point( cX( sPoint.X ), cY( sPoint.Y ) );
        }

        private float sX( int cX ) {
            return cX / XScale - XOffset;
        }

        private float sY( int cY ) {
            return (this.Height - cY) / YScale - YOffset;
        }

        private PointF sPoint( Point cPoint ) {
            return new PointF( sX( cPoint.X ), sY( cPoint.Y ) );
        }

        private float m_place_x {
            get {
                int n1 = (int)Math.Log10( 400 / XScale );
                int n2 = (int)Math.Log10( 200 / XScale );
                int n5 = (int)Math.Log10( 80 / XScale );
                float d1 = (float)Math.Pow( 10, n1 );
                float d2 = (float)Math.Pow( 10, n2 );
                float d5 = (float)Math.Pow( 10, n5 );
                if ( d1 <= 2 * d2 && d1 <= 5 * d5 ) {
                    m_place_count_x = 1;
                    return d1;
                } else if ( 2 * d2 <= d1 && 2 * d2 <= 5 * d5 ) {
                    m_place_count_x = 2;
                    return d2;
                } else {
                    m_place_count_x = 5;
                    return d5;
                }
            }
        }
        
        private float m_place_y {
            get {
                int n1 = (int)Math.Log10( 400 / YScale );
                int n2 = (int)Math.Log10( 200 / YScale );
                int n5 = (int)Math.Log10( 80 / YScale );
                float d1 = (float)Math.Pow( 10, n1 );
                float d2 = (float)Math.Pow( 10, n2 );
                float d5 = (float)Math.Pow( 10, n5 );
                if ( d1 <= 2 * d2 && d1 <= 5 * d5 ) {
                    m_place_count_y = 1;
                    return d1;
                } else if ( 2 * d2 <= d1 && 2 * d2 <= 5 * d5 ) {
                    m_place_count_y = 2;
                    return d2;
                } else {
                    m_place_count_y = 5;
                    return d5;
                }
            }
        }
        
        public BezierChain this[string ID] {
            get {
                return m_list[ID];
            }
            set {
                m_list[ID] = value;
            }
        }
        
        public float this[string ID, float x] {
            get {
                return m_list[ID].GetValue( x );
            }
        }
        
        public bool ScrollEnabled {
            get {
                return m_scroll_enabled;
            }
            set {
                m_scroll_enabled = value;
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks>関数を抜けるとき確実にm_scale_locked = falseとすること！！</remarks>
        private void CurveEditor_Paint( object sender, PaintEventArgs e ) {
            m_scale_locked = true;

            //グラフ内のメモリを描画
            int o_x = cX( 0f );
            int o_y = cY( 0f );
            e.Graphics.DrawLine( new Pen( MainScaleLine ), new Point( 0, o_y ), new Point( this.Width, o_y ) );
            e.Graphics.DrawLine( new Pen( MainScaleLine ), new Point( o_x, 0 ), new Point( o_x, this.Height ) );
#if DEBUG
            //MessageBox.Show( "place_x=" + place_x );
#endif
            float place_x = m_place_count_x * m_place_x;
            int start_x = (int)(sX( 0 ) / place_x) - 1;
            int end_x = (int)(sX( this.Width ) / place_x) + 1;
            for ( int i = start_x; i <= end_x; i++ ) {
                float sx;
                int px;
                if ( i != 0 ) {
                    sx = i * place_x;
                    px = cX( sx );
                    e.Graphics.DrawLine( new Pen( ScaleLine ), new Point( px, 0 ), new Point( px, this.Height ) );
                }
                sx = (i + 0.5f) * place_x;
                px = cX( sx );
                e.Graphics.DrawLine( new Pen( SubScaleLine ), new Point( px, 0 ), new Point( px, this.Height ) );
            }

            float place_y = m_place_count_y * m_place_y;
            int start_y = (int)(sY( this.Height ) / place_y) - 1;
            int end_y = (int)(sY( 0 ) / place_y) + 1;
            for ( int i = start_y; i <= end_y; i++ ) {
                float sy;
                int py;
                if ( i != 0 ) {
                    sy = i * place_y;
                    py = cY( sy );
                    e.Graphics.DrawLine( new Pen( ScaleLine ), new Point( 0, py ), new Point( this.Width, py ) );
                }
                sy = (i + 0.5f) * place_y;
                py = cY( sy );
                e.Graphics.DrawLine( new Pen( SubScaleLine ), new Point( 0, py ), new Point( this.Width, py ) );
            }

            //foreach ( BezierChain chain in m_list.Values ) {
            foreach ( string ID in m_list.Keys ) {
                BezierChain chain = m_list[ID];
                BezierPoint last;
                if ( chain.Count >= 2 ) {
                    last = chain.List[0];
                } else {
                    int default_y = cY( chain.Default );
                    e.Graphics.DrawLine( new Pen( chain.Color ), new Point( 0, default_y ), new Point( this.Width, default_y ) );
                    if ( ID == m_selected && chain.Count >= 1 ) {
                        int width2 = m_data_point_size;
                        switch ( DataPointType ) {
                            case PointType.Circle:
                                if ( chain.List[0].ID == m_picked_point_id ) {
                                    e.Graphics.FillEllipse( new SolidBrush( DataPointHilight ),
                                                            new Rectangle( cX( chain.List[0].Base.X ) - width2, cY( chain.List[0].Base.Y ) - width2, 2 * width2, 2 * width2 ) );
                                } else {
                                    e.Graphics.FillEllipse( new SolidBrush( DataPoint ),
                                                            new Rectangle( cX( chain.List[0].Base.X ) - width2, cY( chain.List[0].Base.Y ) - width2, 2 * width2, 2 * width2 ) );
                                }
                                break;
                            case PointType.Rectangle:
                                if ( chain.List[0].ID == m_picked_point_id ) {
                                    e.Graphics.FillRectangle( new SolidBrush( DataPointHilight ),
                                                              new Rectangle( cX( chain.List[0].Base.X ) - width2, cY( chain.List[0].Base.Y ) - width2, 2 * width2, 2 * width2 ) );
                                } else {
                                    e.Graphics.FillRectangle( new SolidBrush( DataPoint ),
                                                              new Rectangle( cX( chain.List[0].Base.X ) - width2, cY( chain.List[0].Base.Y ) - width2, 2 * width2, 2 * width2 ) );
                                }
                                break;
                        }
                    }
                    continue;
                }
                int width = m_data_point_size;
                if ( ID == m_selected ) {
                    switch ( DataPointType ) {
                        case PointType.Circle:
                            if ( chain.List[0].ID == m_picked_point_id ) {
                                e.Graphics.FillEllipse( new SolidBrush( DataPointHilight ),
                                                        new Rectangle( cX( chain.List[0].Base.X ) - width, cY( chain.List[0].Base.Y ) - width, 2 * width, 2 * width ) );
                            } else {
                                e.Graphics.FillEllipse( new SolidBrush( DataPoint ),
                                                        new Rectangle( cX( chain.List[0].Base.X ) - width, cY( chain.List[0].Base.Y ) - width, 2 * width, 2 * width ) );
                            }
                            break;
                        case PointType.Rectangle:
                            if ( chain.List[0].ID == m_picked_point_id ) {
                                e.Graphics.FillRectangle( new SolidBrush( DataPointHilight ),
                                                          new Rectangle( cX( chain.List[0].Base.X ) - width, cY( chain.List[0].Base.Y ) - width, 2 * width, 2 * width ) );
                            } else {
                                e.Graphics.FillRectangle( new SolidBrush( DataPoint ),
                                                          new Rectangle( cX( chain.List[0].Base.X ) - width, cY( chain.List[0].Base.Y ) - width, 2 * width, 2 * width ) );
                            }
                            break;
                    }
                }

                //デフォルト値（左側）の描画
                if ( chain.Count >= 1 ) {
                    int default_y = cY( chain.Default );
                    int x = cX( chain.List[0].Base.X );
                    int y = cY( chain.List[0].Base.Y );
                    e.Graphics.DrawLine( new Pen( chain.Color ), new Point( x, default_y ), new Point( -1, default_y ) );
                    e.Graphics.DrawLine( new Pen( chain.Color ), new Point( x, default_y ), new Point( x, y ) );
                }
                //for ( int i = 1; i < chain.Count; i++ ) {
                bool first = true;
                Size sz = new Size( 2 * width, 2 * width );
                for( int i = 0; i < chain.List.Count; i++ ){
                    BezierPoint bp = chain.List[i];
                    if ( first ) {
                        last = bp;
                        first = false;
                        continue;
                    }
                    //if ( last.ControlRightType != ControlType.None && chain[i].ControlLeftType != ControlType.None ) {
                    e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                    e.Graphics.DrawBezier( new Pen( chain.Color ),
                                           cPoint( last.Base ),
                                           cPoint( last.ControlRight ),
                                           cPoint( bp.ControlLeft ),
                                           cPoint( bp.Base ) );
                    e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;

                    //制御ハンドル用の線
                    if ( ID == m_selected ) {
                        if ( bp.ControlLeftType == ControlType.Master ) {
                            e.Graphics.DrawLine( new Pen( HandleMaster ), cPoint( bp.Base ), cPoint( bp.ControlLeft ) );
                        } else if ( bp.ControlLeftType == ControlType.Normal ) {
                            e.Graphics.DrawLine( new Pen( HandleNormal ), cPoint( bp.Base ), cPoint( bp.ControlLeft ) );
                        }
                        if ( last.ControlRightType == ControlType.Master ) {
                            e.Graphics.DrawLine( new Pen( HandleMaster ), cPoint( last.Base ), cPoint( last.ControlRight ) );
                        } else if ( last.ControlRightType == ControlType.Normal ) {
                            e.Graphics.DrawLine( new Pen( HandleNormal ), cPoint( last.Base ), cPoint( last.ControlRight ) );
                        }

                        //データ点
                        width = m_data_point_size;
                        Point data_point = new Point( cX( bp.Base.X ) - width, cY( bp.Base.Y ) - width );
                        switch ( DataPointType ) {
                            case PointType.Circle:
                                if ( bp.ID == m_picked_point_id ) {
                                    e.Graphics.FillEllipse( new SolidBrush( DataPointHilight ),
                                                            new Rectangle( data_point, sz ) );
                                } else {
                                    e.Graphics.FillEllipse( new SolidBrush( DataPoint ),
                                                            new Rectangle( data_point, sz ) );
                                }
                                break;
                            case PointType.Rectangle:
                                if ( bp.ID == m_picked_point_id ) {
                                    e.Graphics.FillRectangle( new SolidBrush( DataPointHilight ),
                                                              new Rectangle( data_point, sz ) );
                                } else {
                                    e.Graphics.FillRectangle( new SolidBrush( DataPoint ),
                                                              new Rectangle( data_point, sz ) );
                                }
                                break;
                        }

                        //制御ハンドル点
                        width = m_control_point_size;
                        Color cLeft = Color.Black;
                        Color cRight = Color.Black;
                        if ( bp.ControlLeftType == ControlType.Master ) {
                            cLeft = ControlMaster;
                        } else if ( bp.ControlLeftType == ControlType.Normal ) {
                            cLeft = ControlNormal;
                        }
                        if ( last.ControlRightType == ControlType.Master ) {
                            cRight = ControlMaster;
                        } else if ( last.ControlRightType == ControlType.Normal ) {
                            cRight = ControlNormal;
                        }
                        if ( bp.ControlLeftType != ControlType.None && (bp.ControlLeft.X != bp.Base.X || bp.ControlLeft.Y != bp.Base.Y) ) {
                            Point ctrl_left = new Point( cX( bp.ControlLeft.X ) - width, cY( bp.ControlLeft.Y ) - width );
                            switch ( ControlPointType ) {
                                case PointType.Circle:
                                    e.Graphics.FillEllipse( new SolidBrush( cLeft ), new Rectangle( ctrl_left, sz ) );
                                    break;
                                case PointType.Rectangle:
                                    e.Graphics.FillRectangle( new SolidBrush( cLeft ), new Rectangle( ctrl_left, sz ) );
                                    break;
                            }
                        }
                        if ( last.ControlRightType != ControlType.None && (last.ControlRight.X != last.Base.X || last.ControlRight.Y != last.Base.Y) ) {
                            Point ctrl_right = new Point( cX( last.ControlRight.X ) - width, cY( last.ControlRight.Y ) - width );
                            switch ( ControlPointType ) {
                                case PointType.Circle:
                                    e.Graphics.FillEllipse( new SolidBrush( cRight ), new Rectangle( ctrl_right, sz ) );
                                    break;
                                case PointType.Rectangle:
                                    e.Graphics.FillRectangle( new SolidBrush( cRight ), new Rectangle( ctrl_right, sz ) );
                                    break;
                            }
                        }
                    }
                    last = bp;
                }

                //デフォルト値（右側）の描画
                if ( chain.Count >= 1 ) {
                    int default_y = cY( chain.Default );
                    int x = cX( last.Base.X );
                    int y = cY( last.Base.Y );
                    e.Graphics.DrawLine( new Pen( chain.Color ), new Point( x, default_y ), new Point( this.Width + 1, default_y ) );
                    e.Graphics.DrawLine( new Pen( chain.Color ), new Point( x, default_y ), new Point( x, y ) );
                }
            }

            // マウス位置の数値を描画
            if ( _number_visible ) {
                int x = cX( _number.X );
                int y = cY( _number.Y );
                e.Graphics.DrawString( "x : " + _number.X,
                                       _font,
                                       Brushes.Black,
                                       new PointF( x, y - 24 ) );
                e.Graphics.DrawString( "y : " + _number.Y,
                                       _font,
                                       Brushes.Black,
                                       new PointF( x, y - 12 ) );
            } else {
                float x = sX( _mouse_position.X );
                float y = sY( _mouse_position.Y );
                e.Graphics.DrawString( "x : " + x,
                                       _font,
                                       Brushes.Black,
                                       new PointF( _mouse_position.X, _mouse_position.Y - 24 ) );
                e.Graphics.DrawString( "y : " + y,
                                       _font,
                                       Brushes.Black,
                                       new PointF( _mouse_position.X, _mouse_position.Y - 12 ) );
            }

            int label_y = 0;
            using ( Font font = new Font( "MS UI Gothic", 9 ) ) {
                //ラベルを描画。（必要なら）
                switch ( XLabel ) {
                    case XLabel.Top:
                        e.Graphics.FillRectangle( new SolidBrush( LabelBackground ),
                                                  new Rectangle( 0, 0, this.Width, LABEL_WIDTH ) );
                        break;
                    case XLabel.Bottom:
                        e.Graphics.FillRectangle( new SolidBrush( LabelBackground ),
                                                  new Rectangle( 0, this.Height - LABEL_WIDTH, this.Width, LABEL_WIDTH ) );
                        label_y = this.Height - LABEL_WIDTH;
                        break;
                }
                if ( XLabel != XLabel.None ) {
                    for ( int i = start_x; i <= end_x; i++ ) {
                        float sx = i * place_x;
                        int px = cX( sx );
                        e.Graphics.DrawString( sx.ToString(), font, Brushes.Black, new PointF( px, label_y ) );
                    }
                }

                int label_x = 0;
                switch ( YLabel ) {
                    case YLabel.Left:
                        e.Graphics.FillRectangle(
                            new SolidBrush( LabelBackground ),
                            new Rectangle( 0, 0, LABEL_WIDTH, this.Height ) );
                        break;
                    case YLabel.Right:
                        if ( ShowList ) {
                            label_x = this.Width - LABEL_WIDTH - LIST_WIDTH;
                        } else {
                            label_x = this.Width - LABEL_WIDTH;
                        }
                        e.Graphics.FillRectangle(
                            new SolidBrush( LabelBackground ),
                            new Rectangle( label_x, 0, LABEL_WIDTH, this.Height ) );
                        break;
                }
                if ( YLabel != YLabel.None ) {
                    for ( int i = start_y; i <= end_y; i++ ) {
                        float sy = i * place_y;
                        int py = cY( sy );
                        e.Graphics.DrawString( sy.ToString(), font, Brushes.Black, new PointF( label_x, py ) );
                    }
                }

                //リストを描く
                if ( ShowList ) {
                    e.Graphics.FillRectangle(
                        new SolidBrush( ListBackground ),
                        new Rectangle( this.Width - LIST_WIDTH, 0, LIST_WIDTH, this.Height ) );
                    e.Graphics.DrawLine(
                        Pens.Black,
                        new Point( this.Width - LIST_WIDTH, 0 ),
                        new Point( this.Width - LIST_WIDTH, this.Height ) );
                    int count = 0;
                    using ( Font labelfont = new Font( "Arial", 12, FontStyle.Bold, GraphicsUnit.Pixel ) ) {
                        foreach ( string name in m_list.Keys ) {
                            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                            if ( name == m_selected ) {
                                e.Graphics.DrawString(
                                    name,
                                    labelfont,
                                    Brushes.White,
                                    new PointF( this.Width - 40, count * 17 + 2 ) );
                            } else {
                                e.Graphics.DrawString(
                                    name,
                                    labelfont,
                                    Brushes.Black,
                                    new PointF( this.Width - 40, count * 17 + 2 ) );
                            }
                            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;
                            e.Graphics.FillRectangle(
                                new SolidBrush( m_list[name].Color ),
                                new Rectangle( this.Width - 55, count * 17 + 2, 10, 15 ) );
                            count++;
                        }
                    }
                }
            }
            m_scale_locked = false;
        }
        
        private void CurveEditor_MouseDown( object sender, MouseEventArgs e ) {
            m_mouse_moved = false;
            if ( e.Button == MouseButtons.Middle || (m_spacekey_down && e.Button == MouseButtons.Left ) ) {
                if ( m_spacekey_down && ((e.Button & MouseButtons.Left) == MouseButtons.Left) ) {
                    m_drag_with_space_key = true;
                }
                #region MouseButtons.Middle
                if ( ScrollEnabled ) {
                    switch ( XLabel ) {
                        case XLabel.Top:
                            if ( e.Y <= LABEL_WIDTH ) {
                                return;
                            }
                            break;
                        case XLabel.Bottom:
                            if ( this.Height - LABEL_WIDTH <= e.Y ) {
                                return;
                            }
                            break;
                    }
                    switch ( YLabel ) {
                        case YLabel.Left:
                            if ( e.X <= LABEL_WIDTH ) {
                                return;
                            }
                            if ( ShowList && this.Width - LIST_WIDTH <= e.X ) {
                                return;
                            }
                            break;
                        case YLabel.Right:
                            if ( ShowList ) {
                                if ( this.Width - LIST_WIDTH - LABEL_WIDTH <= e.X && e.X <= this.Width - LIST_WIDTH ) {
                                    // 軸ラベルの部分でマウスダウン
                                    return;
                                } else if ( this.Width - LIST_WIDTH <= e.X ) {
                                    // リストの部分でマウスダウン
                                    return;
                                }
                            } else {
                                if ( this.Width - LABEL_WIDTH <= e.X ) {
                                    return;
                                }
                            }
                            break;
                    }
                    m_drag_start = e.Location;
                    m_old_xoffset = m_xoffset;
                    m_old_yoffset = m_yoffset;
                    Drag = true;
                }
                #endregion
            } else if ( e.Button == MouseButtons.Left ) {
                #region MouseButtons.Left

                #region リスト
                // 右のリストの部分がクリックされたかどうかを検査
                if ( ShowList && this.Width - LIST_WIDTH <= e.X ) {
                    int count = 0;

                    foreach ( string name in m_list.Keys ) {
                        Rectangle active = new Rectangle( this.Width - 55, count * 17, 50, 16 );
                        if ( active.X <= e.X && e.X <= active.X + active.Width &&
                            active.Y <= e.Y && e.Y <= active.Y + active.Height ) {
                            m_selected = name;
                            if ( RescaleYEnabled ) {
                                /*float min, max;
                                m_list[m_selected].GetMinMax( out min, out max );
#if DEBUG
                                LipSync.Common.DebugWriteLine( "min,max=" + min + "," + max );
#endif
                                if ( min != max ) {
                                    float dif = max - min;
                                    YScale = this.GraphSize.Height / (dif * 1.1f);
                                    float new_offset;
                                    if ( XLabel == XLabel.Bottom ) {
                                        new_offset = -(min - dif * 0.05f - LABEL_WIDTH / YScale);
                                    } else {
                                        new_offset = -min + dif * 0.05f;
                                    }
                                    YOffset = new_offset;
                                }*/
                                YScale = m_list_minmax[m_selected].Scale;
                                YOffset = m_list_minmax[m_selected].Offset;
                            }
                            this.Invalidate();
                            return;
                        }
                        count++;
                    }
                    m_selected = "";
                    this.Invalidate();
                    return;
                }

                #endregion

                #region スケール部分
                if ( XLabel == XLabel.Top ) {
                    if ( 0 <= e.Y && e.Y <= LABEL_WIDTH ) {
                        m_change_xscale = true;
                        m_change_origin = e.X;
                        m_old_scale = XScale;
                        return;
                    }
                } else if ( XLabel == XLabel.Bottom ) {
                    if ( this.Height - LABEL_WIDTH <= e.Y && e.Y <= this.Height ) {
                        m_change_xscale = true;
                        m_change_origin = e.X;
                        m_old_scale = XScale;
                        return;
                    }
                }

                if ( YLabel == YLabel.Left ) {
                    if ( 0 <= e.X && e.X <= LABEL_WIDTH ) {
                        m_change_yscale = true;
                        m_change_origin = e.Y;
                        m_old_scale = YScale;
                        return;
                    }
                } else if ( YLabel == YLabel.Right ) {
                    if ( ShowList ) {
                        if ( this.Width - LIST_WIDTH - LABEL_WIDTH <= e.X && e.X <= this.Width - LIST_WIDTH ) {
                            m_change_yscale = true;
                            m_change_origin = e.Y;
                            m_old_scale = YScale;
                            return;
                        }
                    } else {
                        if ( this.Width - LABEL_WIDTH <= e.X && e.X <= this.Width ) {
                            m_change_yscale = true;
                            m_change_origin = e.Y;
                            m_old_scale = YScale;
                            return;
                        }
                    }
                }

                #endregion

                #region マウス位置のデータ点を検索

                DetectSelectedPoint( e.Location, out m_picked_point_id, out m_picked_side );
#if DEBUG
                Common.DebugWriteLine( "CureveEditor_MouseDown" );
                Common.DebugWriteLine( "    m_picked_point_id=" + m_picked_point_id );
                Common.DebugWriteLine( "    m_picked_side=" + m_picked_side );
#endif
                if ( m_picked_point_id >= 0 ) {
                    if ( m_picked_side == PickedSide.Base ) {
                        m_before_edit = m_list[m_selected][m_picked_point_id].Base;
                    } else if ( m_picked_side == PickedSide.Left ) {
                        m_before_edit = m_list[m_selected][m_picked_point_id].ControlLeft;
                    } else {
                        m_before_edit = m_list[m_selected][m_picked_point_id].ControlRight;
                    }
                    _number_visible = true;
                    m_mouse_down_mode = MouseDownMode.MovePoint;
                    Invalidate();
                } else {
                    m_mouse_down_mode = MouseDownMode.Nothing;
                }

                #endregion
                #endregion
            }
        }
        
        /// <summary>
        /// カーブエディタ画面上の指定された点にあるデータ点を調べます。
        /// </summary>
        /// <param name="e"></param>
        /// <param name="picked_point"></param>
        /// <param name="picied_side"></param>
        private void DetectSelectedPoint( Point e, out int picked_point_id, out PickedSide picked_side ) {
            picked_point_id = -1;
            picked_side = PickedSide.Base;
            if ( m_selected != "" ) {
                Rectangle active;
                Point pt;
                for ( int i = 0; i < m_list[m_selected].List.Count; i++ ) {
                    pt = cPoint( m_list[m_selected].List[i].Base );
                    int width = m_data_point_size;
                    pt = new Point( pt.X - width - 1, pt.Y - width - 1 );
                    active = new Rectangle( pt, new Size( width * 2 + 2, width * 2 + 2 ) );
                    if ( active.X <= e.X && e.X <= active.X + active.Width &&
                        active.Y <= e.Y && e.Y <= active.Y + active.Height ) {
                        picked_point_id = m_list[m_selected].List[i].ID;
                        picked_side = PickedSide.Base;
                        //m_before_edit = m_list[m_selected][i].Base;
                        //m_mouse_upped = false;
                        //_number_visible = true;
                        return;
                    }

                    pt = cPoint( m_list[m_selected].List[i].ControlLeft );
                    width = m_control_point_size;
                    pt = new Point( pt.X - width - 1, pt.Y - width - 1 );
                    active = new Rectangle( pt, new Size( width * 2 + 2, width * 2 + 2 ) );
                    if ( active.X <= e.X && e.X <= active.X + active.Width &&
                        active.Y <= e.Y && e.Y <= active.Y + active.Height ) {
                        picked_point_id = m_list[m_selected].List[i].ID;
                        picked_side = PickedSide.Left;
                        //m_before_edit = m_list[m_selected][i].ControlLeft;
                        //m_mouse_upped = false;
                        //_number_visible = true;
                        return;
                    }
                    pt = cPoint( m_list[m_selected].List[i].ControlRight );
                    width = m_control_point_size;
                    pt = new Point( pt.X - width - 1, pt.Y - width - 1 );
                    active = new Rectangle( pt, new Size( width * 2 + 2, width * 2 + 2 ) );
                    if ( active.X <= e.X && e.X <= active.X + active.Width &&
                        active.Y <= e.Y && e.Y <= active.Y + active.Height ) {
                        picked_point_id = m_list[m_selected].List[i].ID;
                        picked_side = PickedSide.Right;
                        //m_before_edit = m_list[m_selected][i].ControlRight;
                        //m_mouse_upped = false;
                        //_number_visible = true;
                        return;
                    }
                }
                //m_picked_point = -1;
                //m_mouse_upped = true;
            }
        }

        private void CurveEditor_MouseUp( object sender, MouseEventArgs e ) {
            Drag = false;
            m_drag_with_space_key = false;
            if ( m_selected != "" && m_mouse_down_mode == MouseDownMode.MovePoint && m_picked_point_id >= 0 && m_mouse_moved ) {
                PointF new_pt = m_list[m_selected][m_picked_point_id].GetPosition( m_picked_side );
                Command run = Command.GCommandEditPosition( m_selected, m_picked_point_id, m_picked_side, new_pt );
                m_list[m_selected][m_picked_point_id].SetPosition( m_picked_side, m_before_edit );
                Register( Execute( run ) );
                this.Invalidate();
            }
            m_mouse_down_mode = MouseDownMode.Nothing;

            m_change_xscale = false;
            m_change_yscale = false;

            _number_visible = false;
        }

        private void CurveEditor_MouseMove( object sender, MouseEventArgs e ) {
            m_mouse_moved = true;
            _mouse_position = e.Location;
            if ( Drag ) {
                bool drag_ok = false;
                if ( m_drag_with_space_key ) {
                    if ( m_spacekey_down ) {
                        drag_ok = true;
                    }
                } else {
                    drag_ok = true;
                }
                if ( drag_ok && m_selected != "" ) {
                    int dx = e.X - m_drag_start.X;
                    int dy = m_drag_start.Y - e.Y;
                    XOffset = m_old_xoffset + dx / m_xscale;
                    YOffset = m_old_yoffset + dy / m_yscale;
                    m_list_minmax[m_selected].Offset = YOffset;
                    this.Invalidate();
                }
            } else if ( m_picked_point_id >= 0 && m_mouse_down_mode == MouseDownMode.MovePoint ) {
                PointF new_pt = sPoint( e.Location );
                BezierPoint bp = m_list[m_selected][m_picked_point_id];
                int centre = m_list[m_selected].GetIndexFromId( m_picked_point_id );
                int left_id = m_list[m_selected].GetIdFromIndex( centre - 1 );
                int right_id = m_list[m_selected].GetIdFromIndex( centre + 1 );
                switch ( m_picked_side ) {
                    case PickedSide.Base:
                        #region PickedSide.Base
                        if ( 1 <= centre ) {
                            if ( new_pt.X < m_list[m_selected][left_id].Base.X ) {
                                new_pt.X = m_list[m_selected][left_id].Base.X;
                            }
                            if ( bp.ControlLeftType != ControlType.None ) {
                                float x1 = m_list[m_selected][left_id].Base.X;
                                float x2 = m_list[m_selected][left_id].ControlRight.X;
                                float x3 = new_pt.X + (bp.ControlLeft.X - bp.Base.X);
                                float x4 = new_pt.X;
                                if ( !IsBezierImplicit( x1, x2, x3, x4 ) ) {
                                    bp.Base = new PointF( bp.Base.X, new_pt.Y );
                                    _number = bp.Base;
                                    this.Invalidate();
                                    return;
                                }
                            }
                        }
                        if ( centre < m_list[m_selected].Count - 1 ) {
                            if ( m_list[m_selected][right_id].Base.X < new_pt.X ) {
                                new_pt.X = m_list[m_selected][right_id].Base.X;
                            }
                            if ( bp.ControlRightType != ControlType.None ) {
                                float x1 = new_pt.X;
                                float x2 = new_pt.X + (bp.ControlRight.X - bp.Base.X);
                                float x3 = m_list[m_selected][right_id].ControlLeft.X;
                                float x4 = m_list[m_selected][right_id].Base.X;
                                if ( !IsBezierImplicit( x1, x2, x3, x4 ) ) {
                                    bp.Base = new PointF( bp.Base.X, new_pt.Y );
                                    _number = bp.Base;
                                    this.Invalidate();
                                    return;
                                }
                            }
                        }
                        bp.Base = new_pt;
                        _number = bp.Base;
                        #endregion
                        break;
                    case PickedSide.Right:
                        #region PickedSide.Right
                        if ( centre < m_list[m_selected].Count - 1 ) {
                            float x1 = bp.Base.X;
                            float x2 = new_pt.X;
                            float x3 = m_list[m_selected][right_id].ControlLeft.X;
                            float x4 = m_list[m_selected][right_id].Base.X;
                            bool is_right = IsBezierImplicit( x1, x2, x3, x4 );

                            bool is_left = true;
                            float dx = new_pt.X - bp.Base.X;
                            float dy = new_pt.Y - bp.Base.Y;
                            float k = 1f;
                            //if ( bp.ControlRightType == ControlType.Master && m_picked_point >= 1 ) {
                            if ( bp.ControlRightType != ControlType.None && centre >= 1 ) {
                                x1 = m_list[m_selected][left_id].Base.X;
                                x2 = m_list[m_selected][left_id].ControlRight.X;
                                float dx1 = (bp.ControlLeft.X - bp.Base.X) * XScale;
                                float dy1 = (bp.ControlLeft.Y - bp.Base.Y) * YScale;
                                float length = (float)Math.Sqrt( dx1 * dx1 + dy1 * dy1 );
                                float tdx = dx * XScale;
                                float tdy = dy * YScale;
                                k = length / (float)Math.Sqrt( tdx * tdx + tdy * tdy );
                                x3 = bp.Base.X - dx * k;
                                x4 = bp.Base.X;
                                is_left = IsBezierImplicit( x1, x2, x3, x4 );
                            }
                            if ( is_right && is_left ) {
                                bp.ControlRight = new_pt;
                                _number = bp.ControlRight;
                                if ( bp.ControlRightType == ControlType.Master && centre >= 1 && bp.ControlLeftType != ControlType.None ) {
                                    bp.ControlLeft = new PointF( bp.Base.X - dx * k, bp.Base.Y - dy * k );
                                }
                                this.Invalidate();
                                return;
                            } else {
                                if ( centre == 0 ) {
                                    bp.ControlRight = new PointF( bp.ControlRight.X, new_pt.Y );
                                    _number = bp.ControlRight;
                                    this.Invalidate();
                                    return;
                                } else {
                                    //とりあえずnew_ptのyだけ替えてみて再評価してみる
                                    new_pt = new PointF( bp.ControlRight.X, new_pt.Y );
                                    dx = new_pt.X - bp.Base.X;
                                    dy = new_pt.Y - bp.Base.Y;
                                    x1 = bp.Base.X;
                                    x2 = new_pt.X;
                                    x3 = m_list[m_selected][right_id].ControlLeft.X;
                                    x4 = m_list[m_selected][right_id].Base.X;
                                    is_right = IsBezierImplicit( x1, x2, x3, x4 );
                                    is_left = true;
                                    if ( bp.ControlRightType == ControlType.Master ) {
                                        x1 = m_list[m_selected][left_id].Base.X;
                                        x2 = m_list[m_selected][left_id].ControlRight.X;
                                        float dx2 = (bp.ControlLeft.X - bp.Base.X) * XScale;
                                        float dy2 = (bp.ControlLeft.Y - bp.Base.Y) * YScale;
                                        float length = (float)Math.Sqrt( dx2 * dx2 + dy2 * dy2 );
                                        float tdx = dx * XScale;
                                        float tdy = dy * YScale;
                                        k = length / (float)Math.Sqrt( tdx * tdx + tdy * tdy );
                                        x3 = bp.Base.X - dx * k;
                                        x4 = bp.Base.X;
                                        is_left = IsBezierImplicit( x1, x2, x3, x4 );
                                    }
                                    if ( is_right && is_left ) {
                                        bp.ControlRight = new PointF( bp.ControlRight.X, new_pt.Y );
                                        _number = bp.ControlRight;
                                        if ( bp.ControlRightType == ControlType.Master ) {
                                            bp.ControlLeft = new PointF( bp.Base.X - dx * k, bp.Base.Y - dy * k );
                                        }
                                        this.Invalidate();
                                        return;
                                    }
                                }
                            }
                        } else {
                            bp.ControlRight = new_pt;
                            _number = bp.ControlRight;
                        }
                        #endregion
                        break;
                    case PickedSide.Left:
                        #region PickedSide.Left
                        if ( centre >= 1 ) {
                            float x1 = m_list[m_selected][left_id].Base.X;
                            float x2 = m_list[m_selected][left_id].ControlRight.X;
                            float x3 = new_pt.X;
                            float x4 = bp.Base.X;
                            bool is_left = IsBezierImplicit( x1, x2, x3, x4 );

                            bool is_right = true;
                            float dx = new_pt.X - bp.Base.X;
                            float dy = new_pt.Y - bp.Base.Y;
                            float k = 1f;
                            if ( bp.ControlLeftType != ControlType.None && centre < m_list[m_selected].Count - 1 ) {
                                //if ( bp.ControlLeftType == ControlType.Master && m_picked_point < m_list[m_selected].Count - 1 ) {
                                float dx1 = (bp.ControlRight.X - bp.Base.X) * XScale;
                                float dy1 = (bp.ControlRight.Y - bp.Base.Y) * YScale;
                                float length = (float)Math.Sqrt( dx1 * dx1 + dy1 * dy1 );
                                float tdx = dx * XScale;
                                float tdy = dy * YScale;
                                k = length / (float)Math.Sqrt( tdx * tdx + tdy * tdy );
                                x1 = bp.Base.X;
                                x2 = bp.Base.X - dx * k;
                                x3 = m_list[m_selected][right_id].ControlLeft.X;
                                x4 = m_list[m_selected][right_id].Base.X;
                                is_right = IsBezierImplicit( x1, x2, x3, x4 );
                            }
                            if ( is_right && is_left ) {
                                bp.ControlLeft = new_pt;
                                _number = bp.ControlLeft;
                                if ( bp.ControlLeftType == ControlType.Master && centre >= 1 && bp.ControlRightType != ControlType.None ) {
                                    bp.ControlRight = new PointF( bp.Base.X - dx * k, bp.Base.Y - dy * k );
                                }
                                this.Invalidate();
                                return;
                            } else {
                                if ( centre == m_list[m_selected].Count - 1 ) {
                                    bp.ControlLeft = new PointF( bp.ControlLeft.X, new_pt.Y );
                                    _number = bp.ControlLeft;
                                    this.Invalidate();
                                    return;
                                } else {
                                    //とりあえずnew_ptのyだけ替えてみて再評価してみる
                                    new_pt = new PointF( bp.ControlLeft.X, new_pt.Y );
                                    dx = new_pt.X - bp.Base.X;
                                    dy = new_pt.Y - bp.Base.Y;
                                    x1 = m_list[m_selected][left_id].Base.X;
                                    x2 = m_list[m_selected][left_id].ControlRight.X;
                                    x3 = new_pt.X;
                                    x4 = bp.Base.X;
                                    is_left = IsBezierImplicit( x1, x2, x3, x4 );
                                    is_right = true;
                                    if ( bp.ControlLeftType == ControlType.Master ) {
                                        float dx2 = (bp.ControlRight.X - bp.Base.X) * XScale;
                                        float dy2 = (bp.ControlRight.Y - bp.Base.Y) * YScale;
                                        float length = (float)Math.Sqrt( dx2 * dx2 + dy2 * dy2 );
                                        float tdx = dx * XScale;
                                        float tdy = dy * YScale;
                                        k = length / (float)Math.Sqrt( tdx * tdx + tdy * tdy );
                                        x1 = bp.Base.X;
                                        x2 = bp.Base.X - dx * k;
                                        x3 = m_list[m_selected][right_id].ControlLeft.X;
                                        x4 = m_list[m_selected][right_id].Base.X;
                                        is_right = IsBezierImplicit( x1, x2, x3, x4 );
                                    }
                                    if ( is_right && is_left ) {
                                        bp.ControlLeft = new PointF( bp.ControlLeft.X, new_pt.Y );
                                        _number = bp.ControlLeft;
                                        if ( bp.ControlLeftType == ControlType.Master ) {
                                            bp.ControlRight = new PointF( bp.Base.X - dx * k, bp.Base.Y - dy * k );
                                        }
                                        this.Invalidate();
                                        return;
                                    }
                                }
                            }
                        } else {
                            bp.ControlLeft = new_pt;
                            _number = bp.ControlLeft;
                        }
                        //昔の=>
                        /*if ( m_picked_point >= 1 ) {
                            float x1 = m_list[m_selected][m_picked_point - 1].Base.X;
                            float x2 = m_list[m_selected][m_picked_point - 1].ControlRight.X;
                            float x3 = new_pt.X;
                            float x4 = bp.Base.X;
                            if ( !IsBezierImplicit( x1, x2, x3, x4 ) ) {
                                bp.ControlLeft = new PointF( bp.ControlLeft.X, new_pt.Y );
                                this.Invalidate();
                                return;
                            }
                        }
                        bp.ControlLeft = new_pt;*/
                        //<=ここまで
                        #endregion
                        break;
                }
                this.Invalidate();
            } else if ( m_change_xscale ) {
                float new_scale = m_old_scale * (float)Math.Pow( 10, (e.X - m_change_origin) / CHANGE_SCALE_ORDER );
                if ( new_scale < MinXScale ) {
                    new_scale = MinXScale;
                } else if ( MaxXScale < new_scale ) {
                    new_scale = MaxXScale;
                }
                if ( new_scale != XScale ) {
                    XOffset = (m_change_origin) * (1f / new_scale - 1f / XScale) + XOffset;
                    XScale = new_scale;
                    this.Invalidate();
                }
            } else if ( m_change_yscale ) {
                float new_scale = m_old_scale * (float)Math.Pow( 10, (e.Y - m_change_origin) / CHANGE_SCALE_ORDER );
                if ( new_scale < MinYScale ) {
                    new_scale = MinYScale;
                } else if ( MaxYScale < new_scale ) {
                    new_scale = MaxYScale;
                }
                if ( new_scale != YScale ) {
                    YOffset = m_change_origin * (1f / new_scale - 1f / YScale) + YOffset;
                    YScale = new_scale;
                    m_list_minmax[m_selected].Offset = YOffset;
                    m_list_minmax[m_selected].Scale = YScale;
                    this.Invalidate();
                }
            }
            this.Invalidate();
        }

        private void CurveEditor_Resize( object sender, EventArgs e ) {
            this.Invalidate();
        }
        
        private void CurveEditor_MouseDoubleClick( object sender, MouseEventArgs e ) {
            if ( m_selected == "" ) {
                return;
            }
            float x = sX( e.X );
            float y = sY( e.Y );

            int picked_id;
            PickedSide picked_side;
            DetectSelectedPoint( e.Location, out picked_id, out picked_side );
            if ( picked_id >= 0 ) {
                m_picked_point_id = picked_id;
                m_picked_side = picked_side;
                m_before_edit = m_list[m_selected][m_picked_point_id].GetPosition( m_picked_side );
                using ( LipSync.SetSize<float> dlg = new LipSync.SetSize<float>(
                    _( "Numeric entry" ),
                    "x",
                    "y",
                    m_before_edit.X,
                    m_before_edit.Y ) ) {
                    if ( dlg.ShowDialog() == DialogResult.OK ) {
                        SizeF res = new SizeF( dlg.ResultWidth, dlg.ResultHeight );
                        PointF new_pt = new PointF( res.Width, res.Height );
                        Command run = Command.GCommandEditPosition( m_selected, m_picked_point_id, m_picked_side, new_pt );
                        m_list[m_selected][m_picked_point_id].SetPosition( m_picked_side, m_before_edit );
                        Register( Execute( run ) );
                        this.Invalidate();
                    }
                }
            } else {
                float handle_length;
                float slope = 0f;
                if ( m_list[m_selected].Count == 0 ) {
                    handle_length = x * 0.5f;
                } else {
                    int right_point = -1;
                    //右側の点を検索
                    for ( int i = 0; i < m_list[m_selected].List.Count; i++ ) {
                        if ( x == m_list[m_selected].List[i].Base.X ) {
                            //xが等しくなる位置にはデータ点を追加できない仕様。
                            return;
                        }
                        if ( x < m_list[m_selected].List[i].Base.X ) {
                            right_point = i;
                            break;
                        }
                    }
                    if ( right_point == -1 ) {
                        // 最も右
                        float dx = Math.Abs( x - m_list[m_selected].List[m_list[m_selected].List.Count - 1].Base.X );
                        handle_length = dx / 2;
                    } else if ( right_point == 0 ) {
                        float dx = Math.Abs( m_list[m_selected].List[0].Base.X - x );
                        handle_length = dx / 2;
                    } else {
                        float dx_r = Math.Abs( m_list[m_selected].List[right_point].Base.X - x );
                        float dx_l = Math.Abs( x - m_list[m_selected].List[right_point - 1].Base.X );
                        handle_length = Math.Min( dx_r, dx_l ) / 2;
                        slope = (m_list[m_selected].List[right_point].Base.Y - m_list[m_selected].List[right_point - 1].Base.Y) /
                            (m_list[m_selected].List[right_point].Base.X - m_list[m_selected].List[right_point - 1].Base.X);
                    }
                }

                PointF p_left, p_right;
                p_right = new PointF( x + handle_length, y + handle_length * slope );
                p_left = new PointF( x - handle_length, y - handle_length * slope );
                BezierPoint bp = new BezierPoint( new PointF( x, y ),
                                                  p_left,
                                                  p_right );
                bp.ControlLeftType = m_last_added_type;
                bp.ControlRightType = m_last_added_type;
                Command run = Command.GCommandAdd( m_selected, bp );
                Register( Execute( run ) );
                m_picked_side = PickedSide.Base;
                for ( int i = 0; i < m_list[m_selected].List.Count; i++ ) {
                    BezierPoint bpoint = m_list[m_selected].List[i];
                    if ( x == bpoint.Base.X ) {
                        m_picked_point_id = bpoint.ID;
                        break;
                    }
                }
            }
            this.Invalidate();
        }
        
        private void CurveEditor_PreviewKeyDown( object sender, PreviewKeyDownEventArgs e ) {
            if ( m_selected != "" && m_picked_point_id >= 0 ) {
                if ( m_picked_side != PickedSide.Base ) {
                    switch ( e.KeyCode ) {
                        case Keys.H:
                            if ( m_list[m_selected][m_picked_point_id].GetControlType( m_picked_side ) != ControlType.Master ) {
                                Command run = Command.GCommandChangeType( m_selected, m_picked_point_id, m_picked_side, ControlType.Master );
                                Register( Execute( run ) );
                            }
                            break;
                        case Keys.V:
                            if ( m_list[m_selected][m_picked_point_id].GetControlType( m_picked_side ) != ControlType.Normal ) {
                                Command run = Command.GCommandChangeType( m_selected, m_picked_point_id, m_picked_side, ControlType.Normal );
                                Register( Execute( run ) );
                            }
                            break;
                        case Keys.Delete:
                            if ( m_list[m_selected][m_picked_point_id].GetControlType( m_picked_side ) != ControlType.None ) {
                                Command run = Command.GCommandChangeType( m_selected, m_picked_point_id, m_picked_side, ControlType.None );
                                Register( Execute( run ) );
                            }
                            break;
                    }
                    this.Invalidate();
                } else {
                    ControlType target;
                    switch ( e.KeyCode ) {
                        case Keys.H:
                            target = ControlType.Master;
                            break;
                        case Keys.V:
                            target = ControlType.Normal;
                            break;
                        case Keys.Delete:
                            Command run = Command.GCommandDelete( m_selected, m_picked_point_id );
                            Register( Execute( run ) );
                            m_picked_point_id = -1;
                            this.Invalidate();
                            return;
                        default:
                            return;
                    }
                    BezierPoint bpoint = m_list[m_selected][m_picked_point_id];
                    if ( bpoint != null ) {
                        if ( m_list[m_selected][m_picked_point_id].ControlLeftType != target ||
                             m_list[m_selected][m_picked_point_id].ControlRightType != target ) {
                            BezierPoint bp = m_list[m_selected][m_picked_point_id].Clone();
                            bp.ControlLeftType = target;
                            bp.ControlRightType = target;
                            Command run = Command.GCommandEdit( m_selected, m_picked_point_id, bp );
                            Register( Execute( run ) );
                            this.Invalidate();
                        }
                    }
                }
            }
        }

        private Command Execute( Command run ) {
#if DEBUG
            Common.DebugWriteLine( "CurveEditor.Execute" );
            /*Common.DebugWriteLine( "    before" );
            for ( int i = 0; i < m_list[m_selected].List.Count; i++ ) {
                BezierPoint bp = m_list[m_selected].List[i];
                Common.DebugWriteLine( "        Base.X=" + bp.Base.X + ", ID=" + bp.ID );
            }*/
#endif
            Command ret = null;
            switch ( run.Type ) {
                case CommandType.Position:
                    switch ( run.Side ) {
                        case PickedSide.Base:
                            ret = Command.GCommandEditPosition( run.ID, run.PointID, run.Side, m_list[run.ID][run.PointID].Base );
                            break;
                        case PickedSide.Left:
                            ret = Command.GCommandEditPosition( run.ID, run.PointID, run.Side, m_list[run.ID][run.PointID].ControlLeft );
                            break;
                        case PickedSide.Right:
                            ret = Command.GCommandEditPosition( run.ID, run.PointID, run.Side, m_list[run.ID][run.PointID].ControlRight );
                            break;
                    }
#if DEBUG
                    LipSync.Common.DebugWriteLine( "    before;Position=" + m_list[run.ID][run.PointID].GetPosition( PickedSide.Base ) );
#endif
                    m_list[run.ID][run.PointID].SetPosition( run.Side, run.Position );
#if DEBUG
                    for ( int i = 0; i < AppManager.SaveData.m_telop_ex2.Count; i++ ) {
                        if ( AppManager.SaveData.m_telop_ex2[i].Text == run.ID ) {

                        }
                    }
#endif
                    break;
                case CommandType.Type:
                    switch ( run.Side ) {
                        case PickedSide.Left:
                            ret = Command.GCommandChangeType( run.ID, run.PointID, run.Side, m_list[run.ID][run.PointID].ControlLeftType );
                            m_list[run.ID][run.PointID].ControlLeftType = run.ControlType;
                            break;
                        case PickedSide.Right:
                            ret = Command.GCommandChangeType( run.ID, run.PointID, run.Side, m_list[run.ID][run.PointID].ControlRightType );
                            m_list[run.ID][run.PointID].ControlRightType = run.ControlType;
                            break;
                    }
                    break;
                case CommandType.Add:
                    BezierPoint bp = run.BezierPoint.Clone();
                    bp.ID = m_list[run.ID].GetNextID();
                    ret = Command.GCommandDelete( run.ID, bp.ID );
                    m_list[run.ID].Add( bp );
                    break;
                case CommandType.Delete:
                    ret = Command.GCommandAdd( run.ID, m_list[run.ID][run.PointID] );
                    m_list[run.ID].RemoveAt( run.PointID );
                    break;
                case CommandType.Edit:
                    ret = Command.GCommandEdit( run.ID, run.PointID, m_list[run.ID][run.PointID] );
                    m_list[run.ID][run.PointID] = run.BezierPoint.Clone();
                    break;
                default:
                    return null;
            }
            if ( this.CurveEdited != null ) {
                CurveEdited();
            }
#if DEBUG
            /*Common.DebugWriteLine( "    after" );
            for ( int i = 0; i < m_list[m_selected].List.Count; i++ ) {
                BezierPoint bp = m_list[m_selected].List[i];
                Common.DebugWriteLine( "        Base.X=" + bp.Base.X + ", ID=" + bp.ID );
            }*/
#endif
            return ret;
        }
        
        /// <summary>
        /// アンドゥ処理行います
        /// </summary>
        public void Undo() {
            if ( IsUndoAvailable ) {
                Command run = m_commands[m_command_position].Clone();
                m_commands[m_command_position] = Execute( run );
                m_command_position--;
                this.Invalidate();
            }
        }
                
        /// <summary>
        /// リドゥ処理行います
        /// </summary>
        public void Redo() {
            if ( IsRedoAvailable ) {
                Command run = m_commands[m_command_position + 1].Clone();
                m_commands[m_command_position + 1] = Execute( run );
                m_command_position++;
                this.Invalidate();
            }
        }
        
        /// <summary>
        /// リドゥ操作が可能かどうかを表す値を取得します
        /// </summary>
        public bool IsRedoAvailable {
            get {
                if ( m_command_position + 1 < m_commands.Count ) {
                    return true;
                } else {
                    return false;
                }
            }
        }
        
        /// <summary>
        /// アンドゥ操作が可能かどうかを表す値を取得します
        /// </summary>
        public bool IsUndoAvailable {
            get {
                if ( 0 > m_command_position ) {
                    return false;
                } else {
                    return true;
                }
            }
        }

        /// <summary>
        /// コマンドバッファに指定されたコマンドを登録します
        /// </summary>
        /// <param name="command"></param>
        void Register( Command command ) {
            if ( m_command_position == m_commands.Count - 1 ) {
                // 新しいコマンドバッファを追加する場合
                m_commands.Add( command.Clone() );
                m_command_position = m_commands.Count - 1;
            } else {
                // 既にあるコマンドバッファを上書きする場合
                m_commands[m_command_position + 1].Dispose();
                m_commands[m_command_position + 1] = command.Clone();
                for ( int i = m_commands.Count - 1; i >= m_command_position + 2; i-- ) {
                    m_commands.RemoveAt( i );
                }
                m_command_position++;
            }
        }
        
        private void CurveEditor_KeyDown( object sender, KeyEventArgs e ) {
            if ( e.KeyCode == Keys.Space ) {
                m_spacekey_down = true;
                this.Cursor = HAND;
            } else {
                m_spacekey_down = false;
                this.Cursor = Cursors.Default;
            }
        }

        private void CurveEditor_KeyUp( object sender, KeyEventArgs e ) {
            m_spacekey_down = false;
            this.Cursor = Cursors.Default;
        }

        void _num_input_FormClosing( object sender, FormClosingEventArgs e ) {
            e.Cancel = true;
        }

        /// <summary>
        /// データ点の数値入力用のコンテキストメニューを初期化します
        /// </summary>
        private void InitializeContextMenu() {
            if( _cmenu != null ) {
                _cmenu.Dispose();
            }
            if( _cmenuNumericInput != null ) {
                _cmenuNumericInput.Dispose();
            }
            _cmenu = new ContextMenuStrip();
            _cmenu.ShowCheckMargin = false;
            _cmenu.ShowImageMargin = false;
            _cmenuNumericInput = new ToolStripMenuItem();
            _cmenu.Items.AddRange( new ToolStripItem[] {
                _cmenuNumericInput} );
            _cmenu.Name = "cmenu";
            _cmenu.Size = new System.Drawing.Size( 135, 26 );
            _cmenu.Font = this.Font;
            _cmenuNumericInput.Name = "cmenuNumericInput";
            _cmenuNumericInput.Size = new System.Drawing.Size( 134, 22 );
            _cmenuNumericInput.Text = _( "Numeric entry" ) + "(&N)";
            _cmenuNumericInput.Click += new EventHandler( _cmenuNumericInput_Click );
        }

        void _cmenuNumericInput_Click( object sender, EventArgs e ) {
            m_before_edit = m_list[m_selected][m_picked_point_id].GetPosition( m_picked_side );
            using ( LipSync.SetSize<float> dlg = new LipSync.SetSize<float>(
                _( "Numeric entry" ),
                "x",
                "y",
                m_before_edit.X,
                m_before_edit.Y ) ) {
                if ( dlg.ShowDialog() == DialogResult.OK ) {
                    SizeF res = new SizeF( dlg.ResultWidth, dlg.ResultHeight );
                    PointF new_pt = new PointF( res.Width, res.Height );
                    Command run = Command.GCommandEditPosition( m_selected, m_picked_point_id, m_picked_side, new_pt );
                    m_list[m_selected][m_picked_point_id].SetPosition( m_picked_side, m_before_edit );
                    Register( Execute( run ) );
                    this.Invalidate();
                }
            }
        }

        private void CurveEditor_MouseClick( object sender, MouseEventArgs e ) {
            if ( (e.Button & MouseButtons.Right) == MouseButtons.Right ) {
                DetectSelectedPoint( e.Location, out m_picked_point_id, out m_picked_side );
#if DEBUG
                Common.DebugWriteLine( "CureveEditor_MouseClick" );
                Common.DebugWriteLine( "    m_picked_point_id=" + m_picked_point_id );
                Common.DebugWriteLine( "    m_picked_side=" + m_picked_side );
#endif
                if ( m_picked_point_id >= 0 ) {
                    InitializeContextMenu();
                    _cmenu.Show( this, e.Location );
                    _number_visible = false;
#if DEBUG
                    LipSync.Common.DebugWriteLine( "MouseClick, m_picked_point_id=" + m_picked_point_id );
#endif
                }
             }
        }

        private void CurveEditor_FontChanged( object sender, EventArgs e ) {
            if ( _cmenu != null ) {
                _cmenu.Font = this.Font;
            }
        }
    }


    internal class Command /*: ICloneable*/ {
        string m_id;
        //int m_picked_index;
        PickedSide m_picked_side;
        PointF m_new_position;
        CommandType m_command_type;
        ControlType m_control_type;
        BezierPoint m_bp;
        //float m_x;
        int m_pid;

        public override string ToString() {
            return "{ID=" + ID + ", PointID=" + PointID + ", Side=" + Side + ", CommandType=" + Type + ", Position=" + Position + "}";
        }

        public Command Clone() {
            Command result = new Command();
            result.m_id = this.m_id;
            //result.m_picked_index = this.m_picked_index;
            result.m_new_position = this.m_new_position;
            result.m_command_type = this.m_command_type;
            result.m_control_type = this.m_control_type;
            if ( this.m_bp != null ) {
                result.m_bp = this.m_bp.Clone();
            }
            result.m_pid = this.m_pid;
            result.m_picked_side = this.m_picked_side;
            return result;
        }

        public void Dispose(){
            m_bp = null;
        }

        public static Command GCommandEditPosition( string ID, int picked_id, PickedSide picked_side, PointF new_position ) {
            Command ret = new Command();
            ret.m_id = ID;
            ret.m_pid = picked_id;
            ret.m_picked_side = picked_side;
            ret.m_new_position = new_position;
            ret.m_command_type = CommandType.Position;
            ret.m_control_type = ControlType.None;
            return ret;
        }
        
        public static Command GCommandChangeType( string ID, int picked_id, PickedSide picked_side, ControlType control_type ){
            Command ret = new Command();
            ret.m_id = ID;
            ret.m_pid = picked_id;
            ret.m_picked_side = picked_side;
            ret.m_command_type = CommandType.Type;
            ret.m_control_type = control_type;
            return ret;
        }

        public static Command GCommandAdd( string ID, BezierPoint point ) {
            Command ret = new Command();
            ret.m_id = ID;
            if ( point != null ) {
                ret.m_bp = (BezierPoint)point.Clone();
            }
            ret.m_command_type = CommandType.Add;
            return ret;
        }

        public static Command GCommandDelete( string ID, /*float*/int pid ) {
            Command ret = new Command();
            ret.m_id = ID;
            //this.m_x = x;
            ret.m_pid = pid;
            ret.m_command_type = CommandType.Delete;
            return ret;
        }

        public static Command GCommandNothing() {
            return new Command();
        }

        private Command() {
            this.m_command_type = CommandType.None;
        }

        public static Command GCommandEdit( string ID, int picked_id, BezierPoint point ) {
            Command ret = new Command();
            ret.m_id = ID;
            ret.m_pid = picked_id;
            if ( point != null ) {
                ret.m_bp = (BezierPoint)point.Clone();
            }
            ret.m_command_type = CommandType.Edit;
            return ret;
        }

        public int PointID {
            get {
                return m_pid;
            }
        }

        /*public float X {
            get {
                return m_x;
            }
        }*/

        public BezierPoint BezierPoint {
            get {
                return m_bp;
            }
        }

        public CommandType Type{
            get{
                return m_command_type;
            }
        }

        public ControlType ControlType {
            get {
                return m_control_type;
            }
        }

        public string ID{
            get{
                return m_id;
            }
        }

        public PickedSide Side{
            get{
                return m_picked_side;
            }
        }

        public PointF Position{
            get{
                return m_new_position;
            }
        }
    }

    internal enum CommandType {
        Position,//単に位置を変更する
        Type,//制御点のタイプを変更する
        Add,
        Delete,
        None,
        Edit,
    }


    public enum PickedSide {
        Right,
        Base,
        Left,
    }


    public enum PointType {
        Circle,
        Rectangle,
    }

    public enum XLabel {
        None,
        Top,
        Bottom,
    }

    public enum YLabel {
        None,
        Left,
        Right,
    }

    [Serializable]
    public class BezierChain : IDisposable, ICloneable {
        private List<BezierPoint> list;
        private float m_default = 0f;
        private Color m_color;

        public bool GetKeyMinMax( out float min, out float max ) {
            if ( list.Count == 0 ) {
                min = 0f;
                max = 0f;
                return false;
            }
            min = float.MaxValue;
            max = float.MinValue;
            for ( int i = 0; i < list.Count; i++ ) {
                min = Math.Min( min, list[i].Base.X );
                max = Math.Max( max, list[i].Base.X );
            }
            return true;
        }

        public int GetIndexFromId( int id ) {
            for ( int i = 0; i < list.Count; i++ ) {
                if ( list[i].ID == id ) {
                    return i;
                }
            }
            return -1;
        }

        public int GetIdFromIndex( int index ) {
            if ( 0 <= index && index < list.Count ) {
                return list[index].ID;
            }
            return -1;
        }

        public List<BezierPoint> List {
            get {
                return list;
            }
            set {
                list = value;
            }
        }

        [OnDeserialized]
        void onDeserialized( StreamingContext sc ) {
            for ( int i = 0; i < list.Count; i++ ) {
                list[i].ID = i;
                list[i].Order = i;
            }
        }

        public void Sort() {
            //list.Sort( new BezierChainOrderIgnoaringComparator() );
            list.Sort();
            for ( int i = 0; i < list.Count; i++ ) {
                list[i].Order = i;
            }
        }

        public void Dispose() {
            if ( list != null ) {
                list.Clear();
            }
        }

        public int GetNextID() {
            int max = -1;
            for ( int i = 0; i < list.Count; i++ ) {
                max = Math.Max( max, list[i].ID );
            }
            return max + 1;
        }

        public void GetValueMinMax( out float min, out float max ){
            //todo: ベジエが有効なときに、曲線の描く最大値、最小値も考慮
            min = Default;
            max = Default;
            foreach ( BezierPoint bp in list ) {
                min = Math.Min( min, bp.Base.Y );
                max = Math.Max( max, bp.Base.Y );
            }
        }

        public object Clone() {
            BezierChain result = new BezierChain( this.m_color );
            foreach ( BezierPoint bp in list ) {
                result.list.Add( bp.Clone() );
            }
            result.m_default = this.m_default;
            return result;
        }

        public float Default {
            get {
                return m_default;
            }
            set {
                m_default = value;
            }
        }

        public BezierChain( Color curve ) {
            list = new List<BezierPoint>();
            m_color = curve;
        }

        public BezierPoint this[int id] {
            get {
                for ( int i = 0; i < list.Count; i++ ) {
                    if ( list[i].ID == id ) {
                        return list[i];
                    }
                }
                return null;
            }
            set {
                for ( int i = 0; i < list.Count; i++ ) {
                    if ( list[i].ID == id ) {
                        list[i] = value;
                        return;
                    }
                }
                throw new Exception( "invalid point id" );
            }
        }

        public Color Color {
            get {
                return m_color;
            }
            set {
                m_color = value;
            }
        }

        public void Add( BezierPoint bp ) {
            if ( list == null ) {
                list = new List<BezierPoint>();
                m_color = Color.Black;
            }
#if DEBUG
            Common.DebugWriteLine( "BezierChain.Add" );
            Common.DebugWriteLine( "    before" );
            for ( int i = 0; i < list.Count; i++ ) {
                Common.DebugWriteLine( "        Base.X=" + list[i].Base.X + ", Order=" + list[i].Order );
            }
#endif
            bool found = false;
            for ( int i = 0; i < list.Count - 1; i++ ) {
                if ( list[i].Base.X <= bp.Base.X && bp.Base.X < list[i + 1].Base.X ) {
                    bp.Order = list[i].Order + 1;
                    for ( int j = i + 1; j < list.Count; j++ ) {
                        list[j].Order = list[j].Order + 1;
                    }
                    found = true;
                    break;
                }
            }
            if ( !found ) {
                if ( list.Count == 0 ){
                    bp.Order = 0;
                }else{
                    bp.Order = list[list.Count - 1].Order + 1;
                }
            }
            list.Add( bp );
            Sort();
#if DEBUG
            Common.DebugWriteLine( "BezierChain.Add" );
            Common.DebugWriteLine( "    after" );
            for ( int i = 0; i < list.Count; i++ ) {
                Common.DebugWriteLine( "        Base.X=" + list[i].Base.X + ", Order=" + list[i].Order );
            }
#endif
        }

        public void RemoveAt( int id ) {
            for ( int i = 0; i < list.Count; i++ ) {
                if ( list[i].ID == id ) {
                    list.RemoveAt( i );
                    Sort();
                    return;
                }
            }
        }

        /*public void RemoveAt( float x ) {
            for ( int i = 0; i < list.Count; i++ ) {
                if ( list[i].Base.X == x ) {
                    list.RemoveAt( i );
                    break;
                }
            }
        }*/

        public int Count {
            get {
                if ( list == null ) {
                    return 0;
                }
                return list.Count;
            }
        }

        public float GetValue( float x ) {
            for ( int i = 0; i < list.Count - 1; i++ ) {
                if ( list[i].Base.X <= x && x <= list[i + 1].Base.X ) {
                    if ( list[i].ControlRightType == ControlType.None && list[i + 1].ControlLeftType == ControlType.None ) {
                        PointF p1 = list[i].Base;
                        PointF p2 = list[i + 1].Base;
                        float slope = (p2.Y - p1.Y) / (p2.X - p1.X);
                        return p1.Y + slope * (x - p1.X);
                    } else {
                        float x1 = list[i].Base.X;
                        float x2 = list[i].ControlRight.X;
                        float x3 = list[i + 1].ControlLeft.X;
                        float x4 = list[i + 1].Base.X;
                        float a3 = x4 - 3 * x3 + 3 * x2 - x1;
                        float a2 = 3 * x3 - 6 * x2 + 3 * x1;
                        float a1 = 3 * (x2 - x1);
                        float a0 = x1;
                        if ( x1 == x ) {
                            return list[i].Base.Y;
                        } else if ( x4 == x ) {
                            return list[i + 1].Base.Y;
                        } else {
                            float t = SolveCubicEquation( a3, a2, a1, a0, x );
                            x1 = list[i].Base.Y;
                            x2 = list[i].ControlRight.Y;
                            x3 = list[i + 1].ControlLeft.Y;
                            x4 = list[i + 1].Base.Y;
                            a3 = x4 - 3 * x3 + 3 * x2 - x1;
                            a2 = 3 * x3 - 6 * x2 + 3 * x1;
                            a1 = 3 * (x2 - x1);
                            a0 = x1;
                            return ((a3 * t + a2) * t + a1) * t + a0;
                        }
                    }
                }
            }
            return m_default;
        }

        /// <summary>
        /// 3次方程式a3*x^3 + a2*x^2 + a1*x + a0 = ansの解をニュートン法を使って計算します。ただし、単調増加である必要がある。
        /// </summary>
        /// <param name="a3"></param>
        /// <param name="a2"></param>
        /// <param name="a1"></param>
        /// <param name="a0"></param>
        /// <param name="ans"></param>
        /// <param name="suggested_t"></param>
        /// <returns></returns>
        private static float SolveCubicEquation( float a3, float a2, float a1, float a0, float ans ) {
            double EPSILON = 1e-9;
            double suggested_t = 0.4;
            double a3_3 = a3 * 3.0;
            double a2_2 = a2 * 2.0;
            while ( (a3_3 * suggested_t + a2_2) * suggested_t + a1 == 0.0 ) {
                suggested_t += 0.1;
            }
            double x = suggested_t;
            double new_x = suggested_t;
            for( int i = 0; i < 5000; i++ ){
                new_x = x - (((a3 * x + a2) * x + a1) * x + a0 - ans) / ((a3_3 * x + a2_2) * x + a1);
                if ( Math.Abs( new_x - x ) < EPSILON * new_x ) {
                    break;
                }
                x = new_x;
            }
            return (float)new_x;
        }
    }

    public enum ControlType {
        None,
        Normal,
        Master,
    }
    
    /// <summary>
    /// ベジエ曲線を構成するデータ点。
    /// </summary>
    [Serializable]
    public class BezierPoint : IComparable<BezierPoint> {
        PointF m_base;
        internal PointF m_control_left;
        internal PointF m_control_right;
        ControlType m_type_left;
        ControlType m_type_right;
        [NonSerialized]
        int m_id;
        [OptionalField]
        public int Order;

        public int ID {
            get {
                return m_id;
            }
            internal set {
                m_id = value;
            }
        }

        public override string ToString() {
            return "m_base=" + m_base.X + "," + m_base.Y + "\n" +
                "m_control_left=" + m_control_left.X + "," + m_control_left.Y + "\n" +
                "m_control_right=" + m_control_right.X + "," + m_control_right.Y + "\n" +
                "m_type_left=" + m_type_left + "\n" +
                "m_type_right=" + m_type_right + "\n";
        }

        public BezierPoint( PointF p1, PointF left, PointF right ) {
            m_base = p1;
            m_control_left = new PointF( left.X - m_base.X, left.Y - m_base.Y );
            m_control_right = new PointF( right.X - m_base.X, right.Y - m_base.Y );
            m_type_left = ControlType.None;
            m_type_right = ControlType.None;
        }

        public BezierPoint Clone() {
            BezierPoint result = new BezierPoint( this.Base, this.ControlLeft, this.ControlRight );
            result.m_control_left = this.m_control_left;
            result.m_control_right = this.m_control_right;
            result.m_type_left = this.m_type_left;
            result.m_type_right = this.m_type_right;
            result.Order = this.Order;
            result.m_id = this.m_id;
            return result;
        }

        public int CompareTo( BezierPoint item ) {
            if ( this.Base.X > item.Base.X ) {
                return 1;
            } else if ( this.Base.X < item.Base.X ) {
                return -1;
            } else {
                return this.Order - item.Order;
                /*if ( this.ID > item.ID ) {
                    return 1;
                } else if ( this.ID < item.ID ) {
                    return -1;
                } else {
                    return 0;
                }*/
            }
        }

        public PointF Base {
            get {
                return m_base;
            }
            set {
                m_base = value;
            }
        }

        public void SetPosition( PickedSide picked_side, PointF new_position ) {
            if ( picked_side == PickedSide.Base ) {
                this.Base = new_position;
            } else if ( picked_side == PickedSide.Left ) {
                this.m_control_left = new PointF( new_position.X - this.Base.X, new_position.Y - this.Base.Y);
            } else {
                this.m_control_right = new PointF( new_position.X - this.Base.X, new_position.Y - this.Base.Y );
            }
        }

        public PointF GetPosition( PickedSide picked_side ) {
            if ( picked_side == PickedSide.Base ) {
                return this.Base;
            } else if ( picked_side == PickedSide.Left ) {
                return this.ControlLeft;
            } else {
                return this.ControlRight;
            }
        }

        public ControlType GetControlType( PickedSide picked_side ) {
            if ( picked_side == PickedSide.Left ) {
                return this.ControlLeftType;
            } else if ( picked_side == PickedSide.Right ) {
                return this.ControlRightType;
            } else {
                return ControlType.None;
            }
        }

        public PointF ControlLeft {
            get {
                if ( m_type_left != ControlType.None ) {
                    return new PointF( m_base.X + m_control_left.X, m_base.Y + m_control_left.Y );
                } else {
                    return m_base;
                }
            }
            set {
                m_control_left = new PointF( value.X - m_base.X, value.Y - m_base.Y );
            }
        }

        public PointF ControlRight {
            get {
                if ( m_type_right != ControlType.None ) {
                    return new PointF( m_base.X + m_control_right.X, m_base.Y + m_control_right.Y );
                } else {
                    return m_base;
                }
            }
            set {
                m_control_right = new PointF( value.X - m_base.X, value.Y - m_base.Y );
            }
        }

        public ControlType ControlLeftType {
            get {
                return m_type_left;
            }
            set {
                m_type_left = value;
            }
        }

        public ControlType ControlRightType {
            get {
                return m_type_right;
            }
            set {
                m_type_right = value;
            }
        }
    }

}
