/*
 * Form1_EventHandler.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.Windows.Forms;

namespace LipSync {

    partial class Form1 : Form {
        /// <summary>
        /// テロップを追加します
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_addTelop( object sender, EventArgs e ) {
            if ( m_text_edit && m_text != null ) {
                Command run = Command.GCommandEditTelop( m_text_id, AppManager.SaveData[m_text_id] );
                AppManager.SaveData[m_text_id].Text = m_text_original;
                AppManager.Register( AppManager.SaveData.Execute( run ) );
                AppManager.Edited = true;

                m_text.Dispose();
                m_text = null;
                m_text_edit = false;
            }

            int id = AppManager.SaveData.GetNextID();
            Telop work = new Telop( id );
            work.Text = "(none)";
            work.Start = Now;
            work.End = work.Start + 1f;
            Command run2 = Command.GCommandAddTelop( work );
            Command inv = AppManager.SaveData.Execute( run2 );
            Telop.DecideLane( AppManager.SaveData.m_telop_ex2 );
            property.Editing = new ZorderItem( work.Text, ZorderItemType.telop, id );
            AppManager.Register( inv );
            UpdateObjectList();
            UpdateEditHandle();
            SetVScrollRange();
            AppManager.Edited = true;
        }

        /// <summary>
        /// エントリのON/OFFを反転してコピーします
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void h_copyTimeTableInvert( object sender, EventArgs e ) {
            int group = m_clicked.group;
            int track = m_clicked.track;
            if ( m_copied_timetable != null ) {
                m_copied_timetable.Dispose();
                m_copied_timetable = null;
            }
            TimeTableType type = m_clicked.type;
            if( type != TimeTableType.vsq && type != TimeTableType.character && type != TimeTableType.another && type != TimeTableType.plugin ){
                return;
            }
            TimeTable table = (TimeTable)AppManager.SaveData[m_clicked.type, m_clicked.group][m_clicked.track].Clone();
            table.Type = TimeTableType.another;
            m_copied_timetable = (TimeTable)table.Clone();
            m_copied_timetable.Clear();
            for ( int i = 0; i < table.Count; i++ ) {
                if ( i > 0 ) {
                    m_copied_timetable.Add( new TimeTableEntry( table[i - 1].end, table[i].begin, "" ) );
                } else {
                    if ( table[0].begin > 0f ) {
                        m_copied_timetable.Add( new TimeTableEntry( 0f, table[0].begin, "" ) );
                    }
                }
            }
            if ( table[table.Count - 1].end < AppManager.SaveData.m_totalSec ) {
                m_copied_timetable.Add( new TimeTableEntry( table[table.Count - 1].end, AppManager.SaveData.m_totalSec, "" ) );
            }
        }

        private void h_shiftEntries( object sender, EventArgs e ) {
            int group = m_clicked.group;
            int track = m_clicked.track;
            int entry = m_clicked.entry;
            TimeTableType type = m_clicked.type;
            if ( type != TimeTableType.vsq && 
                 type != TimeTableType.character && 
                 type != TimeTableType.another && 
                 type != TimeTableType.plugin &&
                 type != TimeTableType.telop ) {
                return;
            }
            using ( InputBox ib = new InputBox( _( "Shift all time-tables" ), _( "Input shift time in second (you can enter minus value)" ) ) ) {
                if ( ib.ShowDialog() == DialogResult.OK ) {
                    float shift;
                    try {
                        shift = float.Parse( ib.rText );
                    } catch {
                        MessageBox.Show( _( "Invalid value has been entered" ),
                                         _( "Error" ),
                                         MessageBoxButtons.OK,
                                         MessageBoxIcon.Exclamation );
                        return;
                    }
                    Command run = Command.GCommandShiftTimeTable( type, (type == TimeTableType.character ? group : track), shift );
                    AppManager.Register( AppManager.SaveData.Execute( run ) );
                    AppManager.Edited = true;
                }
            }
        }

        private void h_splitEntry( object sender, EventArgs e ) {
            TimeTableType type = m_clicked.type;
            int group = m_clicked.group;
            int track = m_clicked.track;
            int entry = m_clicked.entry;
            if ( type != TimeTableType.character && 
                 type != TimeTableType.another && 
                 type != TimeTableType.plugin &&
                 type != TimeTableType.telop ) {
                return;
            }
            if ( type == TimeTableType.telop ) {
                Telop edit = (Telop)AppManager.SaveData[entry].Clone();
                float split_position = SecFromXCoord( m_mousePosition.X );
                edit.End = split_position;
                Telop adding = (Telop)AppManager.SaveData[entry].Clone();
                adding.Start = split_position;
                Command run = Command.GCommandEditTelop( entry, edit );
                run.child = Command.GCommandAddTelop( adding );
                AppManager.Register( AppManager.SaveData.Execute( run ) );
                AppManager.Edited = true;
            } else {
                TimeTable table = (TimeTable)AppManager.SaveData[type, group][track].Clone();
                float begin = SecFromXCoord( m_mousePosition.X );
                float buf = table[entry].end;
                table[entry].end = begin;
                table.Add( new TimeTableEntry( begin, buf, table[entry].body ) );
                table.Sort();
                Command run = Command.GCommandEditTimeTable( m_clicked.type, group, track, table );
                AppManager.Register( AppManager.SaveData.Execute( run ) );
                AppManager.Edited = true;
                table.Dispose();
            }
        }

        /// <summary>
        /// テキストからのインポート
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_importFromText( object sender, EventArgs e ) {
            TimeTableType type = m_clicked.type;
            int group = m_clicked.group;
            int track = m_clicked.track;
            using ( OpenFileDialog dlg = new OpenFileDialog() ) {
                try {
                    dlg.Filter = _( "Text file(*.txt)|*.txt" ) + "|" + _( "All Files(*.*)|*.*" );
                } catch {
                    dlg.Filter = "Text file(*.txt)|*.txt|All Files(*.*)|*.*";
                }
                if ( dlg.ShowDialog() == DialogResult.OK ) {
                    string file = dlg.FileName;
                    // 編集対象をコピー
                    if ( type != TimeTableType.vsq && type != TimeTableType.another && type != TimeTableType.character && type != TimeTableType.plugin ) {
                        return;
                    }
                    TimeTableGroup table = (TimeTableGroup)AppManager.SaveData[type, group].Clone();
                    //int target_group = -1;
                    using ( StreamReader sr = new StreamReader( file ) ) {
                        float begin, end;
                        string line;
                        while ( sr.Peek() > -1 ) {
                            line = sr.ReadLine();
                            string[] spl = line.Split( new char[] { ' ', '\t', ',' }, StringSplitOptions.RemoveEmptyEntries );
                            if ( spl.Length >= 2 ) {
                                if ( m_clicked.type == TimeTableType.vsq ) {
                                    try {
                                        begin = float.Parse( spl[0] );
                                        end = float.Parse( spl[1] );
                                    } catch {
                                        continue;
                                    }
                                    string body = "";
                                    if ( spl.Length >= 3 ) {
                                        body = spl[2];
                                    }
                                    table.Interrup( track, begin, end, body );
                                } else {
                                    try {
                                        begin = float.Parse( spl[0] );
                                        end = float.Parse( spl[1] );
                                        table.Interrup( track, begin, end );
                                    } catch {
                                    }
                                }
                            }
                        }
                    }
                    Command run = Command.GCommandEditGroup( m_clicked.type, group, table );
                    AppManager.Register( AppManager.SaveData.Execute( run ) );
                    AppManager.Edited = true;
                    table.Dispose();
                    table = null;
                }
            }
        }

        /// <summary>
        /// テキストへの出力
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_exportToText( object sender, EventArgs e ) {
            TimeTableType type = m_clicked.type;
            int group = m_clicked.group;
            int track = m_clicked.track;
            using ( SaveFileDialog dlg = new SaveFileDialog() ) {
                try {
                    dlg.Filter = _( "Text file(*.txt)|*.txt" ) + "|" + _( "All Files(*.*)|*.*" );
                } catch {
                    dlg.Filter = "Text file(*.txt)|*.txt|All Files(*.*)|*.*";
                }
                dlg.AddExtension = true;
                if ( dlg.ShowDialog() == DialogResult.OK ) {
                    string file = dlg.FileName;
                    TimeTable table = null;
                    using ( StreamWriter sw = new StreamWriter( file ) ) {
                        table = AppManager.SaveData[type, group][track];
                        if ( table == null ) {
                            return;
                        }
                        for ( int entry = 0; entry < table.Count; entry++ ) {
                            if ( m_clicked.type == TimeTableType.vsq ) {
                                sw.WriteLine( table[entry].begin + "\t" + table[entry].end + "\t" + table[entry].body );
                            } else {
                                sw.WriteLine( table[entry].begin + "\t" + table[entry].end );
                            }
                        }
                    }
                }
            }

        }

        /// <summary>
        /// エントリの貼付け
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_pasteEntry( object sender, EventArgs e ) {
            int group = m_clicked.group;
            int track = m_clicked.track;
            int entry = m_clicked.entry;
            float begin = SecFromXCoord( m_mousePosition.X );
            switch ( m_clicked.type ) {
                case TimeTableType.another:
                    if ( m_copied.type == TimeTableType.telop ) {
                        TimeTableEntry ent = new TimeTableEntry();
                        ent.end = begin + m_copied_telop.End - m_copied_telop.Start;
                        ent.begin = begin;
                        ent.body = AppManager.SaveData.m_group_another[track].Text;
                        TimeTableGroup ttg = (TimeTableGroup)AppManager.SaveData.m_group_another.Clone();
                        ttg.Interrup( track, ent.begin, ent.end, ent.body );
                        Command run = Command.GCommandEditTimeTable( TimeTableType.another, -1, track, ttg[track] );
                        AppManager.Register( AppManager.SaveData.Execute( run ) );
                        AppManager.Edited = true;
                    } else {
                        using ( TimeTableEntry ent = (TimeTableEntry)m_copied_entry.Clone() )
                        using ( TimeTableGroup tablegroup = (TimeTableGroup)AppManager.SaveData.m_group_another.Clone() ) {
                            ent.end = begin + (ent.end - ent.begin);
                            ent.begin = begin;
                            ent.body = AppManager.SaveData.m_group_another[track].Text;
                            tablegroup.Interrup( track, ent.begin, ent.end, ent.body );
                            Command run = Command.GCommandEditTimeTable( TimeTableType.another, -1, track, tablegroup[track] );
                            AppManager.Register( AppManager.SaveData.Execute( run ) );
                            AppManager.Edited = true;
                        }
                    }
                    break;
                case TimeTableType.character:
                    if ( m_copied.type == TimeTableType.telop ) {
                        TimeTableEntry tte = new TimeTableEntry();
                        tte.end = begin + m_copied_telop.End - m_copied_telop.Start;
                        tte.begin = begin;
                        TimeTableGroup ttg = (TimeTableGroup)AppManager.SaveData.m_groups_character[group].Clone();
                        ttg.Interrup( track, tte.begin, tte.end );
                        Command run = Command.GCommandEditGroup( TimeTableType.character, group, ttg );
                        AppManager.Register( AppManager.SaveData.Execute( run ) );
                        AppManager.Edited = true;
                    } else {
                        using ( TimeTableEntry ent = (TimeTableEntry)m_copied_entry.Clone() )
                        using ( TimeTableGroup tablegroup = (TimeTableGroup)AppManager.SaveData.m_groups_character[group].Clone() ) {
                            ent.end = begin + (ent.end - ent.begin);
                            ent.begin = begin;
                            tablegroup.Interrup( track, ent.begin, ent.end );
                            Command run = Command.GCommandEditGroup( TimeTableType.character, group, tablegroup );
                            AppManager.Register( AppManager.SaveData.Execute( run ) );
                            AppManager.Edited = true;
                        }
                    }
                    break;
                case TimeTableType.plugin:
                    if ( m_copied.type == TimeTableType.telop ) {
                        TimeTableEntry tte = new TimeTableEntry();
                        tte.end = begin + m_copied_telop.End - m_copied_telop.Start;
                        tte.begin = begin;
                        TimeTableGroup ttg = (TimeTableGroup)AppManager.SaveData.m_group_plugin.Clone();
                        ttg.Interrup( track, tte.begin, tte.end );
                        Command run = Command.GCommandEditTimeTable( TimeTableType.plugin, -1, track, ttg[track] );
                        AppManager.Register( AppManager.SaveData.Execute( run ) );
                        AppManager.Edited = true;
                    } else {
                        using ( TimeTableEntry ent = (TimeTableEntry)m_copied_entry.Clone() )
                        using ( TimeTableGroup tablegroup = (TimeTableGroup)AppManager.SaveData.m_group_plugin.Clone() ) {
                            ent.end = begin + (ent.end - ent.begin);
                            ent.begin = begin;
                            if ( m_copied.track == m_clicked.track && m_copied.type == TimeTableType.plugin ) {
                                ent.body = m_copied_entry.body;
                            } else {
                                ent.body = "";
                            }
                            tablegroup.Interrup( track, ent.begin, ent.end, ent.body );
                            Command run = Command.GCommandEditTimeTable( TimeTableType.plugin, -1, track, tablegroup[track] );
                            AppManager.Register( AppManager.SaveData.Execute( run ) );
                            AppManager.Edited = true;
                        }
                    }
                    break;
                case TimeTableType.telop:
                    if ( m_copied.type == TimeTableType.telop ) {
                        Telop edited = (Telop)m_copied_telop.Clone();
                        edited.End = begin + m_copied_telop.End - m_copied_telop.Start;
                        edited.Start = begin;
                        Command run = Command.GCommandAddTelop( edited );
                        AppManager.Register( AppManager.SaveData.Execute( run ) );
                        AppManager.Edited = true;
                    }
                    break;
            }
            UpdateObjectList();
        }

        /// <summary>
        /// エントリの切取
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_cutEntry( object sender, EventArgs e ) {
            TimeTableType type = m_clicked.type;
            int group = m_clicked.group;
            int track = m_clicked.track;
            int entry = m_clicked.entry;
            if ( type == TimeTableType.telop ) {
                m_copied_telop = (Telop)AppManager.SaveData[entry].Clone();
                m_copied = m_clicked;
                Command run = Command.GCommandDeleteTelop( m_copied_telop );
                AppManager.Register( AppManager.SaveData.Execute( run ) );
            } else {
                m_copied_entry = (TimeTableEntry)AppManager.SaveData[type, group][track][entry].Clone();
                m_copied = m_clicked;
                Command run = Command.GCommandDeleteTimeTableEntry( type, group, track, AppManager.SaveData[type, group][track][entry] );
                AppManager.Register( AppManager.SaveData.Execute( run ) );
            }
            UpdateObjectList();
            AppManager.Edited = true;
        }

        /// <summary>
        /// エントリのコピー
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_copyEntry( object sender, EventArgs e ) {
            TimeTableType type = m_clicked.type;
            int group = m_clicked.group;
            int track = m_clicked.track;
            int entry = m_clicked.entry;
            if ( type == TimeTableType.telop ) {
                m_copied_telop = (Telop)AppManager.SaveData[entry].Clone();
            } else {
                m_copied_entry = (TimeTableEntry)AppManager.SaveData[type, group][track][entry].Clone();
            }
            m_copied = m_clicked;
        }

        /// <summary>
        /// タイムラインのグループごとの折りたたみを行う
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_foldAll( object sender, EventArgs e ) {
            AppManager.SaveData.m_group_vsq.Folded = true;
            for ( int group = 0; group < AppManager.SaveData.m_groups_character.Count; group++ ) {
                AppManager.SaveData.m_groups_character[group].Folded = true;
            }
            AppManager.SaveData.m_group_another.Folded = true;
            AppManager.SaveData.m_group_plugin.Folded = true;
            AppManager.SaveData.TelopListFolded = true;
            this.Invalidate();
        }
        
        /// <summary>
        /// 折りたたまれたタイムライン・グループを展開する
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_expandAll( object sender, EventArgs e ) {
            AppManager.SaveData.m_group_vsq.Folded = false;
            for ( int group = 0; group < AppManager.SaveData.m_groups_character.Count; group++ ) {
                AppManager.SaveData.m_groups_character[group].Folded = false;
            }
            AppManager.SaveData.m_group_another.Folded = false;
            AppManager.SaveData.m_group_plugin.Folded = false;
            AppManager.SaveData.TelopListFolded = false;
            SetVScrollRange();
            this.Invalidate();
        }

        /// <summary>
        /// 選択されたエントリを可能な限り拡張する
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_expandEntry( object sender, EventArgs e ) {
            PointF px;
            float time = SecFromXCoord( m_mousePosition.X );
            if ( m_clicked.type == TimeTableType.character ) {
                px = GetEntryLimit( m_clicked, time, true );
            } else {
                px = GetEntryLimitSingle( m_clicked, time, true );
            }
            Command run, inv;
            TimeTableType type = m_clicked.type;
            int group = m_clicked.group;
            int track = m_clicked.track;
            int entry = m_clicked.entry;
            TimeTableEntry item = new TimeTableEntry( px.X, px.Y, "" );
            if ( type != TimeTableType.another && type != TimeTableType.character && type != TimeTableType.plugin ) {
                return;
            }
            if ( type == TimeTableType.plugin ) {
                item.body = "";
            } else {
                item.body = AppManager.SaveData[type, group][track].Text;
            }
            run = Command.GCommandEditTimeTableEntry( type, group, track, entry, item );
            AppManager.Register( AppManager.SaveData.Execute( run ) );
            AppManager.Edited = true;
        }
        
        /// <summary>
        /// タイムテーブルの貼付け
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_pasteTimeTable( object sender, EventArgs e ) {
            if ( m_copied_timetable == null ) {
                return;
            }
            TimeTableType type = m_clicked.type;
            int group = m_clicked.group;
            int track = m_clicked.track;

            bool overwrite = false;
            TimeTableGroup tmp;
            TimeTableType command_target;
            int target_group;
            if ( AppManager.SaveData[type, group][track].Count > 0 ) {
                overwrite = true;
            }
            command_target = type;
            if ( type == TimeTableType.character ) {
                target_group = group;
            } else {
                target_group = -1;
            }
            tmp = (TimeTableGroup)AppManager.SaveData[type, group].Clone();

            if ( overwrite ) {
                using ( PasteModeDialog dlg = new PasteModeDialog() ) {
                    dlg.ShowDialog();
                    PasteModeDialogResult result = dlg.DialogResult;
                    switch ( result ) {
                        case PasteModeDialogResult.Cancel:
                            tmp.Dispose();
                            tmp = null;
                            return;
                        case PasteModeDialogResult.Overwrite:
                            tmp[track].Clear();
                            break;
                    }
                }
            }

            for ( int entry = 0; entry < m_copied_timetable.Count; entry++ ) {
                if ( command_target == TimeTableType.vsq && m_copied_timetable.Type == TimeTableType.vsq ) {
                    tmp.Interrup(
                        track,
                        m_copied_timetable[entry].begin,
                        m_copied_timetable[entry].end,
                        m_copied_timetable[entry].body );
                } else {
                    tmp.Interrup( track, m_copied_timetable[entry].begin, m_copied_timetable[entry].end );
                }
            }
            Command run = Command.GCommandEditGroup( command_target, target_group, tmp );
            AppManager.Register( AppManager.SaveData.Execute( run ) );
            AppManager.Edited = true;

            if ( tmp != null ) {
                tmp.Dispose();
                tmp = null;
            }
            this.Invalidate();
        }

        /// <summary>
        /// タイムテーブルのコピー
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_copyTimeTable( object sender, EventArgs e ){
            int group = m_clicked.group;
            int track = m_clicked.track;
            if ( m_copied_timetable != null ) {
                m_copied_timetable.Dispose();
                m_copied_timetable = null;
            }
            m_copied_timetable = (TimeTable)AppManager.SaveData[m_clicked.type, group][track].Clone();
        }
        
        /// <summary>
        /// 表示倍率の設定
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_setScale( object sender, EventArgs e ) {
            TimeTableType type = m_clicked.type;
            int group = m_clicked.group;
            int track = m_clicked.track;
            if ( type != TimeTableType.character && type != TimeTableType.another ) {
                return;
            }
            using ( InputBox ib = new InputBox( _( "Scale setting" ), _( "Please enter scale. (If entered value is minus, character or image will be flipped horizontally.)" ) ) ) {
                float sscale;
                switch ( m_clicked.type ) {
                    case TimeTableType.character:
                        sscale = AppManager.SaveData.m_groups_character[group].Scale;
                        break;
                    case TimeTableType.another:
                        sscale = AppManager.SaveData.m_group_another[track].Scale;
                        break;
                    default:
                        return;
                }
                ib.rText = sscale.ToString();
                if ( ib.ShowDialog() == DialogResult.OK ) {
                    //float scale = 1.0f;
                    try {
                        sscale = float.Parse( ib.rText );
                    } catch {
                        MessageBox.Show( _( "Invalid value has been entered" ), _( "Error" ), MessageBoxButtons.OK, MessageBoxIcon.Exclamation );
                        return;
                    }
                    Command run = Command.GCommandChangeScale( type, group, track, sscale );
                    AppManager.Register( AppManager.SaveData.Execute( run ) );
                    AppManager.Edited = true;
                    UpdateEditHandle();
                }
            }
        }
        
        /// <summary>
        /// クリックされたトラック上のエントリをクリアします
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_clearEntry( object sender, EventArgs e ) {
            TimeTableType type = m_clicked.type;
            int group = m_clicked.group;
            int track = m_clicked.track;
            if ( MessageBox.Show( _( "...clearing entries of selected time-table.\nWould you like to continue?" ), _( "Confirmation" ), MessageBoxButtons.OKCancel, MessageBoxIcon.Question ) == DialogResult.OK ) {
                if ( type == TimeTableType.telop ) {
                    Telop[] edit = AppManager.SaveData.m_telop_ex2.ToArray();
                    Command run = Command.GCommandDeleteTelopRange( edit );
                    AppManager.Register( AppManager.SaveData.Execute( run ) );
                    AppManager.Edited = true;
                } else {
                    using ( TimeTable table = (TimeTable)AppManager.SaveData[type, group][track].Clone() ) {
                        table.Clear();
                        Command run = Command.GCommandEditTimeTable( type, group, track, table );
                        AppManager.Register( AppManager.SaveData.Execute( run ) );
                        AppManager.Edited = true;
                    }
                }
            }
        }

        private void h_editEntry( object sender, EventArgs e ) {
            TimeTableType type = m_clicked.type;
            int group = m_clicked.group;
            int track = m_clicked.track;
            int entry = m_clicked.entry;
            if ( type != TimeTableType.character && 
                 type != TimeTableType.another && 
                 type != TimeTableType.plugin &&
                 type != TimeTableType.telop ) {
                return;
            }
            PointF bound;
            float begin, end;
            if ( type == TimeTableType.telop ) {
                begin = AppManager.SaveData[entry].Start;
                end = AppManager.SaveData[entry].End;
            } else {
                begin = AppManager.SaveData[type, group][track][entry].begin;
                end = AppManager.SaveData[type, group][track][entry].end;
            }
            if ( type == TimeTableType.character ) {
                bound = GetEntryLimit( m_clicked, (begin + end) / 2f, true );
            } else if ( type == TimeTableType.telop ) {
                bound = new PointF( 0f, AppManager.SaveData.m_totalSec );
            } else {
                bound = GetEntryLimitSingle( m_clicked, (begin + end) / 2f, true );
            }
            using ( EditEntry eden = new EditEntry( begin, end, bound.X, bound.Y ) ) {
                if ( eden.ShowDialog() == DialogResult.OK ) {
                    if ( type == TimeTableType.telop ) {
                        Telop edited = (Telop)AppManager.SaveData[entry].Clone();
                        edited.Start = eden.Start;
                        edited.End = eden.End;
                        Command run = Command.GCommandEditTelop( entry, edited );
                        AppManager.Register( AppManager.SaveData.Execute( run ) );
                        AppManager.Edited = true;
                    } else {
                        using ( TimeTableEntry item = new TimeTableEntry( eden.Start, eden.End, AppManager.SaveData[type, group][track][entry].body ) ) {
                            Command run = Command.GCommandEditTimeTableEntry( type, group, track, entry, item );
                            AppManager.Register( AppManager.SaveData.Execute( run ) );
                            AppManager.Edited = true;
                        }
                    }
                }
            }
        }

        private void h_pluginSetting( object sender, EventArgs e ) {
            ToolStripDropDownItem item = (ToolStripDropDownItem)sender;
            string plugin_name = item.Text;
            for ( int i = 0; i < AppManager.SaveData.m_plugins_config.Count; i++ ) {
                if ( plugin_name == AppManager.SaveData.m_plugins_config[i].ID ) {
                    string old_config = AppManager.SaveData.m_plugins[i].Instance.Config;
                    if ( AppManager.SaveData.m_plugins[i].Instance.BaseSetting() == DialogResult.OK ) {
                        string new_config = AppManager.SaveData.m_plugins[i].Instance.Config;
                        AppManager.SaveData.m_plugins[i].Instance.Config = old_config; //ちょっとセコイなｗ
                        if ( old_config != new_config ) {
                            Command run = Command.GCommandChangePluginConfig( i, new_config );
                            AppManager.Register( AppManager.SaveData.Execute( run ) );
                            AppManager.SaveData.m_plugins[i].Instance.Config = new_config;
                            AppManager.Edited = true;
                        }
                    }
                    break;
                }
            }
        }

        private void h_entrySetting( object sender, EventArgs e ) {
            int track = m_clicked.track;
            int entry = m_clicked.entry;
            string current = AppManager.SaveData.m_group_plugin[track][entry].body;
            if ( AppManager.SaveData.m_plugins[track].Instance.EntrySetting( ref current ) == DialogResult.OK ) {
                using ( TimeTableEntry tmp = new TimeTableEntry( AppManager.SaveData.m_group_plugin[track][entry].begin, AppManager.SaveData.m_group_plugin[track][entry].end, current ) ) {
                    Command run = Command.GCommandEditTimeTableEntry( TimeTableType.plugin, -1, track, entry, tmp );
                    AppManager.Register( AppManager.SaveData.Execute( run ) );
                    AppManager.Edited = true;
                }
            }
        }
                
        /// <summary>
        /// 目パチを自動で追加します。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_addWink( object sender, EventArgs e ) {
            List<string> titles = new List<string>();
            foreach ( ImageEntry img in AppManager.SaveData.m_groups_character[m_clicked.group].Character ) {
                titles.Add( img.title );
            }
            using ( Winker winkconfig = new Winker( titles.ToArray(), AppManager.SaveData.m_totalSec ) ) {
                if ( winkconfig.ShowDialog() != DialogResult.OK ) {
                    return;
                }
                bool randomize = winkconfig.Randomize;

                float interval = winkconfig.WinkInterval;
                float begin;
                if ( winkconfig.BeginForced ) {
                    begin = winkconfig.ForcedBegin;
                } else {
                    begin = 0.0f;
                }
                float end;
                if ( winkconfig.EndForced ) {
                    end = winkconfig.ForcedEnd;
                } else {
                    end = AppManager.SaveData.m_totalSec;
                }
                string closed = winkconfig.ClosedEye;
                string in_between = winkconfig.InBetween;
                float close_frames = winkconfig.CloseFrames;

                Random rnd = new Random();
                float total;
                if ( begin == 0.0f ) {
                    total = nextInterval( ref rnd, interval, randomize );
                } else {
                    total = begin;
                }

                //titleがclosedであるImagesのインデクスを検索
                int track = -1;
                for ( int i = 0; i < AppManager.SaveData.m_groups_character[m_clicked.group].Count; i++ ) {
                    if ( AppManager.SaveData.m_groups_character[m_clicked.group][i].Text == closed ) {
                        track = i;
                        break;
                    }
                }
                if ( track < 0 ) {
                    winkconfig.Dispose();
                    return;
                }

                //titleがin_betweenであるImagesのインデクスを検索
                int bet_track = -1;
                for ( int i = 0; i < AppManager.SaveData.m_groups_character[m_clicked.group].Count; i++ ) {
                    if ( AppManager.SaveData.m_groups_character[m_clicked.group][i].Text == in_between ) {
                        bet_track = i;
                        break;
                    }
                }

                using ( TimeTableGroup temp = (TimeTableGroup)AppManager.SaveData.m_groups_character[m_clicked.group].Clone() ) {
                    float unit_frame;
                    if ( bet_track < 0 ) {
                        unit_frame = close_frames / AppManager.SaveData.FrameRate;
                    } else {
                        unit_frame = close_frames / 2f / AppManager.SaveData.FrameRate;
                    }
                    while ( total <= end ) {
                        temp.Interrup( track, total, total + unit_frame );
                        if ( bet_track >= 0 ) {
                            temp.Interrup( bet_track, total + unit_frame, total + 2f * unit_frame );
                        }
                        total += (close_frames / AppManager.SaveData.FrameRate + nextInterval( ref rnd, interval, randomize ));
                    }
                    temp[track].Sort();
                    if ( bet_track >= 0 ) {
                        temp[bet_track].Sort();
                    }
                    Command run = Command.GCommandEditGroup( TimeTableType.character, m_clicked.group, temp );
                    AppManager.Register( AppManager.SaveData.Execute( run ) );
                    AppManager.Edited = true;
                }
                this.Invalidate();
            }
        }
        
        /// <summary>
        /// 新しいトラックを追加します
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_addTrack( object sender, EventArgs e ) {
            switch ( m_clicked.type ) {
                case TimeTableType.another:
                    using ( TimeTable temp = new TimeTable( "test track", 0, TimeTableType.another, null ) ) {
                        Command run = Command.GCommandAddTimeTable( TimeTableType.another, -1, AppManager.SaveData.m_group_another.Count, temp );
                        AppManager.Register( AppManager.SaveData.Execute( run ) );
                    }
                    AppManager.SaveData.UpdateZorder();
                    AppManager.Edited = true;
                    UpdateObjectList();
                    SetVScrollRange();
                    break;
            }
        }

        /// <summary>
        /// クリックされた位置のトラックが担当する画像を変更します
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_setImage( object sender, EventArgs e ) {
            int group = m_clicked.group;
            int track = m_clicked.track;
            if ( group < 0 ) {
                return;
            }

            switch ( m_clicked.type ) {
                case TimeTableType.another:
                    if ( dialogImage.ShowDialog() == DialogResult.OK ) {
                        if ( track >= 0 ) {
                            string file = dialogImage.FileName;
                            if ( Path.GetExtension( file ).ToLower() == ".avi" ) {
                                Command run = Command.GCommandSetAvi( track, file );
                                AppManager.Register( AppManager.SaveData.Execute( run ) );
#if DEBUG
                                Common.DebugWriteLine( "Form1.h_setImage; mode avi" );
#endif
                            } else {
                                Command run = Command.GCommandSetImage( track, new Bitmap( Common.ImageFromFile( dialogImage.FileName ) ) );
                                AppManager.Register( AppManager.SaveData.Execute( run ) );
#if DEBUG
                                Common.DebugWriteLine( "Form1.h_setImage; mode image" );
#endif
                            }
                            AppManager.Edited = true;
                        }
                    }
                    UpdateEditHandle();
                    break;
            }
        }

        private void h_previewImage( object sender, EventArgs e ) {
            int group = m_clicked.group;
            int track = m_clicked.track;
            if ( group < 0 ) {
                return;
            }
            switch ( m_clicked.type ) {
                case TimeTableType.character:
                    if ( track < 0 ) {
                        ShowPreviewPictureBox( AppManager.SaveData.m_groups_character[group].Character.DefaultFace, m_mousePosition );
                    } else {
                        string title = AppManager.SaveData[m_clicked.type, group][track].Text;
                        Character3 ch = AppManager.SaveData.m_groups_character[group].Character;
                        if ( ch == null ) {
                            return;
                        }
                        List<int> list = new List<int>();
                        list.Add( track );
                        string tag = ch[title].tag;
                        foreach ( ImageEntry ie in ch ) {
                            if ( tag != "" ) {
                                if ( ie.IsDefault ) {
                                    list.Add( ie.Z );
                                }
                            } else {
                                if ( ie.IsDefault && (tag != "" && ie.tag == tag) ) {
                                    list.Add( ie.Z );
                                }
                            }
                        }
                        /*for ( int i = 0; i < ch.Count; i++ ) {
                            if ( tag != "" ) {
                                if ( ch[i].IsDefault ) {
                                    list.Add( i );
                                }
                            } else {
                                if ( ch[i].IsDefault && (tag != "" && ch[i].tag == tag) ) {
                                    list.Add( i );
                                }
                            }
                        }*/
                        Image img = AppManager.SaveData.m_groups_character[group].Character.Face( list.ToArray() );
                        ShowPreviewPictureBox( img, m_mousePosition );
                    }
                    break;
                case TimeTableType.another:
                    if ( track >= 0 ) {
                        ShowPreviewPictureBox( AppManager.SaveData.m_group_another[track].Image, m_mousePosition );
                        //showPreviewPictureBox( s.m_group_another[track].GetImage( 0f ), m_mousePosition );
                    }
                    break;
                default:
                    break;
            }
        }
        
        /// <summary>
        /// time_tableに格納されている歌詞の情報から、口パクトラックを作成し、追加します
        /// アンドゥ・リドゥ用の処理はgenMouthFromVsqでやってる。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_genMouthFromVsq( object sender, EventArgs e ) {
            if ( m_clicked.type != TimeTableType.vsq ) {
                return;
            }
            List<string> plugins = new List<string>();
            for ( int i = 0; i < AppManager.SaveData.m_plugins.Length; i++ ) {
                if ( (AppManager.SaveData.m_plugins[i].Instance.Type & Plugin.Constants.LS_TYPE_CHARACTER) == Plugin.Constants.LS_TYPE_CHARACTER ) {
                    plugins.Add( AppManager.SaveData.m_plugins_config[i].ID );
                }
            }
            using ( SelectCharacater sc = new SelectCharacater( plugins ) ) {
                if ( sc.ShowDialog() == DialogResult.OK ) {
                    //if ( sc.SaveCharacterConfigOutside ) {
                    //    genMouthFromVsq( s.m_group_vsq[m_clicked.track], sc.Character, true, sc.Path );
                    //} else {
                        GenerateLipsyncFromVsq( AppManager.SaveData.m_group_vsq[m_clicked.track], sc.Character, true );
                    //}
                        AppManager.Edited = true;
                    SetVScrollRange();
                    this.Invalidate();
                }
            }
        }
        
        /// <summary>
        /// vsqファイルから、トラックを読み込みます
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_readVsq( object sender, EventArgs e ) {
            openVsqDialog.FileName = AppManager.Config.LastVsqPath;
            try {
                openVsqDialog.Filter = _( "VOCALOID2 Sequence File(*.vsq)|*.vsq" ) + "|" +
                                       _( "UTAU Script File(*.ust)|*.ust" ) + "|" +
                                       _( "All Files(*.*)|*.*" );
            } catch {
                openVsqDialog.Filter = "VOCALOID2 Sequence File(*.vsq)|*.vsq|UTAU Script File(*.ust)|*.ust|All Files(*.*)|*.*";
            }
            if ( openVsqDialog.ShowDialog() == DialogResult.OK ) {
                AppManager.Config.LastVsqPath = openVsqDialog.FileName;
                string file_name = openVsqDialog.FileName;
                ReadVsq( file_name );
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_setVideoSize( object sender, EventArgs e ) {
            sizeChange();
        }

        private void h_setImagePosition( object sender, EventArgs e ) {
            TimeTableType type = m_clicked.type;
            Point point;
            if ( type == TimeTableType.another ) {
                point = AppManager.SaveData.m_group_another[m_clicked.track].Position;
            } else if ( type == TimeTableType.character ) {
                point = AppManager.SaveData.m_groups_character[m_clicked.group].Position;
            } else {
                return;
            }

            using ( SetSize<int> setsize = new SetSize<int>( _( "Image placement" ), "X", "Y", point.X, point.Y ) ) {
                if ( setsize.ShowDialog() == DialogResult.OK ) {
                    Point point_new = new Point( setsize.ResultWidth, setsize.ResultHeight );
                    Command run = Command.GCommandSetPosition( type, m_clicked.group, m_clicked.track, point_new );
                    AppManager.Register( AppManager.SaveData.Execute( run ) );
                    AppManager.Edited = true;
                }
            }
        }

        private void h_editCharacter( object sender, EventArgs e ) {
            if ( AppManager.SaveData.m_groups_character[m_clicked.group].Character.Type == CharacterType.def ) {
                int prev = AppManager.SaveData.m_groups_character[m_clicked.group].Character.Count;
                int group = m_clicked.group;
                using ( GenerateCharacter gc = new GenerateCharacter( AppManager.SaveData.m_groups_character[group] ) ) {
                    gc.LastPath = AppManager.Config.LastCharacterPath;
                    if ( gc.ShowDialog() == DialogResult.OK ) {
                        AppManager.Config.LastCharacterPath = gc.LastPath;
                        /*List<TimeTable> old = new List<TimeTable>();
                        for ( int i = 0; i < AppManager.SaveData.m_groups_character[group].Count; i++ ) {
                            old.Add( (TimeTable)AppManager.SaveData.m_groups_character[group][i].Clone() );
                        }
                        TimeTableGroup ttg = (TimeTableGroup)AppManager.SaveData.m_groups_character[group].Clone();
                        ttg.list.Clear();
                        foreach ( ImageEntry ie in gc.EditedResult.Character ) {
                            for ( int j = 0; j < old.Count; j++ ) {
                                if ( ie.title == old[j].Text ) {
                                    ttg.list.Add( old[j] );
                                    break;
                                }
                            }
                        }*/
                        /*for ( int i = 0; i < gc.EditedResult.Count; i++ ) {
                            for ( int j = 0; j < old.Count; j++ ) {
                                if ( gc.EditedResult[i].title == old[j].Text ) {
                                    ttg.list.Add( old[j] );
                                    break;
                                }
                            }
                        }*/
                        /*using ( TimeTableGroup temp = (TimeTableGroup)AppManager.SaveData.m_groups_character[group].Clone() ) {
                            temp.Character = gc.EditedResult.Character;
                            //temp.Character = (Character3)gc.Character.Clone();
                            temp.Clear();
                            bool found = false;
                            // Images
                            foreach ( ImageEntry item in temp.Character ) {
                                found = false;
                                for ( int track = 0; track < AppManager.SaveData.m_groups_character[group].Count; track++ ) {
                                    if ( AppManager.SaveData.m_groups_character[group][track].Text == item.title ) {
                                        temp.Add( (TimeTable)AppManager.SaveData.m_groups_character[group][track].Clone() );
                                        found = true;
                                        break;
                                    }
                                }
                                if ( !found ) {
                                    temp.Add( new TimeTable( item.title, 0, TimeTableType.character, null ) );
                                }
                            }

                            temp.Text = gc.Character.Name;
                            Command run = Command.GCommandEditGroup( TimeTableType.character, group, temp );
                            AppManager.Register( AppManager.SaveData.Execute( run ) );
                        }*/
                        TimeTableGroup ttg = gc.EditedResult;
                        ttg.Text = ttg.Character.Name;
                        Command run = Command.GCommandEditGroup( TimeTableType.character, group, ttg );
                        AppManager.Register( AppManager.SaveData.Execute( run ) );
                        SetVScrollRange();
                        this.Invalidate();
                        AppManager.Edited = true;
                    }
                }
            } else {
                string id = AppManager.SaveData.m_groups_character[m_clicked.group].Character.PluginConfig.ID;
                int index = -1;
                for ( int i = 0; i < AppManager.SaveData.m_plugins_config.Count; i++ ) {
                    if ( id == AppManager.SaveData.m_plugins_config[i].ID ) {
                        index = i;
                        break;
                    }
                }
                if ( index >= 0 ) {
                    if ( AppManager.SaveData.m_plugins[index].Instance.BaseSetting() == DialogResult.OK ) {
                        Command run = Command.GCommandChangePluginConfig( index, AppManager.SaveData.m_plugins[index].Instance.Config );
                        AppManager.Register( AppManager.SaveData.Execute( run ) );
                        AppManager.Edited = true;
                    }
                }
            }
        }

        /// <summary>
        /// m_clickedで指定されたトラック、またはトラックグループを削除します。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void h_deleteTrack( object sender, EventArgs e ) {
#if DEBUG
            Common.DebugWriteLine( "Form1+h_deleteTrack" );
            Common.DebugWriteLine( "    m_curve.comboObjects.SelectedIndex=" + m_curve.comboObjects.SelectedIndex );
#endif
            if ( m_curve.comboObjects.SelectedIndex < 0 ) {
                m_curve.SetSelectedNone();
            }
            TimeTableType type = m_clicked.type;
            int group = m_clicked.group;
            int track = m_clicked.track;
            bool deleted = false;
            StopPauseCore();
            m_player.Pause();
            if ( type == TimeTableType.character ) {
                if ( track < 0 ) {
                    if ( MessageBox.Show( _( "...deleting selected character.\nWould you like to continue?" ), _( "Confirmation" ), MessageBoxButtons.OKCancel, MessageBoxIcon.Question ) == DialogResult.OK ) {
                        Command run = Command.GCommandDeleteTimeTableGroup( TimeTableType.character, group );
                        AppManager.Register( AppManager.SaveData.Execute( run ) );
                        deleted = true;
                    }
                }
            } else if ( type == TimeTableType.another || type == TimeTableType.vsq ) {
                if ( MessageBox.Show( _( "...deleting selected track.\nWould you like to continue?" ), _( "Confirmation" ), MessageBoxButtons.OKCancel, MessageBoxIcon.Question ) == DialogResult.OK ) {
                    Command run = Command.GCommandDeleteTimeTable( type, -1, track );
                    AppManager.Register( AppManager.SaveData.Execute( run ) );
                    deleted = true;
                }
            }
#if DEBUG
            Common.DebugWriteLine( "    deleted=" + deleted );
#endif
            if ( deleted ) {
                if ( m_curve.comboObjects.SelectedItem != null ) {
                    bool clear_required = false;
                    TagForTreeNode t = (TagForTreeNode)m_curve.comboObjects.SelectedItem;
                    if ( type == TimeTableType.character && t.type == ZorderItemType.character && t.id_or_index == group ) {
                        clear_required = true;
                    } else if ( type == TimeTableType.another && t.type == ZorderItemType.character && t.id_or_index == track ) {
                        clear_required = true;
                    }
                    if ( clear_required ) {
                        m_curve.SetSelectedNone();
                    }
                }
                AppManager.SaveData.UpdateZorder();
                AppManager.Edited = true;
                UpdateObjectList();
                this.Invalidate();
            }
        }
        
        private void h_noteON( object sender, EventArgs e ) {
            TimeTableType type = m_clicked.type;
            int group = m_clicked.group;
            int track = m_clicked.track;
            int entry = m_clicked.entry;
            float begin = SecFromXCoord( m_mousePosition.X );
            PointF bound;
            string body;
            if ( type == TimeTableType.character ) {
                body = AppManager.SaveData.m_groups_character[group][track].Text;
                bound = GetEntryLimit( m_clicked, begin, false );
            } else if ( type == TimeTableType.another || type == TimeTableType.plugin ) {
                body = AppManager.SaveData[type, -1][track].Text;
                bound = GetEntryLimitSingle( m_clicked, begin, false );
            } else {
                return;
            }
            if ( bound.Y > begin ) {
                Command run = Command.GCommandAddTimeTableEntry( type, group, track, new TimeTableEntry( begin, bound.Y, body ) );
                AppManager.Register( AppManager.SaveData.Execute( run ) );
                AppManager.Edited = true;
                this.Invalidate();
            }
        }
        
        private void h_pluginInfo( object sender, EventArgs e ) {
            ToolStripMenuItem c_sender = (ToolStripMenuItem)sender;
            int index = -1;
            for ( int i = 0; i < AppManager.SaveData.m_plugins.Length; i++ ) {
                if ( AppManager.SaveData.m_plugins[i].Instance.Name == c_sender.Text ) {
                    index = i;
                    break;
                }
            }
            if ( index >= 0 ) {
                VersionBox info = new VersionBox( AppManager.SaveData.m_plugins[index].Instance.Name, AppManager.SaveData.m_plugins[index].Instance.Abstract );
                info.ShowDialog();
                info.Dispose();
            }
        }

        private void h_noteOFF( object sender, EventArgs e ) {
            TimeTableType type = m_clicked.type;
            int group = m_clicked.group;
            int track = m_clicked.track;
            int entry = m_clicked.entry;
            float end = (m_mousePosition.X + m_startToDrawX) / AppManager.Config.PixelPerSec;
            if ( type != TimeTableType.character && 
                 type != TimeTableType.another && 
                 type != TimeTableType.plugin &&
                 type != TimeTableType.telop ) {
                return;
            }
            if ( type == TimeTableType.telop ) {
                Telop item = (Telop)AppManager.SaveData[entry].Clone();
                item.End = end;
                Command run = Command.GCommandEditTelop( entry, item );
                AppManager.Register( AppManager.SaveData.Execute( run ) );
            } else {
                TimeTableEntry item;
                item = (TimeTableEntry)AppManager.SaveData[type, group][track][entry].Clone();
                item.end = end;
                Command run = Command.GCommandEditTimeTableEntry( type, group, track, entry, item );
                AppManager.Register( AppManager.SaveData.Execute( run ) );
            }
            AppManager.Edited = true;
            this.Invalidate();
        }
    }

}
