using System;
using System.Collections.Generic;
using System.Text;
using Yanesdk.Ytl;
using Yanesdk.Draw;
using Yamalib.Util;
using Yanesdk.Sound;
using Yanesdk.Input;
using System.Collections;
using Yamalib.Input;

namespace Yamalib.Scenario
{

    /// <summary>
    /// シナリオパーサ
    /// </summary>
    public interface IScenarioParser
    {
    }


    /// <summary>
    /// ビューを提供する
    /// </summary>
    public interface IScnarioView
    {
        IScenarioModel Model
        {
            get;
            set;
        }

        /// <summary>
        /// オフセット位置の設定
        /// </summary>
        /// <param name="ox"></param>
        /// <param name="oy"></param>
        void SetViewOffsetXY(int ox, int oy);

        /// <summary>
        /// オフセット位置の取得
        /// </summary>
        /// <param name="ox"></param>
        /// <param name="oy"></param>
        void GetViewOffsetXY(out int ox, out int oy);

        void SetTextOffsetXY(int ox, int oy);
        void GetTextOffsetXY(out int ox, out int oy);

        YanesdkResult OnPaint(IScreen screen);

    }

    /// <summary>
    /// レイヤー
    /// </summary>
    public interface IViewLayler
    {
        int Priority
        {
            get;
            set;
        }

        /// <summary>
        /// レイヤータイプの設定
        /// それぞれだが、背景のレイヤーとかトップのレイヤーだとか
        /// そういう指定に使うといい
        /// </summary>
        int LaylerType
        {
            get;
            set;
        }

        /// <summary>
        /// 動作処理
        /// </summary>
        /// <param name="screen"></param>
        /// <returns></returns>
        YanesdkResult OnMove(IScreen screen);

        /// <summary>
        /// 描画処理
        /// </summary>
        /// <param name="screen"></param>
        /// <returns></returns>
        YanesdkResult OnPaint(IScreen screen);
    }

    /// <summary>
    /// シナリオモデルを提供する
    /// </summary>
    public interface IScenarioModel
    {
        YanesdkResult OnMove(IScreen screen);

        IScenarioState ScenarioState
        {
            get;
            set;
        }

        bool IsReadStop
        {
            get;
        }
        bool IsEffectWait
        {
            get;
        }
        bool IsScenarioFinish
        {
            get;
        }

        bool IsPageEnd
        {
            get;
        }

        int ShowTextCount
        {
            get;
        }

        int PageTextCount
        {
            get;
        }

        ICharacterImageManager CharacterImgMgr
        {
            set;
            get;
        }

        IBgImageManager BgImgMgr
        {
            set;
            get;
        }

        IBgmResourceManager BgmMgr
        {
            set;
            get;
        }

        ISeResourceManager SeMgr
        {
            set;
            get;
        }

        IVoiceResourceManager VoiceMgr
        {
            set;
            get;
        }


        ICommandHandlerFactory CommandFactory
        {
            get;
            set;
        }

        MouseInputHandler MouseInputHandler
        {
            get;
            set;
        }


        int ReadSpeed
        {
            get;
            set;
        }

        List<ILetterDeta> LetterData
        {
            get;
        }

        /// <summary>
        /// これは順序が保障されていること！！
        /// </summary>
        List<IViewLayler> ViewLayler
        {
            get;
            set;
        }

        /// <summary>
        /// 読み続ける
        /// </summary>
        /// <returns></returns>
        YanesdkResult ContinueRead();

        /// <summary>
        /// 読みを中断させる
        /// </summary>
        /// <returns></returns>
        YanesdkResult StopRead();


    }

    /// <summary>
    /// シナリオモデル内で、状態を提供する
    /// </summary>
    public interface IScenarioState
    {
        bool ReadStopFlg
        {
            get;
            set;
        }

        bool PageEndFlg
        {
            get;
            set;
        }

        bool ScenarioEndFlg
        {
            get;
            set;
        }

        bool JumpAnotherSceneFlg
        {
            get;
            set;
        }

        bool CharacterEffectWaitFlg
        {
            get;
            set;
        }

        bool BgEffectWaitFlg
        {
            get;
            set;
        }

        bool InputLockFlg
        {
            get;
            set;
        }

        bool ReadSkipFlg
        {
            get;
            set;
        }

    }

    /// <summary>
    /// イメージ画像を管理するクラス
    /// </summary>
    public interface IImageResourceManager
    {
        /// <summary>
        /// 描画するかどうか
        /// 描画状態は内部で保持する
        /// </summary>
        bool DrawDisable
        {
            get;
            set;
        }
    }

    public interface ICharacterImageManager : IImageResourceManager
    {
        /// <summary>
        /// 指定したnoのキャラクターコンテナを取得する
        /// </summary>
        /// <param name="no"></param>
        /// <returns></returns>
        ICharacterImageResource GetCharacterImage(int no);
        /// <summary>
        /// 指定したキャラクタIDのリソースが存在すればそれを返却する。
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        ICharacterImageResource GetCharacterImage(String id);

        /// <summary>
        /// 新しいキャラクターリソースを取得する
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        ICharacterImageResource GetNewCharacterImage(String id);
    }

    public interface IBgImageManager : IImageResourceManager
    {


        /// <summary>
        /// このリソースは表示中か
        /// </summary>
        bool IsShowing
        {
            get;
        }

        /// <summary>
        /// エフェクト中か
        /// </summary>
        bool IsEffecting
        {
            get;
        }

        void SetEffect(int effectNo, int start, int end, int speed);

        int EffectNo
        {
            get;
        }

        int EffectPhase
        {
            get;
        }

        int X
        {
            set;
            get;
        }
        int Y
        {
            set;
            get;
        }

        float Size
        {
            set;
            get;
        }

        Color4ub Color
        {
            set;
            get;
        }

        /// <summary>
        /// エフェクトアウトしていくイメージ番号
        /// </summary>
        int EffectOutBgImgNo
        {
            get;
        }

        int BgImgNo
        {
            set;
            get;
        }

        ITexture GetTexture();

        ITexture GetEffectOutTexture();

        /// <summary>
        /// リソースの管理値を次に移行させる
        /// </summary>
        /// <param name="screen"></param>
        /// <returns></returns>
        YanesdkResult OnMove(IScreen screen);

    }

    /// <summary>
    /// イメージリソースを表現するクラス
    /// </summary>
    public interface IImageResource
    {
        /// <summary>
        /// このリソースは表示中か
        /// </summary>
        bool IsShowing
        {
            get;
        }

        /// <summary>
        /// エフェクト中か
        /// </summary>
        bool IsEffecting
        {
            get;
        }

        void SetEffect(int effectNo, int start, int end, int speed);

        int EffectNo
        {
            get;
        }

        int EffectPhase
        {
            get;
        }

        int X
        {
            set;
            get;
        }
        int Y
        {
            set;
            get;
        }

        float Size
        {
            set;
            get;
        }

        Color4ub Color
        {
            set;
            get;
        }

        ITexture GetTexture();

        /// <summary>
        /// リソースの管理値を次に移行させる
        /// </summary>
        /// <param name="screen"></param>
        /// <returns></returns>
        YanesdkResult OnMove(IScreen screen);
    }

    /// <summary>
    /// キャラクターに特化した
    /// </summary>
    public interface ICharacterImageResource : IImageResource
    {
    }

    /// <summary>
    /// Bgに特化した
    /// </summary>
    public interface IBgImageResource : IImageResource
    {
    }

    /// <summary>
    /// 一つの描画文字を表すデータクラス
    /// </summary>
    public interface ILetterDeta
    {
        //ITexture Texture
        //{
        //    Get;
        //    Set;
        //}

        /// <summary>
        /// 画像データはべつに何で実装しようと自由
        /// </summary>
        Object ImgObj
        {
            get;
            set;
        }

        float ImgObjWidth
        {
            get;
        }

        float ImgObjHeight
        {
            get;
        }

        float Size
        {
            get;
            set;
        }

        /// <summary>
        /// 回転角
        /// 0 - 512
        /// </summary>
        int Angle
        {
            get;
            set;
        }

        Color4ub Color
        {
            get;
            set;
        }

        List<ILetterDeta> RubiChara
        {
            get;
            set;
        }

        char Letter
        {
            get;
            set;
        }

        Position Pos
        {
            get;
            set;
        }

        ILetterDeta Clone();

    }



    /// <summary>
    /// 音リソースを管理するクラス
    /// </summary>
    public interface ISoundResourceManager
    {
        /// <summary>
        /// サウンドを再生するかどうか
        /// 再生状態については内部で保持する
        /// </summary>
        bool SoundDisable
        {
            get;
            set;
        }

        Sound GetSound(int no);
    }

    /// <summary>
    /// BGMを管理する
    /// BGMは１曲しか再生しないこと
    /// </summary>
    public interface IBgmResourceManager : ISoundResourceManager
    {
        float MasterVolume
        {
            get;
            set;
        }

        bool IsPalying
        {
            get;
        }
        int PlayBgmNo
        {
            get;
        }
        float PlayBgmVolume
        {
            get;
        }
        YanesdkResult PlayBgm(int playNo, float volume);
        YanesdkResult PlayBgmFade(int playNo, float volume, int fadeTime);
        YanesdkResult StopBgm();
        YanesdkResult StopBgmFade(int fadeTime);
    }

    /// <summary>
    /// 効果音を管理する
    /// 同時に複数再生する
    /// </summary>
    public interface ISeResourceManager : ISoundResourceManager
    {
        /// <summary>
        /// マスターボリュームの設定取得
        /// </summary>
        float MasterVolume
        {
            get;
            set;
        }

        /// <summary>
        /// 再生しているSEを取得する
        /// </summary>
        /// <returns>再生しているSEの番号の配列</returns>
        int[] GetPlayingSeNo();

        YanesdkResult StopAll();

        YanesdkResult PlaySe(int playNo, float volume, int loopTime);
        YanesdkResult PlaySeFade(int playNo, float volume, int fadeTime, int loopTime);
        YanesdkResult StopSe(int playNo);
        YanesdkResult StopSeFade(int playNo, int fadeTime);

        /// <summary>
        /// 再生している効果音の再生回数を調査し、
        /// 負荷が高いSEに関してはまびく。
        /// フレーム内に１００回要求されたとしても100個のSEを鳴らすのはおかしい
        /// この機構を実装すること
        /// </summary>
        void Update();
    }

    /// <summary>
    /// ボイス再生を管理
    /// これは、一連処理が可能
    /// たとえば、ボイス１、ボイス２、ボイス３を連続して再生させたい場合、
    /// 一連で指定しておけば、終了後随時次のボイスを再生していく
    /// ただし、同時再生はサポートしない
    /// </summary>
    public interface IVoiceResourceManager : ISoundResourceManager
    {
        /// <summary>
        /// マスターボリュームの設定取得
        /// </summary>
        float MasterVolume
        {
            get;
            set;
        }

        bool IsPalying
        {
            get;
        }
        int PlayVoiceNo
        {
            get;
        }
        YanesdkResult PlayVoice(int[] playNo, float volume);
        YanesdkResult PlayVoiceFade(int[] playNo, float volume, int fadeTime);

        YanesdkResult PlayVoiceFade(int playNo, float volume, int fadeTime);
        YanesdkResult PlayVoice(int playNo, float volume);

        YanesdkResult StopVoice();
        YanesdkResult StopVoiceFade(int fadeTime);

        void Update();
    }

    /*
        /// <summary>
        /// 音リソースを表現するクラス
        /// </summary>
        public interface ISoundResource
        {
            bool IsPalying
            {
                Get;
            }

        }

        /// <summary>
        /// SEに特化した
        /// </summary>
        public interface ISeResource : ISoundResource
        {
            YanesdkResult PlaySe(int volume);
            YanesdkResult PalySeFade(int volume, int fadeTime);
            YanesdkResult StopSe(int volume);
            YanesdkResult StopSeFade(int fadeTime);
        }

        /// <summary>
        /// ボイスに特化した
        /// </summary>
        public interface IVoiceResource : ISoundResource
        {
            YanesdkResult PlayVoice(int volume);
            YanesdkResult PalyVoiceFade(int volume, int fadeTime);
            YanesdkResult StopVoice();
            YanesdkResult StopVoiceFade(int fadeTime);
        }
    */

    public delegate YanesdkResult CommandProcessor(ICommandHandler own, IScenarioModel model);

    public enum CommandState
    {
        UN_EXECUTION,
        EXECUTING,
        DONE
    };

    /// <summary>
    /// スクリプト内で何らかの、コマンド、命令文を保持するデータクラス
    /// </summary>
    public interface ICommandHandler
    {

        YanesdkResult SetCommand(String command);

        CommandState State
        {
            get;
        }

        /// <summary>
        /// 実行ポイントの指定
        /// </summary>
        int ExecPosition
        {
            set;
            get;
        }

        CommandProcessor CommandProsser
        {
            get;
            set;
        }

        /// <summary>
        /// コマンドを実行する
        /// </summary>
        /// <param name="model">モデル</param>
        /// <returns>実行結果を返却する</returns>
        YanesdkResult DoCommand(IScenarioModel model);
    }

    public delegate ICommandHandler CommandHandlerCreator();
    /// <summary>
    /// コマンドより、適合するハンドラーを生成し返却する
    /// </summary>
    public interface ICommandHandlerFactory
    {
        void SetFactories(Dictionary<String, CommandHandlerCreator> factories);
        Dictionary<String, CommandHandlerCreator> GetFactories();
        ICommandHandler Create(String commandId);
    }

    /// <summary>
    /// 何もしないコマンドハンドラ
    /// </summary>
    public class NullCommandHandler : ICommandHandler
    {
        private static NullCommandHandler instance;

        private static YanesdkResult NullCommand(ICommandHandler commandHanler, IScenarioModel model)
        {
            return YanesdkResult.NoError;
        }

        private NullCommandHandler()
        {
        }

        public CommandState State
        {
            get { return CommandState.DONE; }
        }

        public int ExecPosition
        {
            set { }
            get { return 0; }
        }

        public static NullCommandHandler Instance
        {
            get
            {
                if (null == instance)
                {
                    instance = new NullCommandHandler();
                }
                return instance;
            }
        }

        public YanesdkResult SetCommand(String command)
        {
            return YanesdkResult.NoError;
        }

        public CommandProcessor CommandProsser
        {
            get { return NullCommand; }
            set { }
        }

        public YanesdkResult DoCommand(IScenarioModel model)
        {
            return YanesdkResult.NoError;
        }
    }


    public delegate YanesdkResult InputHandler(IScenarioModel model);

    /// <summary>
    /// マウスの状態を表す
    /// </summary>
    public struct MouseState
    {
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="bt">ボタンの種類</param>
        /// <param name="upFlg">
        /// 押し上げかどうか。これが false なら押し下げられている状態
        /// </param>
        public MouseState(MouseInput.Button bt, bool upFlg)
        {
            this.button = bt;
            this.up = upFlg;
        }
        public MouseInput.Button button;
        public bool up;
    }

    /// <summary>
    /// マウス入力をハンドリングする
    /// </summary>
    public class MouseInputHandler
    {
        #region メンバフィールド
        private readonly Dictionary<MouseState, InputHandler> handlers = new Dictionary<MouseState, InputHandler>();
        private IMouseInput mouse;
        #endregion

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="mouse"></param>
        public MouseInputHandler(IMouseInput mouse)
        {
            this.mouse = mouse;
        }

        /// <summary>
        /// ハンドラを追加する
        /// </summary>
        /// <param name="mouseState"></param>
        /// <param name="handler"></param>
        public void SetHandler(MouseState mouseState, InputHandler handler)
        {
            handlers[mouseState] = handler;
        }

        /// <summary>
        /// 登録しているハンドラを取得する
        /// 登録されていなければnullが返却される
        /// </summary>
        /// <param name="mouseState"></param>
        /// <returns></returns>
        public InputHandler GetHandler(MouseState mouseState)
        {
            if (handlers.ContainsKey(mouseState))
            {
                return handlers[mouseState];
            }
            return null;
        }

        /// <summary>
        /// マウスの状態よりハンドラを起動
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public YanesdkResult OnHandle(IScenarioModel model)
        {
            foreach (KeyValuePair<MouseState, InputHandler> keyVal in handlers)
            {
                MouseState mouseState = keyVal.Key;
                if (mouseState.up)
                {
                    if (mouse.IsPush(mouseState.button))
                    {
                        keyVal.Value(model);
                    }
                }
                else
                {
                    if (mouse.IsPress(mouseState.button))
                    {
                        keyVal.Value(model);
                    }
                }
            }

            return YanesdkResult.NoError;
        }
    }

    /// <summary>
    /// マウスの状態を表す
    /// </summary>
    public struct KeyState
    {
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="bt">ボタンの種類</param>
        /// <param name="upFlg">
        /// 押し上げかどうか。これが false なら押し下げられている状態
        /// </param>
        public KeyState(KeyCode[] keyCodes, bool pushFlg)
        {
            pressKeyCodes = new SortedList<KeyCode, Object>();
            foreach (KeyCode keyCode in keyCodes)
            {
                pressKeyCodes.Add(keyCode, null);
            }
            this.push = pushFlg;
        }

        public readonly SortedList<KeyCode, Object> pressKeyCodes;
        public bool push;
    }

    public class KeyInputHandler
    {
        #region メンバフィールド

        private IKeyInput key;
        private readonly Dictionary<KeyState, InputHandler> handlers = new Dictionary<KeyState, InputHandler>();

        #endregion

        public KeyInputHandler(IKeyInput key)
        {
            this.key = key;
        }

        /// <summary>
        /// ハンドラを追加する
        /// </summary>
        /// <param name="mouseState"></param>
        /// <param name="handler"></param>
        public void SetHandler(KeyState keyState, InputHandler handler)
        {
            handlers[keyState] = handler;
        }

        /// <summary>
        /// 登録しているハンドラを取得する
        /// 登録されていなければnullが返却される
        /// </summary>
        /// <param name="mouseState"></param>
        /// <returns></returns>
        public InputHandler GetHandler(KeyState keyState)
        {
            if (handlers.ContainsKey(keyState))
            {
                return handlers[keyState];
            }
            return null;
        }

        /// <summary>
        /// マウスの状態よりハンドラを起動
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public YanesdkResult OnHandle(IScenarioModel model)
        {
            foreach (KeyValuePair<KeyState, InputHandler> keyVal in handlers)
            {
                KeyState keyState = keyVal.Key;
                if (keyState.push)
                {
                    bool success = true;
                    foreach (KeyCode keyCode in keyState.pressKeyCodes.Keys)
                    {
                        if (!key.IsPush((int)keyCode))
                        {
                            success = false;
                            break;
                        }
                    }
                    if (success)
                    {
                        // 他のキーおされてると違う？
                        for (int i = 0; i < key.ButtonNum; i++)
                        {
                            if (key.IsPush(i) && !keyState.pressKeyCodes.ContainsKey((KeyCode)i))
                            {
                                success = false;
                                break;
                            }
                        }
                    }
                    if (success)
                    {
                        keyVal.Value(model);
                    }
                }
                else
                {
                    bool success = true;
                    foreach (KeyCode keyCode in keyState.pressKeyCodes.Keys)
                    {
                        if (!key.IsPress((int)keyCode))
                        {
                            success = false;
                            break;
                        }
                    }
                    if (success)
                    {
                        // 他のキーおされてると違う？
                        for (int i = 0; i < key.ButtonNum; i++)
                        {
                            if (key.IsPush(i) && !keyState.pressKeyCodes.ContainsKey((KeyCode)i))
                            {
                                success = false;
                                break;
                            }
                        }
                    }
                    if (success)
                    {
                        keyVal.Value(model);
                    }
                }
            }

            return YanesdkResult.NoError;
        }
    }


}
