using System;
using System.Windows.Forms;
using System.Drawing;

namespace Mix.Tool.ModelEditor
{
    /// <summary>
    /// マップコリジョンエディターフォームクラス
    /// </summary>
    public partial class TerrainRigidBodyEditorForm : Mix.Tool.Docking.DockContent
    {
        #region Constant Memer

        //パートリストの画像のマージン
        private const int PL_IMAGE_MARGIN = 2;
        private const int PL_IMAGE_MARGIN2 = PL_IMAGE_MARGIN * 2;

        //パート画像パネルのマージン
        private const int PIP_IMAGE_MAGIN = 2;
        private const int PIP_IMAGE_MAGIN2 = 4;

        #endregion

        #region Private Member

        private Mix.Tool.Dynamics.PhysicsMaterialCollection physicsMaterials = null;
        private Mix.Tool.Dynamics.Design.Terrain src = null;
        private bool validChangeSrc = true;

        #endregion

        #region Public MapCollisionEditorForm Method

        /// <summary>
        /// ソースの取得、または設定
        /// </summary>
        public Mix.Tool.Dynamics.Design.Terrain Source
        {
            get { return this.src; }
            set
            {
                if (this.src != value)
                {
                    ////////////////////////////////////////////////////////////////////////////////////////////////////
                    // 直前のソースの終了処理
                    ////////////////////////////////////////////////////////////////////////////////////////////////////

                    this.TermSource();

                    ////////////////////////////////////////////////////////////////////////////////////////////////////
                    // 新規のソースの開始処理
                    ////////////////////////////////////////////////////////////////////////////////////////////////////

                    this.src = value;

                    this.BeginInitControl();
                    this.InitSource();
                    this.EndInitControl();
                }

                this.UpdateControlState();
            }
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="physicsMaterials">物理マテリアルコレクション</param>
        public TerrainRigidBodyEditorForm(Mix.Tool.Dynamics.PhysicsMaterialCollection _physicsMaterials)
        {
            if (_physicsMaterials == null)
            {
                throw new System.ArgumentNullException("_physicsMaterials");
            }

            InitializeComponent();

            this.physicsMaterials = _physicsMaterials;
            this.physicsMaterials.DataSource.ListChanged += new System.ComponentModel.ListChangedEventHandler(this.PhysicsMaterials_ListChanged);

            ////////////////////////////////////////////////////////////////////////////////////////////////////
            // コントロールの初期化
            ////////////////////////////////////////////////////////////////////////////////////////////////////

            this.lbPart.ItemHeight = this.tbPart.Value;

            this.lbPhysicsMaterial.DataSource = physicsMaterials.DataSource;
            this.lbPhysicsMaterial.ItemHeight = this.tbPhysicsMaterial.Value;

            this.UpdateControlState();
        }

        private void MapCollisionEditorForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.TermSource();
            this.physicsMaterials.DataSource.ListChanged -= new System.ComponentModel.ListChangedEventHandler(this.PhysicsMaterials_ListChanged);
        }

        #endregion

        #region Part Method

        private void tlpPart_SizeChanged(object sender, EventArgs e)
        {
            TableLayoutPanel tlp = sender as TableLayoutPanel;

            if ((tlp.ColumnCount >= 1) &&
                (tlp.RowCount >= 2))
            {
                tlp.RowStyles[0] = new RowStyle(SizeType.Absolute, (float)tlp.Width);
                this.panelPartImage.Invalidate();
            }
        }

        private void panelPartImage_Paint(object sender, PaintEventArgs e)
        {
            Rectangle rect = new Rectangle();

            rect.X = this.lbPart.Left;
            rect.Y = this.lbPhysicsMaterial.Top;
            rect.Width = lbPart.Width;
            rect.Height = rect.Width;

            e.Graphics.DrawRectangle(SystemPens.ControlLight, rect);

            if (this.lbPart.SelectedIndex >= 0)
            {
                Mix.Tool.Dynamics.Design.TerrainPart part = this.lbPart.SelectedItem as Mix.Tool.Dynamics.Design.TerrainPart;
                Image img = part.Image;

                if (img != null)
                {
                    //画像を持っている(画像と色を合成して描画)
                    int width = rect.Width;
                    int imgWidth = width - PIP_IMAGE_MAGIN2;

                    System.Drawing.Imaging.ColorMatrix colorMat = new System.Drawing.Imaging.ColorMatrix();
                    System.Drawing.Imaging.ImageAttributes imgAttr = new System.Drawing.Imaging.ImageAttributes();

                    if ((imgWidth < img.Width) ||
                        (imgWidth < img.Height))
                    {
                        //画像が矩形内に納まっていない(縦横比を維持して縮小)
                        float ratio;

                        if (img.Width < img.Height)
                        {
                            ratio = (float)imgWidth / (float)img.Height;
                        }
                        else
                        {
                            ratio = (float)imgWidth / (float)img.Width;
                        }

                        rect.Width = (int)((float)img.Width * ratio);
                        rect.Height = (int)((float)img.Height * ratio);
                        rect.X = rect.X + PIP_IMAGE_MAGIN + (imgWidth - rect.Width) / 2;
                        rect.Y = rect.Y + PIP_IMAGE_MAGIN + (imgWidth - rect.Height) / 2;
                    }
                    else
                    {
                        //画像が矩形内に納まっている(センタリング)
                        rect.X = rect.X + PIP_IMAGE_MAGIN + (imgWidth - img.Width) / 2;
                        rect.Y = rect.Y + PIP_IMAGE_MAGIN + (imgWidth - img.Height) / 2;
                        rect.Width = img.Width;
                        rect.Height = img.Height;
                    }

                    colorMat.Matrix00 = part.Color.X;
                    colorMat.Matrix11 = part.Color.Y;
                    colorMat.Matrix22 = part.Color.Z;
                    colorMat.Matrix33 = part.Color.W;
                    colorMat.Matrix44 = 1.0f;

                    imgAttr.SetColorMatrix(colorMat);

                    e.Graphics.DrawImage(img, rect, 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imgAttr);
                }
                else
                {
                    //画像を持っていない(矩形塗りつぶす)
                    rect.X = rect.X + PIP_IMAGE_MAGIN;
                    rect.Y = rect.Y + PIP_IMAGE_MAGIN;
                    rect.Width = rect.Width - PIP_IMAGE_MAGIN2;
                    rect.Height = rect.Width;

                    using (Brush brush = new SolidBrush(part.Color.ToColor()))
                    {
                        e.Graphics.FillRectangle(brush, rect);
                    }
                }
            }
        }

        private void lbPart_SelectedIndexChanged(object sender, EventArgs e)
        {
            ListBox lb = sender as ListBox;
            int index = lb.SelectedIndex;

            if (index >= 0)
            {
                Mix.Tool.Dynamics.Design.TerrainPart part = lb.Items[index] as Mix.Tool.Dynamics.Design.TerrainPart;

                this.lbPhysicsMaterial.Selected = part.PhysicsMaterial;
                this.panelPartImage.Invalidate();
            }
        }

        private void lbPart_DrawItem(object sender, DrawItemEventArgs e)
        {
            if (e.Index < 0)
            {
                return;
            }

            Mix.Tool.Dynamics.Design.TerrainPart part = this.lbPart.Items[e.Index] as Mix.Tool.Dynamics.Design.TerrainPart;

            int itemHeight = lbPart.ItemHeight;
            Rectangle itemRect = this.lbPart.GetItemRectangle(e.Index);
            Rectangle imgRect = new Rectangle();

            imgRect.X = PL_IMAGE_MARGIN;
            imgRect.Y = itemRect.Y + PL_IMAGE_MARGIN;
            imgRect.Width = itemHeight - PL_IMAGE_MARGIN2;
            imgRect.Height = imgRect.Width;

            //背景の描画
            e.DrawBackground();

            //画像の描画
            if (part.Image != null)
            {
                System.Drawing.Imaging.ColorMatrix colorMat = new System.Drawing.Imaging.ColorMatrix();
                System.Drawing.Imaging.ImageAttributes imgAttr = new System.Drawing.Imaging.ImageAttributes();

                colorMat.Matrix00 = part.Color.X;
                colorMat.Matrix11 = part.Color.Y;
                colorMat.Matrix22 = part.Color.Z;
                colorMat.Matrix33 = 1.0f;
                colorMat.Matrix44 = 1.0f;

                imgAttr.SetColorMatrix(colorMat);

                e.Graphics.DrawImage(
                    part.Image,
                    imgRect,
                    0, 0, part.Image.Width, part.Image.Height,
                    GraphicsUnit.Pixel,
                    imgAttr);
            }
            else
            {
                using (Brush brush = new SolidBrush(part.Color.ToColor()))
                {
                    e.Graphics.FillRectangle(brush, imgRect);
                }
            }

            //名前の描画
            if (part.Name.Length > 0)
            {
                Rectangle strRect = new Rectangle();
                StringFormat strFormat = new StringFormat();
                Brush strBrush = null;

                if (part.PhysicsMaterial != null)
                {
                    strBrush = ((e.State & DrawItemState.Selected) == DrawItemState.Selected) ? SystemBrushes.HighlightText : SystemBrushes.WindowText;
                }
                else
                {
                    strBrush = SystemBrushes.GrayText;
                }

                strFormat.Alignment = StringAlignment.Near;
                strFormat.LineAlignment = StringAlignment.Center;

                strRect.X = imgRect.Right + PL_IMAGE_MARGIN;
                strRect.Y = itemRect.Y;
                strRect.Width = itemRect.Width - imgRect.Right;
                strRect.Height = itemRect.Height;

                e.Graphics.DrawString(part.Name, this.lbPart.Font, strBrush, strRect, strFormat);
            }

            //フォーカス矩形の描画
            e.DrawFocusRectangle();
        }

        private void tbPart_ValueChanged(object sender, EventArgs e)
        {
            TrackBar tb = sender as TrackBar;

            this.lbPart.ItemHeight = tb.Value;
        }

        private void part_PhysicsMaterialDiscarded(object sender, EventArgs e)
        {
            Mix.Tool.Dynamics.Design.TerrainPart part = sender as Mix.Tool.Dynamics.Design.TerrainPart;
            part.PhysicsMaterial = null;
        }

        #endregion

        #region PhysicsMaterial Method

        private void tbPhysicsMaterial_ValueChanged(object sender, EventArgs e)
        {
            TrackBar tb = sender as TrackBar;

            this.lbPhysicsMaterial.ItemHeight = tb.Value;
        }

        private void lbPhysicsMaterial_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.AvailableSource == true)
            {
                PhysicsMaterialListBox lb = sender as PhysicsMaterialListBox;
                int partItemIndex = this.lbPart.SelectedIndex;

                if (partItemIndex >= 0)
                {
                    Mix.Tool.Dynamics.Design.TerrainPart part = this.lbPart.Items[partItemIndex] as Mix.Tool.Dynamics.Design.TerrainPart;

                    part.PhysicsMaterial = lb.Selected;
                    this.lbPart.Invalidate(this.lbPart.GetItemRectangle(partItemIndex));
                }
                else
                {
                    lb.Selected = null;
                }
            }
        }

        private void PhysicsMaterials_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e)
        {
            if ((e.ListChangedType == System.ComponentModel.ListChangedType.ItemAdded) ||
                (e.ListChangedType == System.ComponentModel.ListChangedType.ItemDeleted) ||
                (e.ListChangedType == System.ComponentModel.ListChangedType.ItemMoved))
            {
                this.lbPart.SelectedItem = null;
                this.lbPhysicsMaterial.Selected = null;
                this.lbPart.Invalidate();
            }
            
            if (e.ListChangedType == System.ComponentModel.ListChangedType.ItemChanged)
            {
                if ((e.PropertyDescriptor.DisplayName.Equals("Name") == true) ||
                    (e.PropertyDescriptor.DisplayName.Equals("Image") == true))
                {
                    this.lbPhysicsMaterial.Invalidate();
                }
            }
            else
            {
                this.lbPhysicsMaterial.Invalidate();
            }
        }

        #endregion

        #region Misc Method

        private void UpdateControlState()
        {
            bool enabled = this.src != null;

            foreach (Control ctrl in this.Controls)
            {
                ctrl.Enabled = enabled;
            }

            if (enabled == true)
            {
                this.Activate();
            }
        }

        private void BeginInitControl()
        {
            this.validChangeSrc = false;
        }

        private void EndInitControl()
        {
            this.validChangeSrc = true;
        }

        private bool AvailableSource
        {
            get
            {
                if ((this.src == null) ||
                    (this.validChangeSrc == false))
                {
                    return false;
                }

                return true;
            }
        }

        private void InitSource()
        {
            if (this.src != null)
            {
                this.lbPart.DataSource = this.src.Parts.DataSource;

                foreach (Mix.Tool.Dynamics.Design.TerrainPart part in this.src.Parts)
                {
                    part.PhysicsMaterialDiscarded += new EventHandler(this.part_PhysicsMaterialDiscarded);
                }
            }
            else
            {
                this.lbPart.DataSource = null;
            }
        }

        private void TermSource()
        {
            if (this.src != null)
            {
                foreach (Mix.Tool.Dynamics.Design.TerrainPart part in this.src.Parts)
                {
                    part.PhysicsMaterialDiscarded -= new EventHandler(this.part_PhysicsMaterialDiscarded);
                }
            }
        }

        #endregion
    }
}
