using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml;

namespace Mix.Tool.ModelEditor.Project
{
    /// <summary>
    /// ダイナミクスクラス
    /// </summary>
    public class Dynamics
    {
        #region Public EventArgs

        /// <summary>
        /// ステートが変化した時のイベントデータクラス
        /// </summary>
        public class ChangedStateEventArgs : EventArgs
        {
            private bool state;

            /// <summary>
            /// コンストラクタ
            /// </summary>
            /// <param name="_state">ステート</param>
            internal ChangedStateEventArgs(bool _state)
            {
                this.state = _state;
            }

            /// <summary>
            /// ステート
            /// </summary>
            public bool State
            {
                get { return this.state; }
            }
        };

        #endregion

        #region Public EventHandler

        /// <summary>
        /// ステートが変化した時のイベントデリゲート
        /// </summary>
        /// <param name="e"></param>
        public delegate void ChangedStateEventHandler(Object sender, ChangedStateEventArgs e);

        #endregion

        #region Private Member

        private string filePath = "";
        private Mix.Tool.Utility.BindingItemCollection<int> identCollection = new Mix.Tool.Utility.BindingItemCollection<int>();
        private Mix.Tool.Utility.BindingItemCollection<int> attrCollection = new Mix.Tool.Utility.BindingItemCollection<int>();
        private Mix.Tool.Dynamics.PhysicsMaterialCollection mtrlCollection = null;
        private ChangedStateEventHandler onChangedDynamicsState = null;

        #endregion

        #region Public Event

        /// <summary>
        /// ダイナミクスのステートが変化した時に呼び出されます
        /// </summary>
        public event ChangedStateEventHandler ChangedState
        {
            add
            {
                value(this, new ChangedStateEventArgs(this.filePath.Length > 0));
                this.onChangedDynamicsState += value;
            }

            remove
            {
                this.onChangedDynamicsState -= value;
            }
        }

        #endregion

        #region Public Property

        /// <summary>
        /// ファイルパスの取得、または設定
        /// </summary>
        public string FilePath
        {
            get { return this.filePath; }
            set { this.filePath = value; }
        }

        /// <summary>
        /// 識別子コレクションの取得
        /// </summary>
        public Mix.Tool.Utility.BindingItemCollection<int> Identities
        {
            get { return this.identCollection; }
        }

        /// <summary>
        /// 属性コレクションの取得
        /// </summary>
        public Mix.Tool.Utility.BindingItemCollection<int> Attributes
        {
            get { return this.attrCollection; }
        }

        /// <summary>
        /// 物理マテリアルコレクションの取得
        /// </summary>
        public Mix.Tool.Dynamics.PhysicsMaterialCollection PhysicsMaterials
        {
            get { return this.mtrlCollection; }
        }

        #endregion

        #region Public Method

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Dynamics()
        {
            this.mtrlCollection = new Mix.Tool.Dynamics.PhysicsMaterialCollection(identCollection, attrCollection);
        }

        /// <summary>
        /// 読み込み
        /// </summary>
        public void Load()
        {
            if (this.filePath.Length == 0)
            {
                //ステートの変化を通知
                this.NotifyChangedState();
                return;
            }

            ////////////////////////////////////////////////////////////////////////////////////////////////////
            // 直前のものを破棄
            ////////////////////////////////////////////////////////////////////////////////////////////////////

            this.identCollection.Clear();
            this.attrCollection.Clear();
            this.mtrlCollection.Clear();

            ////////////////////////////////////////////////////////////////////////////////////////////////////
            // 読み込み
            ////////////////////////////////////////////////////////////////////////////////////////////////////

            try
            {
                if (System.IO.File.Exists(this.filePath) == true)
                {
                    //既存のものを読み込む
                    XmlDocument doc = new XmlDocument();
                    XmlElement elmRoot;
                    XmlAttribute attrRootVersion;

                    doc.Load(this.filePath);

                    ////////////////////////////////////////////////////////////////////////////////////////////////////
                    // ルート
                    ////////////////////////////////////////////////////////////////////////////////////////////////////

                    elmRoot = doc.FirstChild as XmlElement;
                    if (elmRoot.Name.Equals(Properties.Resources.XML_PDD_ROOT) == false)
                    {
                        throw new System.Exception(Properties.Resources.ILLEGAL_FILE_FORMAT);
                    }

                    attrRootVersion = elmRoot.Attributes[Properties.Resources.XML_PDD_VERSION];

                    ////////////////////////////////////////////////////////////////////////////////////////////////////
                    // チャイルド
                    ////////////////////////////////////////////////////////////////////////////////////////////////////

                    foreach (XmlElement elmChild in elmRoot.ChildNodes)
                    {
                        if (elmChild.Name.Equals(Properties.Resources.XML_PDD_IDENTITIES) == true)
                        {
                            this.LoadIdentities(elmChild);
                        }
                        else if (elmChild.Name.Equals(Properties.Resources.XML_PDD_ATTRIBUTES) == true)
                        {
                            this.LoadAttributes(elmChild);
                        }
                        else if (elmChild.Name.Equals(Properties.Resources.XML_PDD_PHYSICS_MATERIALS) == true)
                        {
                            this.LoadPhysicsMaterials(elmChild);
                        }
                    }
                }
                else
                {
                    //新規に作成する
                    this.Save();
                }

                //ステートの変化を通知
                this.NotifyChangedState();
            }
            catch (System.Exception excep)
            {
                this.identCollection.Clear();
                this.attrCollection.Clear();
                this.mtrlCollection.Clear();

                throw excep;
            }
        }

        /// <summary>
        /// 保存
        /// </summary>
        public void Save()
        {
            if ((this.filePath.Length == 0) ||
                ((this.identCollection.Count == 0) && (this.attrCollection.Count == 0) && (this.mtrlCollection.Count == 0)))
            {
                return;
            }

            try
            {
                XmlDocument doc;
                XmlAttribute attr;
                XmlElement elmRoot;
                XmlElement elmIdentities;
                XmlElement elmAttributes;
                XmlElement elmPhysicsMaterials;
                XmlElement elm;

                doc = new XmlDocument();

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // ルート
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                elmRoot = doc.CreateElement(Properties.Resources.XML_PDD_ROOT);

                attr = doc.CreateAttribute(Properties.Resources.XML_PDD_VERSION);
                attr.Value = Properties.Resources.XML_PDD_VERSION_VALUE;
                elmRoot.Attributes.Append(attr);

                doc.AppendChild(elmRoot);

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // 識別子コレクション
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                elmIdentities = doc.CreateElement(Properties.Resources.XML_PDD_IDENTITIES);

                foreach (Mix.Tool.Utility.BindingItem<int> item in this.identCollection)
                {
                    elm = doc.CreateElement(Properties.Resources.XML_PDD_ITEM);

                    attr = doc.CreateAttribute(Properties.Resources.XML_PDD_NAME);
                    attr.Value = item.Name;
                    elm.Attributes.Append(attr);

                    attr = doc.CreateAttribute(Properties.Resources.XML_PDD_VALUE);
                    attr.Value = item.Value;
                    elm.Attributes.Append(attr);

                    elmIdentities.AppendChild(elm);
                }

                elmRoot.AppendChild(elmIdentities);

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // 属性コレクション
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                elmAttributes = doc.CreateElement(Properties.Resources.XML_PDD_ATTRIBUTES);

                foreach (Mix.Tool.Utility.BindingItem<int> item in this.attrCollection)
                {
                    elm = doc.CreateElement(Properties.Resources.XML_PDD_ITEM);

                    attr = doc.CreateAttribute(Properties.Resources.XML_PDD_NAME);
                    attr.Value = item.Name;
                    elm.Attributes.Append(attr);

                    attr = doc.CreateAttribute(Properties.Resources.XML_PDD_VALUE);
                    attr.Value = item.Value;
                    elm.Attributes.Append(attr);

                    elmAttributes.AppendChild(elm);
                }

                elmRoot.AppendChild(elmAttributes);

                ////////////////////////////////////////////////////////////////////////////////////////////////////
                // 物理マテリアルコレクション
                ////////////////////////////////////////////////////////////////////////////////////////////////////

                elmPhysicsMaterials = doc.CreateElement(Properties.Resources.XML_PDD_PHYSICS_MATERIALS);

                foreach (Mix.Tool.Dynamics.PhysicsMaterial item in this.mtrlCollection.DataSource)
                {
                    XmlElement elmItem;

                    elmItem = doc.CreateElement(Properties.Resources.XML_PDD_ITEM);
                    attr = doc.CreateAttribute(Properties.Resources.XML_PDD_NAME);
                    attr.Value = item.Name;
                    elmItem.Attributes.Append(attr);

                    elm = doc.CreateElement(Properties.Resources.XML_PDD_IMAGEFILEPATH);
                    attr = doc.CreateAttribute(Properties.Resources.XML_PDD_VALUE);
                    attr.Value = item.ImageFilePath;
                    elm.Attributes.Append(attr);
                    elmItem.AppendChild(elm);

                    elm = doc.CreateElement(Properties.Resources.XML_PDD_IDENTITY);
                    attr = doc.CreateAttribute(Properties.Resources.XML_PDD_VALUE);
                    attr.Value = item.Identifier.Name;
                    elm.Attributes.Append(attr);
                    elmItem.AppendChild(elm);

                    foreach (string attrName in item.Attributes)
                    {
                        elm = doc.CreateElement(Properties.Resources.XML_PDD_ATTRIBUTE);
                        attr = doc.CreateAttribute(Properties.Resources.XML_PDD_VALUE);
                        attr.Value = attrName;
                        elm.Attributes.Append(attr);
                        elmItem.AppendChild(elm);
                    }

                    elm = doc.CreateElement(Properties.Resources.XML_PDD_FRICTION);
                    attr = doc.CreateAttribute(Properties.Resources.XML_PDD_VALUE);
                    attr.Value = item.Friction.ToString();
                    elm.Attributes.Append(attr);
                    elmItem.AppendChild(elm);

                    elm = doc.CreateElement(Properties.Resources.XML_PDD_RESTITUTION);
                    attr = doc.CreateAttribute(Properties.Resources.XML_PDD_VALUE);
                    attr.Value = item.Restitution.ToString();
                    elm.Attributes.Append(attr);
                    elmItem.AppendChild(elm);

                    elmPhysicsMaterials.AppendChild(elmItem);
                }

                elmRoot.AppendChild(elmPhysicsMaterials);

                doc.Save(this.filePath);
            }
            catch (System.Exception excep)
            {
                throw excep;
            }
        }

        #endregion

        #region Load Method

        /// <summary>
        /// 識別子コレクションの読み込み
        /// </summary>
        /// <param name="elmParent"></param>
        private void LoadIdentities(XmlElement elmParent)
        {
            try
            {
                foreach (XmlElement elmChild in elmParent.ChildNodes)
                {
                    if (elmChild.Name.Equals(Properties.Resources.XML_PDD_ITEM) == true)
                    {
                        string name = elmChild.Attributes[Properties.Resources.XML_PDD_NAME].Value;
                        if (name.Length > 0)
                        {
                            int value;

                            if (int.TryParse(elmChild.Attributes[Properties.Resources.XML_PDD_VALUE].Value, out value) == true)
                            {
                                this.identCollection.Add(new Mix.Tool.Utility.BindingItem<int>(name, value));
                            }
                        }
                    }
                }
            }
            catch
            {
                throw;
            }
        }

        /// <summary>
        /// 属性コレクションの読み込み
        /// </summary>
        /// <param name="elmParent"></param>
        private void LoadAttributes(XmlElement elmParent)
        {
            try
            {
                foreach (XmlElement elmChild in elmParent.ChildNodes)
                {
                    if (elmChild.Name.Equals(Properties.Resources.XML_PDD_ITEM) == true)
                    {
                        string name = elmChild.Attributes[Properties.Resources.XML_PDD_NAME].Value;
                        if (name.Length > 0)
                        {
                            int value;

                            if (int.TryParse(elmChild.Attributes[Properties.Resources.XML_PDD_VALUE].Value, out value) == true)
                            {
                                this.attrCollection.Add(new Mix.Tool.Utility.BindingItem<int>(name, value));
                            }
                        }
                    }
                }
            }
            catch
            {
                throw;
            }
        }

        /// <summary>
        /// 物理マテリアルコレクションの読み込み
        /// </summary>
        /// <param name="elmParent"></param>
        private void LoadPhysicsMaterials(XmlElement elmParent)
        {
            try
            {
                foreach (XmlElement elmItem in elmParent.ChildNodes)
                {
                    if (elmItem.Name.Equals(Properties.Resources.XML_PDD_ITEM) == true)
                    {
                        Mix.Tool.Dynamics.PhysicsMaterial mtrl = this.mtrlCollection.Add();

                        mtrl.Name = elmItem.Attributes[Properties.Resources.XML_PDD_NAME].Value;

                        foreach (XmlElement elmValue in elmItem.ChildNodes)
                        {
                            if (elmValue.Name.Equals(Properties.Resources.XML_PDD_IMAGEFILEPATH) == true)
                            {
                                mtrl.ImageFilePath = elmValue.Attributes[Properties.Resources.XML_PDD_VALUE].Value;
                            }
                            else if (elmValue.Name.Equals(Properties.Resources.XML_PDD_IDENTITY) == true)
                            {
                                mtrl.Identifier.Name = elmValue.Attributes[Properties.Resources.XML_PDD_VALUE].Value;
                            }
                            else if (elmValue.Name.Equals(Properties.Resources.XML_PDD_ATTRIBUTE) == true)
                            {
                                mtrl.Attributes.Add(elmValue.Attributes[Properties.Resources.XML_PDD_VALUE].Value);
                            }
                            else if (elmValue.Name.Equals(Properties.Resources.XML_PDD_FRICTION) == true)
                            {
                                mtrl.Friction = float.Parse(elmValue.Attributes[Properties.Resources.XML_PDD_VALUE].Value);
                            }
                            else if (elmValue.Name.Equals(Properties.Resources.XML_PDD_RESTITUTION) == true)
                            {
                                mtrl.Restitution = float.Parse(elmValue.Attributes[Properties.Resources.XML_PDD_VALUE].Value);
                            }
                        }
                    }
                }
            }
            catch
            {
                throw;
            }
        }

        #endregion

        #region Misc Method

        /// <summary>
        /// ステートの変化を通知
        /// </summary>
        private void NotifyChangedState()
        {
            if (this.onChangedDynamicsState != null)
            {
                this.onChangedDynamicsState(this, new ChangedStateEventArgs(this.filePath.Length > 0));
            }
        }

        #endregion
    }
}
