using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Mix.Tool.ModelEditor
{
    /// <summary>
    /// フィジクスマテリアルエディターフォーム
    /// </summary>
    public partial class PhysicsMaterialEditorForm : Mix.Tool.Docking.DockContent
    {
        #region Constant member

        const int IMAGE_MARGIN = 2;

        #endregion

        #region Private member

        private Project.Dynamics dynamics = null;

        #endregion

        #region Form member

        public PhysicsMaterialEditorForm(Project.Dynamics _dynamics)
        {
            if (_dynamics == null)
            {
                throw new System.ArgumentNullException("_dynamics");
            }

            InitializeComponent();

            //ダイナミクス
            this.dynamics = _dynamics;
            this.dynamics.ChangedState += new Project.Dynamics.ChangedStateEventHandler(this.Dynamics_ChangedState);

            //フィジクスマテリアル
            this.dynamics.PhysicsMaterials.DataSource.ListChanged += new ListChangedEventHandler(this.PhysicsMaterials_ListChanged);
            this.dynamics.Identities.ListChanged += new ListChangedEventHandler(this.Identities_ListChanged);

            //ディテール(アセットが選択されるまで無効にしておく)
            this.tlpDetail.Enabled = false;

            //識別子
            this.cbIdent.DataSource = this.dynamics.Identities;

            //属性
            this.clbAttr.Collection = this.dynamics.Attributes;
            this.clbAttr.Generator = null;

            //リスト
            this.lbList.ItemHeight = this.tbListItemHeight.Value;
            this.lbList.DataSource = this.dynamics.PhysicsMaterials.DataSource;
            if (this.lbList.Items.Count > 0)
            {
                this.lbList.SelectedIndex = 0;
            }
        }

        private void PhysicsMaterialEditorForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.dynamics.Identities.ListChanged -= new ListChangedEventHandler(this.Identities_ListChanged);
            this.dynamics.PhysicsMaterials.DataSource.ListChanged -= new ListChangedEventHandler(this.PhysicsMaterials_ListChanged);
            this.dynamics.ChangedState -= new Project.Dynamics.ChangedStateEventHandler(this.Dynamics_ChangedState);
        }

        #endregion

        #region PhysicsMaterial

        private void lbList_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                PhysicsMaterialListBox lb = sender as PhysicsMaterialListBox;
                int index = lb.IndexFromPoint(e.Location);

                if (index >= 0)
                {
                    lb.SelectedIndex = index;
                }
            }
        }

        private void lbList_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.UpdateDetailControls(this.Selected);
        }

        private void tbListItemHeight_ValueChanged(object sender, EventArgs e)
        {
            this.lbList.ItemHeight = this.tbListItemHeight.Value;
        }

        private void cmsList_Opening(object sender, CancelEventArgs e)
        {
            this.cmsAdd.Enabled = true;
            this.cmsRemove.Enabled = (this.lbList.SelectedIndex >= 0);
        }

        private void cmsListAdd_Click(object sender, EventArgs e)
        {
            try
            {
                this.UpdateDetailControls(this.dynamics.PhysicsMaterials.Add());
                this.lbList.SelectedIndex = this.lbList.Items.Count - 1;
            }
            catch (System.Exception excep)
            {
                Mix.Tool.MessageDialog.ShowOK(Properties.Resources.PHYSICS_MATERIAL_ADD_ERROR + excep.Message,
                    Properties.Resources.TITLE,
                    MessageDialogIcon.Error);
            }
        }

        private void cmsListRemove_Click(object sender, EventArgs e)
        {
            Mix.Tool.Dynamics.PhysicsMaterial asset = this.Selected;
            if (asset != null)
            {
                this.btImage.BackgroundImage = null;
                this.dynamics.PhysicsMaterials.Remove(asset);
            }
        }

        private Mix.Tool.Dynamics.PhysicsMaterial Selected
        {
            get
            {
                object item = this.lbList.SelectedItem;
                if (item == null)
                {
                    return null;
                }

                return item as Mix.Tool.Dynamics.PhysicsMaterial;
            }
        }

        private void PhysicsMaterials_ListChanged(object sender, ListChangedEventArgs e)
        {
            if (e.ListChangedType == ListChangedType.ItemChanged)
            {
                if (this.lbList.Items.Count > e.NewIndex)
                {
                    Rectangle itemRect = this.lbList.GetItemRectangle(e.NewIndex);

                    if (this.lbList.ClientRectangle.Contains(itemRect) == true)
                    {
                        this.lbList.Invalidate(itemRect);
                    }
                }
            }
            else if (e.ListChangedType == ListChangedType.ItemAdded)
            {
            }
            else
            {
                this.lbList.Invalidate();
            }
        }

        #endregion

        #region Detail

        private void btImage_Click(object sender, EventArgs e)
        {
            Mix.Tool.Dynamics.PhysicsMaterial asset = this.Selected;
            if (asset != null)
            {
                using (OpenFileDialog dlg = new OpenFileDialog())
                {
                    if (asset.ImageFilePath.Length > 0)
                    {
                        try
                        {
                            dlg.InitialDirectory = System.IO.Path.GetDirectoryName(asset.ImageFilePath);
                        }
                        catch
                        {
                            dlg.InitialDirectory = System.IO.Path.GetPathRoot(asset.ImageFilePath);
                        }

                        dlg.FileName = System.IO.Path.GetFileName(asset.ImageFilePath);
                    }
                    else
                    {
                        dlg.InitialDirectory = "";
                        dlg.FileName = "";
                    }

                    dlg.Filter = Properties.Resources.FILE_FILTER_PM_IMAGE;
                    dlg.FilterIndex = 1;

                    if (dlg.ShowDialog() == DialogResult.OK)
                    {
                        asset.ImageFilePath = dlg.FileName;
                        this.btImage.BackgroundImage = asset.Image;
                    }
                }
            }
        }

        private void tbName_Validating(object sender, CancelEventArgs e)
        {
            this.UpdateName();
        }

        private void tbName_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)Keys.Return)
            {
                this.UpdateName();
            }
        }

        private void cbIdent_SelectedIndexChanged(object sender, EventArgs e)
        {
            if ((this.dynamics.Identities.Count == 0) ||
                (this.cbIdent.SelectedIndex < 0))
            {
                return;
            }

            Mix.Tool.Dynamics.PhysicsMaterial asset = this.Selected;
            if (asset != null)
            {
                asset.Identifier.Name = this.dynamics.Identities[this.cbIdent.SelectedIndex].Name;
            }
        }

        private void clbAttr_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            Mix.Tool.Dynamics.PhysicsMaterial asset = this.Selected;
            if (asset != null)
            {
                string attrName = this.dynamics.Attributes[e.Index].Name;

                if (e.NewValue == CheckState.Checked)
                {
                    if (asset.Attributes.Contains(attrName) == false)
                    {
                        asset.Attributes.Add(attrName);
                    }
                }
                else if (e.NewValue == CheckState.Unchecked)
                {
                    asset.Attributes.Remove(attrName);
                }
            }
        }

        private void nudRestitution_ValueChanged(object sender, EventArgs e)
        {
            NUD2TB(this.nudRestitution, this.tbRestitution);

            Mix.Tool.Dynamics.PhysicsMaterial asset = this.Selected;
            if (asset != null)
            {
                asset.Restitution = (float)Convert.ToDouble(this.nudRestitution.Value);
            }
        }

        private void tbRestitution_ValueChanged(object sender, EventArgs e)
        {
            TB2NUD(this.tbRestitution, this.nudRestitution);
        }

        private void nudFriction_ValueChanged(object sender, EventArgs e)
        {
            NUD2TB(this.nudFriction, this.tbFriction);

            Mix.Tool.Dynamics.PhysicsMaterial asset = this.Selected;
            if (asset != null)
            {
                asset.Friction = (float)Convert.ToDouble(this.nudFriction.Value);
            }
        }

        private void tbFriction_ValueChanged(object sender, EventArgs e)
        {
            TB2NUD(this.tbFriction, this.nudFriction);
        }

        private void UpdateName()
        {
            Mix.Tool.Dynamics.PhysicsMaterial physicsMaterial = this.Selected;
            if (physicsMaterial != null)
            {
                physicsMaterial.Name = this.tbName.Text;
            }
        }

        private void UpdateDetailControls(Mix.Tool.Dynamics.PhysicsMaterial physicsMaterial)
        {
            if (physicsMaterial != null)
            {
                this.btImage.BackgroundImage = physicsMaterial.Image;
                this.tbName.Text = physicsMaterial.Name;
                this.cbIdent.SelectedIndex = this.dynamics.Identities.FindByName(physicsMaterial.Identifier.Name);
                this.clbAttr.Generator = physicsMaterial.Attributes;
                this.nudFriction.Value = Convert.ToDecimal(physicsMaterial.Friction);
                this.nudRestitution.Value = Convert.ToDecimal(physicsMaterial.Restitution);

                if (this.tlpDetail.Enabled == false)
                {
                    this.tlpDetail.Enabled = true;
                }
            }
            else
            {
                this.btImage.BackgroundImage = null;
                this.tbName.Text = "";
                this.cbIdent.SelectedIndex = -1;
                this.clbAttr.Generator = null;
                this.nudFriction.Value = this.nudFriction.Minimum;
                this.nudRestitution.Value = this.nudRestitution.Minimum;

                if (this.tlpDetail.Enabled == true)
                {
                    this.tlpDetail.Enabled = false;
                }
            }
        }

        private void Identities_ListChanged(object sender, ListChangedEventArgs e)
        {
            Mix.Tool.Dynamics.PhysicsMaterial mtrl = this.Selected;

            if (mtrl != null)
            {
                if ((e.ListChangedType == ListChangedType.ItemChanged) ||
                    (e.ListChangedType == ListChangedType.ItemDeleted))
                {
                    this.cbIdent.SelectedIndex = this.cbIdent.SelectedText.IndexOf(mtrl.Identifier.Name);
                }
            }
        }

        #endregion

        #region Dynamics

        private void Dynamics_ChangedState(object sender, Project.Dynamics.ChangedStateEventArgs e)
        {
            foreach (Control ctrl in this.Controls)
            {
                ctrl.Enabled = e.State;
            }
        }

        #endregion

        #region Misc

        private static void NUD2TB(NumericUpDown nud, TrackBar tb)
        {
            int newValue = Convert.ToInt32(nud.Value * Convert.ToDecimal(tb.Maximum));

            if (tb.Minimum > newValue)
            {
                tb.Value = tb.Minimum;
            }
            else if (tb.Maximum < newValue)
            {
                tb.Value = tb.Maximum;
            }
            else
            {
                tb.Value = newValue;
            }
        }

        private static void TB2NUD(TrackBar tb, NumericUpDown nud)
        {
            decimal newValue = Convert.ToDecimal(Convert.ToDouble(tb.Value) / Convert.ToDouble(tb.Maximum));

            if (newValue.CompareTo(nud.Minimum) < 0)
            {
                nud.Value = nud.Minimum;
            }
            else if (newValue.CompareTo(nud.Maximum) > 0)
            {
                nud.Value = nud.Maximum;
            }
            else
            {
                nud.Value = newValue;
            }
        }

        #endregion
    }
}
