/*
 * DisplacementControl.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.Drawing;
using System.Windows.Forms;
using System.IO;
using System.Text;

using Boare.Lib.AppUtil;
using CurveEditor;

namespace LipSync {

    public partial class DisplacementControl : Form, IMultiLanguageControl {
        private PointF m_scaleandoffset_x = new PointF( 1f, 0f );
        private PointF m_scaleandoffset_y = new PointF( 1f, 0f );
        private PointF m_scaleandoffset_alpha = new PointF( 400f, 0.3f );
        private PointF m_scaleandoffset_scale = new PointF( 40f, 0.5f );
        private PointF m_scaleandoffset_rotate = new PointF( 1f, 0f );
        private bool m_first_scaleandoffset = true;
        private FormCurveExport m_dialog = null;
        
        public DisplacementControl() {
            InitializeComponent();
            ApplyFont( AppManager.Config.Font.GetFont() );
            ApplyLanguage();
            Rectangle r = AppManager.Config.CurveWindowPos;
            Point pt_lt = new Point( r.Left, r.Top );
            Point pt_lb = new Point( r.Left, r.Bottom );
            Point pt_rt = new Point( r.Right, r.Top );
            Point pt_rb = new Point( r.Right, r.Bottom );
            bool visible = false;
            foreach ( Screen s in Screen.AllScreens ) {
                visible = visible | (IsInRectangle( pt_lt, s.Bounds ) | IsInRectangle( pt_lb, s.Bounds ) | IsInRectangle( pt_rt, s.Bounds ) | IsInRectangle( pt_rb, s.Bounds ));
            }
            if ( visible ) {
                this.Left = r.Left;
                this.Top = r.Top;
                this.Width = r.Width;
                this.Height = r.Height;
            } else {
                this.Width = Screen.PrimaryScreen.Bounds.Width / 2;
                this.Height = Screen.PrimaryScreen.Bounds.Height / 2;
                this.Left = this.Width / 2;
                this.Top = this.Height / 2;
            }
            if ( AppManager.Config.CurveMaximized ) {
                this.WindowState = FormWindowState.Maximized;
            } else {
                this.WindowState = FormWindowState.Normal;
            }
            this.SizeChanged += new EventHandler( DisplacementControl_LocationOrSizeChanged );
            this.LocationChanged += new EventHandler( DisplacementControl_LocationOrSizeChanged );
        }

        /// <summary>
        /// どのカーブも選択されていない状態にします
        /// </summary>
        public void SetSelectedNone() {
            curveEditor.Clear();
            curveEditor.ClearBuffer();
            comboObjects.SelectedIndex = -1;
        }

        /// <summary>
        /// rectの中にpointが入っているかどうかを判定
        /// </summary>
        /// <param name="point"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        private static bool IsInRectangle( Point point, Rectangle rect ) {
            if ( rect.X <= point.X && point.X <= rect.X + rect.Width ) {
                if ( rect.Y <= point.Y && point.Y <= rect.Y + rect.Height ) {
                    return true;
                }
            }
            return false;
        }

        public void ApplyLanguage() {
            this.Text = _( "Edit Motion Curve" );
            menuClose.Text = _( "Close" ) + "(&C)";
            menuFile.Text = _( "File" ) + "(&F)";
            menuFileImport.Text = _( "Import" ) + "(&I)";
            menuFileExport.Text = _( "Export" ) + "(&E)";
            menuRedo.Text = _( "Redo" );
            menuUndo.Text = _( "Undo" );
            menuEdit.Text = _( "Edit" ) + "(&E)";
        }

        public void ApplyFont( Font font ) {
            this.Font = font;
            foreach ( Control c in this.Controls ) {
                Boare.Lib.AppUtil.Misc.ApplyFontRecurse( c, font );
            }
        }

        public static string _( string s ) {
            return Messaging.GetMessage( s );
        }

        /// <summary>
        /// 現在の編集情報を破棄します
        /// </summary>
        public void Clear() {
            curveEditor.Clear();
            comboObjects.Items.Clear();
            comboObjects.Text = "";
        }

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

        private void menuUndo_Click( object sender, EventArgs e ) {
            curveEditor.Undo();
        }

        private void menuRedo_Click( object sender, EventArgs e ) {
            curveEditor.Redo();
        }

        private void menuEdit_DropDownOpening( object sender, EventArgs e ) {
            menuUndo.Enabled = curveEditor.IsUndoAvailable;
            menuRedo.Enabled = curveEditor.IsRedoAvailable;
        }

        private void comboObjects_SelectedIndexChanged( object sender, EventArgs e ) {
            int index = comboObjects.SelectedIndex;
#if DEBUG
            Console.WriteLine( "DisplacementControl+comboObjects_SelectedIndexChanged" );
            Console.WriteLine( "    index=" + index );
#endif
            if ( m_first_scaleandoffset ) {
                float scale, offset;
                if ( curveEditor.GetYScaleAndYOffset( "X", out scale, out offset ) ) {
                    m_scaleandoffset_x = new PointF( scale, offset );
                }
                if ( curveEditor.GetYScaleAndYOffset( "Y", out scale, out offset ) ) {
                    m_scaleandoffset_y = new PointF( scale, offset );
                }
                if ( curveEditor.GetYScaleAndYOffset( "Alpha", out scale, out offset ) ) {
                    m_scaleandoffset_alpha = new PointF( scale, offset );

#if DEBUG
                    Console.WriteLine( "Alpha, scale=" + scale + "; offset=" + offset );
#endif
                }
                if ( curveEditor.GetYScaleAndYOffset( "Scale", out scale, out offset ) ) {
                    m_scaleandoffset_scale = new PointF( scale, offset );
                }
                if ( curveEditor.GetYScaleAndYOffset( "Rotate", out scale, out offset ) ) {
                    m_scaleandoffset_rotate = new PointF( scale, offset );
                }
            }
            curveEditor.Clear();
            curveEditor.ClearBuffer();
            if ( index >= 0 ) {
                TagForTreeNode node = (TagForTreeNode)comboObjects.Items[index];
                int id = node.id_or_index;
                switch ( node.type ) {
                    case ZorderItemType.another:
                        curveEditor.Add( "X", AppManager.SaveData.m_group_another[id].mc_x );
                        curveEditor.Add( "Y", AppManager.SaveData.m_group_another[id].mc_y );
                        curveEditor.Add( "Alpha", AppManager.SaveData.m_group_another[id].mc_alpha );
                        curveEditor.Add( "Scale", AppManager.SaveData.m_group_another[id].mc_scale );
                        curveEditor.Add( "Rotate", AppManager.SaveData.m_group_another[id].mc_rotate );
                        break;
                    case ZorderItemType.character:
                        curveEditor.Add( "X", AppManager.SaveData.m_groups_character[id].mc_x );
                        curveEditor.Add( "Y", AppManager.SaveData.m_groups_character[id].mc_y );
                        curveEditor.Add( "Alpha", AppManager.SaveData.m_groups_character[id].mc_alpha );
                        curveEditor.Add( "Scale", AppManager.SaveData.m_groups_character[id].mc_scale );
                        curveEditor.Add( "Rotate", AppManager.SaveData.m_groups_character[id].mc_rotate );
                        break;
                    case ZorderItemType.plugin:
                        curveEditor.Add( "X", AppManager.SaveData.m_group_plugin[id].mc_x );
                        curveEditor.Add( "Y", AppManager.SaveData.m_group_plugin[id].mc_y );
                        curveEditor.Add( "Alpha", AppManager.SaveData.m_group_plugin[id].mc_alpha );
                        curveEditor.Add( "Scale", AppManager.SaveData.m_group_plugin[id].mc_scale );
                        curveEditor.Add( "Rotate", AppManager.SaveData.m_group_plugin[id].mc_rotate );
                        break;
                    case ZorderItemType.telop:
                        curveEditor.Add( "X", AppManager.SaveData[id].mc_x );
                        curveEditor.Add( "Y", AppManager.SaveData[id].mc_y );
                        curveEditor.Add( "Alpha", AppManager.SaveData[id].mc_alpha );
                        curveEditor.Add( "Scale", AppManager.SaveData[id].mc_scale );
                        curveEditor.Add( "Rotate", AppManager.SaveData[id].mc_rotate );
                        break;
                }
                curveEditor.SetYScaleAndYOffset( "X", m_scaleandoffset_x.X, m_scaleandoffset_x.Y );
                curveEditor.SetYScaleAndYOffset( "Y", m_scaleandoffset_y.X, m_scaleandoffset_y.Y );
                curveEditor.SetYScaleAndYOffset( "Alpha", m_scaleandoffset_alpha.X, m_scaleandoffset_alpha.Y );
                curveEditor.SetYScaleAndYOffset( "Scale", m_scaleandoffset_scale.X, m_scaleandoffset_scale.Y );
                curveEditor.SetYScaleAndYOffset( "Rotate", m_scaleandoffset_rotate.X, m_scaleandoffset_rotate.Y );
                if ( m_first_scaleandoffset ) {
                    m_first_scaleandoffset = false;
                }
                curveEditor.Invalidate();
            }
        }

        private void DisplacementControl_VisibleChanged( object sender, EventArgs e ) {
            ApplyLanguage();
        }

        private void menuVisualNumericInput_CheckedChanged( object sender, EventArgs e ) {
            this.Invalidate();
        }

        private void curveEditor_CurveEdited() {
            AppManager.Edited = true;
        }

        private void DisplacementControl_LocationOrSizeChanged( object sender, EventArgs e ) {
            if ( AppManager.Config != null ) {
                if ( this.WindowState == FormWindowState.Normal ) {
                    AppManager.Config.CurveWindowPos = this.Bounds;
                }
                AppManager.Config.CurveMaximized = (this.WindowState == FormWindowState.Maximized);
            }
        }

        private void menuClose_Click( object sender, EventArgs e ) {
            this.Close();
        }

        private void menuFileExport_Click( object sender, EventArgs e ) {
#if DEBUG
            Console.WriteLine( "DisplacementControl#menuFileExport_Click" );
#endif
            if ( m_dialog == null ) {
                m_dialog = new FormCurveExport();
            }
            m_dialog.setMode( FormCurveExport.Mode.EXPORT );
            if ( m_dialog.ShowDialog() != DialogResult.OK ) {
                return;
            }
            int index = comboObjects.SelectedIndex;
            if ( index < 0 ) {
                return;
            }
            TagForTreeNode node = (TagForTreeNode)comboObjects.Items[index];
            int id = node.id_or_index;
            BezierChain x = null;
            BezierChain y = null;
            BezierChain alpha = null;
            BezierChain scale = null;
            BezierChain rotate = null;
            switch ( node.type ) {
                case ZorderItemType.another:
                    x = AppManager.SaveData.m_group_another[id].mc_x;
                    y = AppManager.SaveData.m_group_another[id].mc_y;
                    alpha = AppManager.SaveData.m_group_another[id].mc_alpha;
                    scale = AppManager.SaveData.m_group_another[id].mc_scale;
                    rotate = AppManager.SaveData.m_group_another[id].mc_rotate;
                    break;
                case ZorderItemType.character:
                    x = AppManager.SaveData.m_groups_character[id].mc_x;
                    y = AppManager.SaveData.m_groups_character[id].mc_y;
                    alpha = AppManager.SaveData.m_groups_character[id].mc_alpha;
                    scale = AppManager.SaveData.m_groups_character[id].mc_scale;
                    rotate = AppManager.SaveData.m_groups_character[id].mc_rotate;
                    break;
                case ZorderItemType.plugin:
                    x = AppManager.SaveData.m_group_plugin[id].mc_x;
                    y = AppManager.SaveData.m_group_plugin[id].mc_y;
                    alpha = AppManager.SaveData.m_group_plugin[id].mc_alpha;
                    scale = AppManager.SaveData.m_group_plugin[id].mc_scale;
                    rotate = AppManager.SaveData.m_group_plugin[id].mc_rotate;
                    break;
                case ZorderItemType.telop:
                    x = AppManager.SaveData[id].mc_x;
                    y = AppManager.SaveData[id].mc_y;
                    alpha = AppManager.SaveData[id].mc_alpha;
                    scale = AppManager.SaveData[id].mc_scale;
                    rotate = AppManager.SaveData[id].mc_rotate;
                    break;
            }
            String name = comboObjects.Items[index].ToString();
            String dir = m_dialog.getPath();
            if ( m_dialog.isX() ) {
                exportBezierChain( x, Path.Combine( dir, name + "_X.txt" ) );
            }
            if ( m_dialog.isY() ) {
                exportBezierChain( y, Path.Combine( dir, name + "_Y.txt" ) );
            }
            if ( m_dialog.isAlpha() ) {
                exportBezierChain( alpha, Path.Combine( dir, name + "_ALPHA.txt" ) );
            }
            if ( m_dialog.isScale() ) {
                exportBezierChain( scale, Path.Combine( dir, name + "_SCALE.txt" ) );
            }
            if ( m_dialog.isRotation() ) {
                exportBezierChain( rotate, Path.Combine( dir, name + "_ROTATE.txt" ) );
            }
        }

        private void menuFileImport_Click( object sender, EventArgs e ) {
#if DEBUG
            Console.WriteLine( "menuFileImport_Click" );
#endif
            if ( m_dialog == null ) m_dialog = new FormCurveExport();
            m_dialog.setMode( FormCurveExport.Mode.IMPORT );
            if ( m_dialog.ShowDialog() != DialogResult.OK ) return;
            
            BezierChain chain = new BezierChain( Color.Black );
            if ( m_dialog.isX() ) chain.Color = Common.CURVE_X;
            if ( m_dialog.isY() ) chain.Color = Common.CURVE_Y;
            if ( m_dialog.isAlpha() ) chain.Color = Common.CURVE_ALPHA;
            if ( m_dialog.isScale() ) chain.Color = Common.CURVE_SCALE;
            if ( m_dialog.isRotation() ) chain.Color = Common.CURVE_ROTATE;
            String file = m_dialog.getPath();
            if ( !File.Exists( file ) ) return;
            if ( comboObjects.SelectedIndex < 0 ) return;
            using ( StreamReader sr = new StreamReader( file, Encoding.GetEncoding( 0 ) ) ) {
                String line = "";
                int point_id = 0;
                while ( (line = sr.ReadLine()) != null ) {
                    string[] spl = line.Split( new char[] { '\t', ' ', '　' }, StringSplitOptions.RemoveEmptyEntries );
                    if ( spl.Length < 2 ) continue;
                    ControlType tleft = ControlType.None;
                    ControlType tright = ControlType.None;
                    PointF pbase = new PointF();
                    PointF pleft = new PointF();
                    PointF pright = new PointF();
                    float v = 0.0f;
                    if ( float.TryParse( spl[0], out v ) ) pbase.X = v;
                    if ( float.TryParse( spl[1], out v ) ) pbase.Y = v;
                    if ( spl.Length >= 8 ) {
                        if ( float.TryParse( spl[2], out v ) ) pleft.X = v;
                        if ( float.TryParse( spl[3], out v ) ) pleft.Y = v;
                        String s = spl[4].Trim().ToLower();
                        if ( s.Equals( "master" ) ) tleft = ControlType.Master;
                        if ( s.Equals( "normal" ) ) tleft = ControlType.Normal;
                        if ( float.TryParse( spl[5], out v ) ) pright.X = v;
                        if ( float.TryParse( spl[6], out v ) ) pright.Y = v;
                        s = spl[7].Trim().ToLower();
                        if ( s.Equals( "master" ) ) tright = ControlType.Master;
                        if ( s.Equals( "normal" ) ) tright = ControlType.Normal;
                    }
                    point_id++;
                    BezierPoint bp = new BezierPoint( pbase, new PointF(), new PointF() );
                    bp.ID = point_id;
                    bp.m_control_left = pleft;
                    bp.m_control_right = pright;
                    bp.ControlLeftType = tleft;
                    bp.ControlRightType = tright;
#if DEBUG
                    Console.WriteLine( "    bp=" + bp.ToString() );
#endif
                    chain.List.Add( bp );
                }
            }
            String id = "X";
            if ( m_dialog.isX() ) id = "X";
            if ( m_dialog.isY() ) id = "Y";
            if ( m_dialog.isAlpha() ) id = "Alpha";
            if ( m_dialog.isScale() ) id = "Scale";
            if ( m_dialog.isRotation() ) id = "Rotate";
            CurveEditor.Command run = CurveEditor.Command.GCommandReplace( id, chain );
            curveEditor.Register( curveEditor.Execute( run ) );
        }

        private void exportBezierChain( BezierChain chain, String file ) {
            int count = chain.List.Count;
            using ( StreamWriter sw = new StreamWriter( file, false, Encoding.GetEncoding( 0 ) ) ) {
                for ( int i = 0; i < count; i++ ) {
                    BezierPoint point = chain.List[i];
                    sw.WriteLine( point.Base.X + "\t" + point.Base.Y + "\t" + point.m_control_left.X + "\t" + point.m_control_left.Y + "\t" + point.ControlLeftType + "\t" + point.m_control_right.X + "\t" + point.m_control_right.Y + "\t" + point.ControlRightType );
                }
            }
        }
    }

}
