using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.IO;
using MikuMikuDance.XNA.Builder.Build;
using MikuMikuDance.XNA;
using MikuMikuDance.XNA.Model;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using MikuMikuDance.XNA.Motion;
using MikuMikuDance.XNA.Model.ModelData;

namespace MikuMikuDanceXNADemo7
{
    /// <summary>
    /// MikuMikuDance for XNAのデモ
    /// このデモでは
    /// -コンテンツの任意のタイミングでのビルド方法
    /// -フォームアプリケーション下でのMMDXの使用方法
    /// が書かれています
    /// </summary>
    /// <remarks>
    /// MMDX及びXNAの目的からして、このサンプルは正規の方法よりも裏技に近いものです。
    /// このサンプルを用いれば任意のタイミングでビルドできたり、
    /// フォームアプリケーションと同時にMMDXを使えますが、
    /// このサンプルの手法を本当に使う必要があるのか、使う前によく考えることをお勧めします。
    /// 
    /// あとMMDBuilderを使用する際、どういうわけか使用するプロジェクトにApp.configのような設定をしなければ動きません。
    /// 入れないとMSBuild内でのバージョン違いによりビルドに失敗します。
    /// 
    /// なお、動作にはXNA Game Studio 3.1 SDK(Distributionじゃダメ)が必要です。
    /// MMDBuilder内のクラスがこいつが無いと動作しません。(コンテンツビルド機能)
    /// MMDXWinFormsExpantion内のクラスはDistributionでも動作します。
    /// どちらにしてもWindows専用の機能ですが……
    /// 
    /// なお、読み込みが遅いのは読み込んだモデルをパイプラインでコンパイルする処理が遅いからです……
    /// その辺は、モデルの読むタイミングをズラす等、上手く工夫して下さい。(できるよね？)
    /// </remarks>
    public partial class FrmMain : Form
    {
        //MMDコンテンツビルドクラス
        MMDBuilder mmdBuilder;
        //MMDモデルクラス
        MMDModel model = null;
        MMDMotion motion = null;
        int count = 0;
        //時間計測用のストップウォッチ
        Stopwatch stopwatch = new Stopwatch();
        decimal beforeUpdateTime = -1;
        /// <summary>
        /// 既定のコンストラクタ
        /// </summary>
        public FrmMain()
        {
            InitializeComponent();
            //時刻計測イベント時に時間計測を開始する
            this.Shown += (object sender, EventArgs e) => stopwatch.Start();
            //すぐにメニューの読み込みが表示されるようにする
            this.Shown += new EventHandler(MILoadModel_Click);
            //参照しているMMDImporterのアセンブリ参照を取得
            string dllPath = typeof(MikuMikuDance.XNA.Model.MMDImporter).Assembly.Location;
            //MMDビルダーを作成
            mmdBuilder = new MMDBuilder(dllPath);
            //MMDXControl(MMDXWinForm動作用のヘルパーコントロール)の初期化イベント時に
            //コンテントマネージャの読み込み先をビルダーの出力フォルダに指定するようにする。
            mmdxExControl1.Initialize += 
                (object sender, EventArgs e) => mmdxExControl1.Content.RootDirectory = mmdBuilder.OutputDirectory;
            //MMDXControlのUpdateとDrawイベントをフック
            mmdxExControl1.UpdateEvent += new EventHandler(mmdxExControl1_Update);
            mmdxExControl1.DrawEvent += new EventHandler(mmdxExControl1_Draw);
        }

        
        
        //メニューのモデルロードをクリック
        private void MILoadModel_Click(object sender, EventArgs e)
        {
            //FileDialogを使って開く
            //開くディレクトリをcontentフォルダに変更
            fileDialog.InitialDirectory = Path.GetFullPath(
                Path.Combine(Assembly.GetExecutingAssembly().Location, "../../../../Content"));
            fileDialog.Title = "MMDモデルのロード";
            fileDialog.FileName = "";
            fileDialog.Filter = "MikuMikuDanceモデル(*.pmd)|*.pmd|" +
                                "すべてのファイル(*.*)|*.*";
            if (fileDialog.ShowDialog() == DialogResult.OK)
            {//開いた場合はロードモデルで読み込む
                LoadModel(fileDialog.FileName);
            }
        }
        /// <summary>
        /// モデルのビルド及び読み込み
        /// </summary>
        /// <param name="filename">ファイル名</param>
        private void LoadModel(string filename)
        {
            //待ちカーソルに変更
            Cursor = Cursors.WaitCursor;
            //ビルド設定
            int number = count++;
            mmdBuilder.Clear();
            mmdBuilder.Add(filename, "Model" + number.ToString(), null, "MMDProcessor");

            //MMDBuilderでビルド
            string buildError = mmdBuilder.Build();

            if (string.IsNullOrEmpty(buildError))
            {//ビルド成功
                model = mmdxExControl1.MMDX.LoadModel("Model" + number.ToString(), mmdxExControl1.GraphicsDevice, QuatTransform.Identity);
                if (motion != null)
                    model.Player.SetMotion(0, motion, true);
                lblModel.Text = Path.GetFileNameWithoutExtension(filename);
            }
            else
            {
                // ビルドが失敗した場合、エラー メッセージを表示します。
                MessageBox.Show(buildError, "Error");
            }

            Cursor = Cursors.Arrow; 
        }

        private void MILoadMotion_Click(object sender, EventArgs e)
        {
            //FileDialogを使って開く
            //開くディレクトリをcontentフォルダに変更
            fileDialog.InitialDirectory = Path.GetFullPath(
                Path.Combine(Assembly.GetExecutingAssembly().Location, "../../../../Content"));
            fileDialog.Title = "MMDモーションのロード";
            fileDialog.FileName = "";
            fileDialog.Filter = "MikuMikuDanceモーション(*.vmd)|*.vmd|" +
                                "すべてのファイル(*.*)|*.*";
            if (fileDialog.ShowDialog() == DialogResult.OK)
            {//開いた場合はロードモデルで読み込む
                //待ちカーソルに変更
                Cursor = Cursors.WaitCursor;
                //ビルド設定
                mmdBuilder.Clear();
                int number = count++;
                mmdBuilder.Add(fileDialog.FileName, "Motion" + number.ToString(), null, "MMDMotionProcessor");
                //MMDBuilderでビルド
                string buildError = mmdBuilder.Build();

                if (string.IsNullOrEmpty(buildError))
                {//ビルド成功
                    motion = mmdxExControl1.MMDX.LoadMotion("Motion" + number.ToString());
                    if (model != null)
                        model.Player.SetMotion(0, motion, true);
                    lblMotion.Text = Path.GetFileNameWithoutExtension(fileDialog.FileName);
                }
                else
                {
                    // ビルドが失敗した場合、エラー メッセージを表示します。
                    MessageBox.Show(buildError, "Error");
                }

                Cursor = Cursors.Arrow;
            }
        }

        private void MIPlay_Click(object sender, EventArgs e)
        {
            if (model != null && motion != null)
            {
                model.Player.Reset(0);
                model.Player.Start(0, MILoop.Checked);
            }
        }
        private void MIStop_Click(object sender, EventArgs e)
        {
            if (model != null && motion != null && model.Player.IsPlay(0))
                model.Player.Stop(0);
        }
        void mmdxExControl1_Update(object sender, EventArgs e)
        {
            if (model != null)
            {
                model.Update(null);//nullにするとMMDXからのイベントで渡されるGameTimeもnullになるのだが……、MMDX内では基本的に自前で時間計測をしている.
                MIPlay.Enabled = (motion != null) && !model.Player.IsPlay(0);
                MIStop.Enabled = (motion != null) && model.Player.IsPlay(0);
            }
            else
            {
                MIPlay.Enabled = false;
                MIStop.Enabled = false;
            }
            //物理エンジンの更新はGameが使えないので自分で更新を呼び出す必要があります
            decimal elapsedTime = (decimal)stopwatch.ElapsedTicks / (decimal)Stopwatch.Frequency - beforeUpdateTime;
            if (elapsedTime >= 0.016m)
            {
                mmdxExControl1.MMDX.Update((beforeUpdateTime != -1 ? 1f / 60f : 0f));
                if (beforeUpdateTime == -1)
                    beforeUpdateTime = (decimal)stopwatch.ElapsedTicks / (decimal)Stopwatch.Frequency;
                else
                    beforeUpdateTime += 1m / 60m;
            }
        }
        void mmdxExControl1_Draw(object sender, EventArgs e)
        {
            //MMDXWinFormsExtentionではGameが使えないので自分で描画ルーチンを書く必要があります
            //背景色でクリア
            Microsoft.Xna.Framework.Graphics.Color color =
                new Microsoft.Xna.Framework.Graphics.Color
                    (mmdxExControl1.BackColor.R, mmdxExControl1.BackColor.G, mmdxExControl1.BackColor.B);
            mmdxExControl1.GraphicsDevice.Clear(color);

            //モデル描画用の初期化ルーチン
            MMDModel.GraphicsSetup(mmdxExControl1.GraphicsDevice, false);
            if (model != null)
            {//モデルが読み込まれているなら描画
                model.Draw(mmdxExControl1.GraphicsDevice, stopwatch.ElapsedTicks, false);
            }
        }

        

        
    }
}
