using System;
using System.Collections.Generic;
using System.Text;
using Yanesdk.Draw;
using Yanesdk.Ytl;
using Yanesdk.Timer;
using Yanesdk.Math;
using Yanesdk.Sound;
using Yanesdk.Input;
using Yanesdk.System;
using Kyojin.Input;
using Yamalib.Yanesdkext.System;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using Yamalib.Input;
using Yamalib.GUI;
using Yamalib.Draw;
using Kyojin.Scene.YesNo;
using Yamalib.Yanesdkext.Draw;
using Yamalib.Logger;

namespace Kyojin.Model
{
    /// <summary>
    /// ウィンドウとかFpsTimerとか、ゲーム全体で使うオブジェクトを持つクラス。
    /// </summary>
    public class GameSystem : IDisposable
    {
        #region 定数値
        /// <summary>
        /// デフォルトのFPS。
        /// </summary>
        public const int DEFAULT_FPS = 60;

        // システムSEの名前付け
        public enum SYS_SE : int
        {
            DISABLE = 0,
            ENABLE,
            CLICK_TITLE_ACTIVE,
            CLICK_TITLE_NEGATIVE,
        }
        #endregion

        #region メンバフィールド

        private readonly SDLWindow2DGl window = new SDLWindow2DGl();
        private readonly IMouseInput mouse;
        private readonly IKeyInput key;		//!< キーボードオブジェクト
        private readonly IMouseInput keymouse;	//!< マウスオブジェクト
        private readonly FpsTimer fpsTimer = new FpsTimer();
        private readonly TaskController taskController = new TaskController();
        private readonly SceneController<String> sceneController = new SceneController<String>();
        private readonly SmartTextureLoader smartTextureLoader = new SmartTextureLoader();
        private readonly SmartSoundLoader smartSoundLoader = new SmartSoundLoader();
        private readonly SmartFontLoader smartFontLoader = new SmartFontLoader();
        private readonly SmartSurfaceLoader smartSurfaceLoader = new SmartSurfaceLoader();
        private readonly Rand rand = new Rand();
        private readonly TimestampLogWriter logWriter;
        private readonly SoundLoader commonBgmLoader;
        private readonly SoundLoader commonSeLoader;
        private readonly FontLoader commonFontLoader;
        private UserInfo userInfo;
        private OptionInfo optionInfo;
        private readonly OptionScenePram optionSceneParam = new OptionScenePram();
        private readonly BookmarkSceneParam bookmarkSceneParam = new BookmarkSceneParam();
        private readonly TelopParam telopParam = new TelopParam();
        private readonly ScenarioSceneParam scenarioSceneParam = new ScenarioSceneParam();
        private readonly KyojinEndingSceneParam kyojinEndingSceneParam = new KyojinEndingSceneParam();
        private readonly EyeCatcheSceneParam eyeCatcheSceneParam = new EyeCatcheSceneParam();
        private bool fromTitleScene = false;
        private readonly YesNoScene yesNoTask;
        private KeyConfigData keyConfigData;

        # endregion

        # region プロパティ

        public SDLWindow2DGl Window
        {
            get { return window; }
        }

        public delegate void ShowDialogEvent();
        private ShowDialogEvent finishDialogEvent;

        /// <summary>
        /// アプリケーションが
        /// </summary>
        public ShowDialogEvent FinishDialogEvent
        {
            get { return finishDialogEvent; }
            set { finishDialogEvent = value; }
        }

        /// <summary>
        /// タスクコントローラーの取得プロパティ
        /// </summary>
        public TaskController TaskController { get { return taskController; } }
        /// <summary>
        /// シーンコントローラーの取得プロパティ
        /// </summary>
        public SceneController<String> SceneController { get { return sceneController; } }
        /// <summary>
        /// Screenの取得プロパティ
        /// </summary>
        public Screen2DGl Screen { get { return Window.Screen; } }
        /// <summary>
        /// マウスの取得プロパティ
        /// </summary>
        public IMouseInput Mouse { get { return mouse; } }

        public MouseInput MouseOriginal { get { return ((NullableMouseInput)Mouse).GetOriginal() as MouseInput; } }
        /// <summary>
        /// キーデバイスの取得
        /// </summary>
        public IKeyInput Key
        {
            get { return key; }
        }
        /// <summary>
        /// キーをデコレートしたマウス
        /// </summary>
        public IMouseInput KeyDecolateMouse { get { return keymouse; } }
        /// <summary>
        /// ジョイスティック
        /// </summary>
        /// <returns></returns>
        public JoyStick JoyStick
        {
            get
            {
                return (JoyStick)((MousedKey)((NullableKeyInput)this.key).GetOriginal()).GetDevice(1);
            }
        }
        /// <summary>
        ///  ジョイスティックが接続されているか？
        /// </summary>
        public bool IsEnableJoystick
        {
            get { return JoyStick.DeviceName != "KeyInputNullDevice"; }
        }

        /// <summary>
        /// 可能なインプットデバイスをNullデバイスにスイッチする
        /// </summary>
        public void DisableAllInputDevie()
        {
            NullableMouseInput aMouse = (NullableMouseInput)Mouse;
            NullableKeyInput aKey = (NullableKeyInput)Key;
            NullableMouseInput aKeyMouse = (NullableMouseInput)KeyDecolateMouse;
            aMouse.Disable();
            aKeyMouse.Disable();
            aKey.Disable();
        }

        /// <summary>
        /// 可能なインプットデバイスをNullデバイスから通常のデバイスにスイッチする
        /// </summary>
        public void EnableAllInputDevie()
        {
            NullableMouseInput aMouse = (NullableMouseInput)Mouse;
            NullableKeyInput aKey = (NullableKeyInput)Key;
            NullableMouseInput aKeyMouse = (NullableMouseInput)KeyDecolateMouse;
            aMouse.Enable();
            aKeyMouse.Enable();
            aKey.Enable();
        }

        /// <summary>
        /// FpsTimerの取得プロパティ
        /// </summary>
        public FpsTimer FpsTimer { get { return fpsTimer; } }
        /// <summary>
        /// SmartTextureLoader取得プロパティ
        /// </summary>
        public SmartTextureLoader SmartTextureLoader { get { return smartTextureLoader; } }
        /// <summary>
        /// SmartSoundLoader取得プロパティ
        /// </summary>
        public SmartSoundLoader SmartSoundLoader { get { return smartSoundLoader; } }
        /// <summary>
        /// SmartFontLoader取得プロパティ
        /// </summary>
        public SmartFontLoader SmartFontLoader { get { return smartFontLoader; } }
        /// <summary>
        /// SmartSurfaceLoader取得プロパティ
        /// </summary>
        public SmartSurfaceLoader SmartSurfaceLoader
        {
            get { return smartSurfaceLoader; }
        }
        /// <summary>
        /// Randインスタンス取得プロパティ
        /// </summary>
        public Rand Rand { get { return rand; } }
        /// <summary>
        /// LogWriterインスタンスの取得
        /// </summary>
        public TimestampLogWriter LogWriter { get { return logWriter; } }
        /// <summary>
        /// アプリケーションで一意に使用するBGMローダ
        /// </summary>
        public SoundLoader CommonBgmLoader
        {
            get { return commonBgmLoader; }
        }
        /// <summary>
        /// アプリケーション共通で使うSEのローだ
        /// </summary>
        public SoundLoader CommonSeLoader
        {
            get { return commonSeLoader; }
        }

        /// <summary>
        /// アプリケーション共通で使用するフォントローダ
        /// </summary>
        public FontLoader CommonFontLoader
        {
            get { return commonFontLoader; }
        }

        /// <summary>
        /// ユーザ情報
        /// </summary>
        public UserInfo UserInfo
        {
            get { return userInfo; }
        }
        /// <summary>
        /// オプション情報
        /// </summary>
        public OptionInfo OptionInfo
        {
            get { return optionInfo; }
            set { optionInfo = value; }
        }

        /// <summary>
        /// キーコンフィグデータ
        /// </summary>
        public KeyConfigData KeyConfigData
        {
            get { return keyConfigData; }
            set { keyConfigData = value; }
        }

        /// <summary>
        /// オプション画面用のパラメータ
        /// </summary>
        public OptionScenePram OptionSceneParam
        {
            get { return optionSceneParam; }
        }

        /// <summary>
        /// セーブ/ロード画面用のパラメータ
        /// </summary>
        public BookmarkSceneParam BookmarkSceneParam
        {
            get { return bookmarkSceneParam; }
        }

        /// <summary>
        /// テロップ用のシーンパラメータ
        /// </summary>
        public TelopParam TelopParam
        {
            get { return telopParam; }
        }

        /// <summary>
        /// アイキャッチ画面用のパラメータ
        /// </summary>
        public EyeCatcheSceneParam EyeCatcheSceneParam
        {
            get { return eyeCatcheSceneParam; }
        }

        /// <summary>
        /// シナリオシーン用のパラメータ
        /// </summary>
        public ScenarioSceneParam ScenarioSceneParam
        {
            get { return scenarioSceneParam; }
        }

        /// <summary>
        /// 去人パートエンディング用のシーンパラメーター
        /// </summary>
        public KyojinEndingSceneParam KyojinEndingSceneParam
        {
            get { return kyojinEndingSceneParam; }
        }

        /// <summary>
        /// 遷移元がタイトル画面かどうか
        /// </summary>
        public bool FromTitleScene
        {
            get { return fromTitleScene; }
            set { fromTitleScene = value; }
        }

        private bool showYesNo;
        /// <summary>
        /// YesNoダイアログを表示しているか
        /// </summary>
        public bool IsShowYesNo
        {
            get { return showYesNo; }
            set { showYesNo = value; }
        }

        private YesNoScene.CLICK yesnoClickState = YesNoScene.CLICK.NO_CLICK;
        /// <summary>
        /// 共通ダイアログの選択状況
        /// </summary>
        public YesNoScene.CLICK YesnoClickState
        {
            get { return yesnoClickState; }
            set { yesnoClickState = value; }
        }

        #endregion

        #region publicメソッド

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public GameSystem()
        {
            // LogWriterのセットアップ
            logWriter = new TimestampLogWriter();
            logWriter.SetPath(KyojinConst.LOG_DIR);
            CommonLogWriter.LogWriter = logWriter;

            // インプットデバイスの準備
            // input関連
            mouse = new NullableMouseInput(new MouseInputImpl());
            key = new NullableKeyInput(new MousedKey());
            IKeyInput keyOrigin = ((NullableKeyInput)key).GetOriginal();
            MouseInputAdapter.MouseKeyInputMap map = ((MouseAdaptable)keyOrigin).GetAdaptKeyMapping();
            // KEY と MOUSE を合成し、一つのマウスとして使う！
            MultiMouse aKeymouse = new MultiMouse(mouse);
            aKeymouse.AddMouse(
                new MouseInputAdapter(
                    mouse, key, map));
            keymouse = new NullableMouseInput(aKeymouse);

            // ユーザ情報のロード構築
            InitializeUserInfo();

            if (!optionInfo.WindowMode)
            {
                // 画面の初期化
                window.TestVideoMode(KyojinConst.DAFAULT_DISP_WIDTH, KyojinConst.DEFAULT_DISP_HEIGHT, 32);
            }
            else
            {
                // 画面の初期化
                window.TestVideoMode(KyojinConst.DAFAULT_DISP_WIDTH, KyojinConst.DEFAULT_DISP_HEIGHT, 0);
            }
            // ウィンドゥのキャプションを設定
            window.SetCaption(KyojinConst.APP_NAME);
            // FPS設定
            fpsTimer.Fps = DEFAULT_FPS;
            // シーンコントローラーにファクトリーを設定
            sceneController.TaskFactory = new SceneFactory(this);
            // タスクコントローラーにタスクを登録
            taskController.AddTask(sceneController, 1);
            rand.Randomize();

            // BGMは１個
            commonBgmLoader = smartSoundLoader.LoadDefFile("snd/bgm/list.lst", false);
            // システムSEは１個
            commonSeLoader = smartSoundLoader.LoadDefFile("snd/system/sys_se.lst", false);
            // フォントローダ
            commonFontLoader = smartFontLoader.LoadDefFile("data/font.lst", false);

            yesNoTask = new YesNoScene(this);


        }

        /// 有効ボタンを押したときの音を再生する
        public void PlaySEActive()
        {
            commonSeLoader.PlaySE((int)SYS_SE.ENABLE);
        }

        /// 無効ボタンをおしたときの音を再生する
        public void PlaySENegative()
        {
            commonSeLoader.PlaySE((int)SYS_SE.DISABLE);
        }
        /// 指定したシステムSEを再生する
        public void PlaySystemSE(int num)
        {
            commonSeLoader.PlaySE(num);
        }

        /// <summary>
        /// オブジェクトの内容をファイルから読み込み復元する
        /// </summary>
        /// <param name="path">読み込むファイル名</param>
        /// <returns>復元されたオブジェクト</returns>
        public YanesdkResult LoadUserInfo(String filename)
        {
            //読み込んで逆シリアル化する
            UserInfo loadedUserInfo = DescerializeObject(filename) as UserInfo;
            if (null == loadedUserInfo)
            {
                return YanesdkResult.FileReadError;
            }
            this.userInfo = loadedUserInfo;
            return YanesdkResult.NoError;

        }

        /// <summary>
        /// ユーザ情報をセーブ（ファイル書き出し）を行う
        /// </summary>
        /// <returns></returns>
        public YanesdkResult SaveUserInfo()
        {
            return SaveUserInfo(KyojinConst.SAVE_HEADER_FILE);
        }

        /// <summary>
        /// オブジェクトの内容をファイルに保存する
        /// </summary>
        /// <param name="obj">保存するオブジェクト</param>
        /// <param name="path">保存先のファイル名</param>
        public YanesdkResult SaveUserInfo(String fileName)
        {
            // セーブデータ書き出し
            SaveDataContainer saveDataContainer = new SaveDataContainer();
            saveDataContainer.UserInfo = this.userInfo;
            saveDataContainer.OptionInfo = this.optionInfo;
            saveDataContainer.KeyConfigData = this.KeyConfigData;
            if (YanesdkResult.NoError != ScerializeObject(saveDataContainer, fileName))
            {
                return YanesdkResult.FileWriteError;
            }

            return YanesdkResult.NoError;
        }

        /// ダイアログを表示する
        public void ShowDialog(YesNoScene.TYPE type)
        {
            ((YesNoScene)yesNoTask).DialogType = type;
            IsShowYesNo = true;
            YesnoClickState = YesNoScene.CLICK.NO_CLICK;
        }

        /// <summary>
        /// オブジェクトが使用しているリソースを破棄します。
        /// </summary>
        public void Dispose()
        {
            taskController.Dispose();
            smartFontLoader.Dispose();
            smartSoundLoader.Dispose();
            smartTextureLoader.Dispose();
            mouse.Dispose();
            Window.Dispose();
            logWriter.Dispose();
        }

        public int OnMoveCommon()
        {
            // Ctrl + C で終了ダイアログショートカット
            if ((key.IsPress(11) || key.IsPress(12)) && key.IsPress(13))
            {
                SetupFinishDialog();
            }
            return 0;
        }

        private int dialogTaskState = 1;
        public int OnDrawCommon()
        {
            if (IsShowYesNo)
            {
                dialogTaskState = yesNoTask.Task(this);
            }
            return 0;
        }

        /// <summary>
        /// メインループ
        /// </summary>
        public void Run()
        {
            try
            {
                logWriter.Write("RUN START");

                // エントリーポジション
                sceneController.JumpScene(Scenes.Logo);
                //sceneController.JumpScene(Scenes.Startmenu);
                //sceneController.JumpScene(Scenes.Selector);
                //sceneController.JumpScene(Scenes.Scenario);
                //sceneController.JumpScene(Scenes.Story);
                //sceneController.JumpScene(Scenes.Map);
                //sceneController.JumpScene(Scenes.SeveLoad);
                //sceneController.JumpScene(Scenes.Telop);
                //sceneController.JumpScene(Scenes.Ending);
                //sceneController.JumpScene(Scenes.MainPartEnding);
                //sceneController.JumpScene(Scenes.MusicRoom);
                //sceneController.JumpScene(Scenes.Demo);
                //sceneController.JumpScene(Scenes.TrialEnding);

                //userInfo.SetReadFlg((int)KyojinConst.ScenarioID.STORY01, true);
                //userInfo.SetReadFlg((int)KyojinConst.ScenarioID.STORY02_0, true);
                //userInfo.SetReadFlg((int)KyojinConst.ScenarioID.STORY02_1, true);
                //userInfo.SetReadFlg((int)KyojinConst.ScenarioID.STORY02_2, true);
                //userInfo.SetReadFlg((int)KyojinConst.ScenarioID.STORY03, true);
                //userInfo.SetReadFlg((int)KyojinConst.ScenarioID.STORY04, true);
                //userInfo.SetReadFlg((int)KyojinConst.ScenarioID.STORY05, true);
                //userInfo.SetReadFlg((int)KyojinConst.ScenarioID.STORY06, true);
                //userInfo.SetReadFlg((int)KyojinConst.ScenarioID.STORY07, true);
                //SaveUserInfo();


                while (!taskController.End)
                {
                    YanesdkResult gaemState = SDLFrameEx.PollEvent();
                    switch (gaemState)
                    {
                        case YanesdkResult.NoError:
                            break;
                        case YanesdkResult.ShouldBeQuit:
                            // ダイアログ表示
                            SetupFinishDialog();
                            if (finishDialogEvent != null)
                            {
                                finishDialogEvent();
                            }
                            break;
                        default:

                            break;
                    }

                    // 入力の更新
                    //mouse.Update();
                    // タスクの実行
                    taskController.CallTask(this);

                    // 終了ボタンが押された
                    if (IsShowYesNo)
                    {
                        /* ↓のタスクの中で、quite変数を直に操作している */
                        if (dialogTaskState == 0)
                        {
                            IsShowYesNo = false;
                            if (yesNoTask.DialogType == YesNoScene.TYPE.FINISH && YesNoScene.CLICK.YES == yesNoTask.ClickState)
                            {
                                //Log.print("UserInput : Quit!");
                                break;
                            }
                        }
                    }

                    // FPSの調整
                    fpsTimer.WaitFrame();
                }
            }
            catch (Exception e)
            {
                // スタックトレースをログ出力しておく
                logWriter.Write(e.Message);
                logWriter.Write(e.StackTrace);
            }
            logWriter.Write("RUN END");
        }

        # endregion

        #region 非公開メソッド

        /// <summary>
        /// ユーザ情報を初期化します
        /// </summary>
        private void InitializeUserInfo()
        {
            // セーブファイルがあれば読込してみる
            SaveDataContainer tmpSaveDataInfo = DescerializeObject(KyojinConst.SAVE_HEADER_FILE) as SaveDataContainer;
            if (null != tmpSaveDataInfo)
            {
                userInfo = tmpSaveDataInfo.UserInfo;
                optionInfo = tmpSaveDataInfo.OptionInfo;
                keyConfigData = tmpSaveDataInfo.KeyConfigData;
                if (KeyConfigData == null)
                {
                    KeyConfigData = new KeyConfigData();
                }
                if (keyConfigData.DeviceName != null)
                {
                    UpdateJoystickMapping(GetKeyData());
                }
            }
            else
            {
                optionInfo = new OptionInfo();
                userInfo = new UserInfo();
                keyConfigData = new KeyConfigData();
                // ブックマークデータ
                for (int i = 0; i < KyojinConst.MAX_SEVADATA_NUM; ++i)
                {
                    BookmarkData bm = new BookmarkData();
                    bm.ReadOnly = true;
                    if (KyojinConst.AUTO_BOOKMARK_NO == i)
                    {
                        bm.IsAutoBookmark = true;
                    }
                    userInfo.BookmarkData.Add(bm);
                }
            }
        }

        private List<int> GetKeyData()
        {
            List<int> result = new List<int>();
            for (int i = 0; i < 5; i++)
            {
                result.Add(keyConfigData.KeyData[i][0]);
            }
            return result;
        }

        /// 設定したKEY IDで アップデートする
        private void UpdateJoystickMapping(List<int> buttonIDs)
        {
            MousedKey aMouse = (MousedKey)((NullableKeyInput)Key).GetOriginal();
            aMouse.Init();
            aMouse.AddJoyKeyLeft(buttonIDs[0]);
            aMouse.AddJoyKeyRight(buttonIDs[1]);
            aMouse.AddJoyKeyWheelUp(buttonIDs[2]);
            aMouse.AddJoyKeyWheelDown(buttonIDs[3]);
            aMouse.AddJoyKeyExit(buttonIDs[4]);
        }

        /// <summary>
        /// シリアライズする
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="fileName"></param>
        private static YanesdkResult ScerializeObject(Object obj, String fileName)
        {
            System.IO.FileStream fileStream = new System.IO.FileStream(fileName, System.IO.FileMode.Create,
                System.IO.FileAccess.Write);
            try
            {
                BinaryFormatter bf = new BinaryFormatter();
                //シリアル化して書き込む
                bf.Serialize(fileStream, obj);
                return YanesdkResult.NoError;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
                return YanesdkResult.FileWriteError;
            }
            finally
            {
                if (null != fileStream)
                {
                    fileStream.Close();
                }
            }
        }

        public Object DescerializeObject(String filename)
        {
            System.IO.FileStream fs = null;
            try
            {
                fs = new System.IO.FileStream(filename,
                    System.IO.FileMode.Open,
                    System.IO.FileAccess.Read);
                BinaryFormatter f = new BinaryFormatter();
                //読み込んで逆シリアル化する
                return f.Deserialize(fs);
            }
            catch (FileNotFoundException e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
                return null;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
                return null;
            }
            finally
            {
                if (null != fs)
                {
                    fs.Close();
                }
            }
        }

        private static TileTexture dialogTile;	//!< ダイアログの基本タイル
        private static TileTexture cursorTile;
        /// ダイアログで使用するリソースコンテナを生成する
        public DialogBoxResources createDialogResources()
        {
            if (dialogTile == null)
            {
                dialogTile = new TileTexture(SmartTextureLoader.LoadDefFile("img/dialog/dialog.lst", false),
                    TileTexture.TILE_TYPE.SQUARE9);
            }
            if (cursorTile == null)
            {
                cursorTile = new TileTexture(SmartTextureLoader.LoadDefFile("img/dialog/sel_bg.sdf", false),
                    TileTexture.TILE_TYPE.H_LINE3);
            }
            DialogBoxResources dlgRes = new DialogBoxResources();
            dlgRes.ButtonLabelFontLoader = this.commonFontLoader;
            dlgRes.ButtonLabelFontLoaderNo = KyojinConst.DIALOG_TEXT_FONT_NO;
            dlgRes.MessageFont = this.commonFontLoader.GetFont(KyojinConst.DIALOG_TEXT_FONT_NO);
            dlgRes.DialogTile = dialogTile;
            dlgRes.CursorTile = cursorTile;

            return dlgRes;
        }


        /// ゲーム終了確認ダイアログ表示準備を行う
        private void SetupFinishDialog()
        {
            // 他でダイアログを表示しているのであれば何もしない
            if (IsShowYesNo)
            {
                return;
            }
            ShowDialog(YesNoScene.TYPE.FINISH);

            /* 
                ここで終了することになったとき、ScenarioTaskが実行されていた場合、
                オートセーブされないないことになってしまうので、
                ダサいながらも、ここでなんとかセーブ情報を抜き出す 
            */
            //GameTaskBase nowTask = gameSceneController.getNowTask();

            /* 
                現在のタスクがシナリオタスクならばセーブを実行する
                どこかにスタックされているとしても、それはシナリオタスクが
                タスク移行時にデータをストアすべきである
             */
            //if ( KyojinConst.TASK_NAME[KyojinConst.Task.Task_Scenario] == nowTask.getTaskName() ) {
            //   Log.printLook("Quite Event. ScenarioTask save data.");
            //    // セーブ情報蓄積！
            //    (cast(TaskScenario) nowTask).DoAutoSave(this);
            //}
        }




        #endregion

    }
}
