using System;
using System.Collections.Generic;
using System.Text;
using Yamalib.Util;
using Yanesdk.Draw;
using Yanesdk.System;
using Yanesdk.Math;
using Yamalib.Yanesdkext.Draw;
using Yamalib.Draw;
using Yanesdk.Ytl;
using System.Diagnostics;
using Yamalib.Scenario;

namespace Yamalib.Model
{
    public class Character : ICharacterImageResource
    {
        public const String PRESET_LEFT = "left";
        public const String PRESET_CENTER = "center";
        public const String PRESET_RIGHT = "right";

        public enum PresetPosID : int
        {
            LEFT = 1,
            CENTER = 2,
            RIGHT = 3,
            UNSET = -1
        };

        #region 静的フィールド
        static int C_CHARA_BLT_Y = 0;
        #endregion


        #region フィールドメンバ
        /// <summary>
        /// プリセットされて立ち位置の番号
        /// </summary>
        private PresetPosID preset_pos = PresetPosID.UNSET;
        /// <summary>
        /// キャラ移動変数
        /// </summary>
        private readonly RootCounterS charaMove;
        /// <summary>
        /// 立ち位置Ｘ座標
        /// </summary>
        private int posx;
        private int posy;
        /// <summary>
        /// スライドイン、アウト、フラグ
        /// </summary>
        private int slide = 0;
        /// <summary>
        /// キャラエフェクト待機フラグ
        /// </summary>
        private bool wait = false;
        /// <summary>
        /// 消去エフェクト後、キャラクタを自動でクリアするか　
        /// キャラクタが非表示になったあとも、キャラクタを使いたい場合はOffにする
        /// </summary>
        private bool autoClear = true;

        //	    private static int[String] charMap;		//!< 
        /// <summary>
        /// キャラクター番号とキャラクターＩＤ文字列との連想記憶
        /// </summary>
        private static Dictionary<String, int> charMap = new Dictionary<string, int>();
        private static CSVReader lineParser = new CSVReader();	//!< ラインパーサ
        private readonly static SinTable st = SinTable.Instance;

        /// <summary>
        /// キャラクターが画像のローダ
        /// </summary>
        private TextureLoader textureLoader;
        private int charNo;		//!< 実キャラクター番号
        private int charNoOld;	//!< 前に表示していたキャラクター番号
        private String strCharId;	//!< キャラクターのＩＤ文字列
        private String strCharIdOld;	//!< 前に表示していたキャラクタのＩＤ文字列

        private int preX;		//!< 前のキャラクターが表示されていた位置
        private int preY;
        /// <summary>
        /// 描画カラー
        /// </summary>
        private Color4ub m_color = new Color4ub(255, 255, 255, 255);

        private int slideOffsetX;

        /// <summary>
        /// 表示オフセット位置
        /// </summary>
        private int m_offsetx;
        private int m_offsety;

        /// <summary>
        /// 画面の幅
        /// </summary>
        private int m_viewWidth;

        /// <summary>
        /// トランジットエフェクト番号
        /// </summary>
        private int effectNo;
        /// <summary>
        /// トランジット番号置換前用
        /// </summary>
        private int effectNoBack;
        /// <summary>
        ///  キャラエフェクト変数
        /// </summary>
        private RootCounterS effectPhaseCounter;
        /// <summary>
        /// キャラエフェクト変数
        /// </summary>
        //private InteriorCounter charaEffectPre;
        private RootCounterS charaEffectPre;
        /// <summary>
        /// フェードキャラ用のカウンタ
        /// </summary>
        //        private BackCharaAlaphaCounter backCharaAlphaCounter;
        /// <summary>
        /// キャラ置き換えフラグ
        /// </summary>
        private bool replace;

        #endregion

        #region プロパティ

        /// <summary>
        /// キャラクター移動を指定した場合の移動量
        /// </summary>
        public RootCounterS SlideCounter
        {
            get { return charaMove; }
        }

        /// <summary>
        /// キャラクターエフェクトに負荷してスライドさえせる稼働かを
        /// 指定できる
        /// </summary>
        public bool IsSlide
        {
            get { return slide != 0; }
        }

        /// <summary>
        /// スライドを指定したときの移動サイズ
        /// </summary>
        public int SlideOffsetX
        {
            get { return slideOffsetX; }
        }

        /// <summary>
        /// Ｘ座標を指定して表示する場合の位置
        /// </summary>
        public int X
        {
            get { return posx; }
            set { posx = value; }
        }

        public int Y
        {
            get { return posy; }
            set { posy = value; }
        }

        /// <summary>
        /// 未実装
        /// </summary>
        public float Size
        {
            get { return 1.0f; }
            set { throw new Exception("Not Imprimented"); }
        }

        /// <summary>
        /// プリセットされている位置ＩＤ
        /// </summary>
        public PresetPosID PresetPos
        {
            get { return preset_pos; }
            set { preset_pos = value; }
        }

        /// <summary>
        /// エフェクトしているときに、待機するかどうかのフラグ
        /// このフラグをみるかどうかは、使用しているユーザの自由
        /// </summary>
        public bool WaitFlg
        {
            get { return wait; }
            set { wait = value; }
        }

        /// <summary>
        /// 未使用の状態になったときに、自動的に未使用状態にし、空にする。
        /// 非表示状態にしたあとに、また復帰させて使用するときはOFFにする
        /// デフォルトtrue
        /// </summary>
        public bool AutoClearFlg
        {
            get { return autoClear; }
            set { autoClear = value; }
        }

        /// <summary>
        /// キャラクターのオフセット位置
        /// </summary>
        public int CharacterOffsetX
        {
            get { return m_offsetx; }
            set { m_offsetx = value; }
        }

        /// <summary>
        /// キャラクターのオフセット位置
        /// Yで指定した位置＋これで指定した位置
        /// が描画位置になる
        /// </summary>
        public int CharacterOffsetY
        {
            get { return m_offsety; }
            set { m_offsety = value; }
        }

        /// <summary>
        /// 表示画面のサイズを指定する。
        /// この値を元に、プリセット位置を割り出す。
        /// デフォルトは640
        /// </summary>
        public int ViewWidth
        {
            get { return m_viewWidth; }
            set { m_viewWidth = value; }
        }

        /// <summary>
        /// キャラクターの表示カラー
        /// </summary>
        public Color4ub Color
        {
            get { return m_color; }
            set { m_color = value; }
        }

        /// <summary>
        /// 表示中のキャラクター絵を交換する
        /// このフラグをONにすれば、前に表示しているキャラクターは
        /// 指定されたエフェクトでフェードアウトし、現在のキャラクターを
        /// 表示する
        /// </summary>
        public bool ReplaceFlg
        {
            get { return replace; }
            set { replace = value; }
        }

        public bool IsEffecting
        {
            get
            {
                return (!charaMove.IsEnd || !effectPhaseCounter.IsEnd);
            }
        }

        /// <summary>
        /// エフェクト番号
        /// </summary>
        public int EffectNo
        {
            set { effectNo = value; }
            get { return effectNo; }
        }

        /// <summary>
        /// エフェクトの位相
        /// </summary>
        public int EffectPhase
        {
            get { return effectPhaseCounter.Value(); }
            //            Set { effectPhaseCounter = value as RootCounterS; }
        }

        /// <summary>
        /// 使用しているか
        /// </summary>
        public bool IsShowing
        {
            get
            {
                return ((charNo == -1 || strCharId == null) ? false : true);
            }
        }

        #endregion

        /// 文字列と番号のマップ
        public static void SetCharMap(String filename)
        {
            if (null == filename)
            {
                throw new Exception("param is null");
            }
            lineParser.Read(filename);
            int count = 0;
            foreach (List<String> lineData in lineParser.CsvData)
            {
                if (2 > lineData.Count || lineData[0].ToCharArray()[0] == '#')
                {
                    continue;
                }
                charMap[lineData[1]] = count++;
                Console.WriteLine("charMap added: " + lineData[1] + " - " + (count - 1));
            }
        }

        /// <summary>
        /// 文字列からポジションIDに変換する
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static PresetPosID ToPresetPos(String value)
        {
            switch (value.ToLower())
            {
                case PRESET_LEFT:
                    return PresetPosID.LEFT;
                case PRESET_CENTER:
                    return PresetPosID.CENTER;
                case PRESET_RIGHT:
                    return PresetPosID.RIGHT;
                default:
                    return PresetPosID.CENTER;
            }
        }

        /// <summary>
        /// キャラクタマップの取得
        /// </summary>
        /// <returns></returns>
        public static Dictionary<String, int> GetCharacterMap()
        {
            return charMap;
        }

        /// コンストラクタ
        public Character()
        {
            charaMove = new RootCounterS();
            charaMove.Set(0, 0, 1);	// 無効
            effectPhaseCounter = new RootCounterS(0, TransBltter.PAHSE_MAX, 2);
            charaEffectPre = new RootCounterS();
            Reset();
            m_viewWidth = YamalibConst.DEFAULT_SCRENN_WIDTH; // デフォルト
        }

        /// <summary>
        /// 表示テクスチャを返却する
        /// </summary>
        /// <returns></returns>
        public ITexture GetTexture()
        {
            if (null != textureLoader)
            {
                return textureLoader.GetTexture(GetTextureNo());
            }
            return null;
        }

        /// キャラクターテクスチャローダの設定
        public void SetTextureLoader(TextureLoader tl)
        {
            Debug.Assert(tl != null);
            textureLoader = tl;
        }

        /// 非表示エフェクト中か？
        public bool IsEffectOut()
        {
            if (effectPhaseCounter == null)
            {
                return false;
            }
            return effectPhaseCounter.StartValue > effectPhaseCounter.EndValue;
        }

        /// 表示エフェクト中か？
        public bool IsEffectIn()
        {
            if (effectPhaseCounter == null)
            {
                return false;
            }
            return effectPhaseCounter.StartValue < effectPhaseCounter.EndValue;
        }

        /// <summary>
        /// エフェクトを設定する
        /// start < end
        /// であれば、フェードインと見なす。
        /// さもなければフェードアウト
        /// </summary>
        /// <param name="effectNo"></param>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="speed"></param>
        public void SetEffect(int effectNo, int start, int end, int speed)
        {
            this.effectNo = effectNo;
            if (start < end)
            {
                FadeIn(speed, speed);
            }
            else
            {
                FadeOut(effectNo, speed);
            }
        }

        /**
         * キャラクター表示時のエフェクトを設定
         * キャラクター入れ替えのときのエフェクト指定はsetEffectReplaceで
         */
        public void SetEffect(int effectType, int effectSpeed)
        {
            ReplaceFlg = false;
            this.effectNo = effectType;
            this.effectPhaseCounter.Set(0, TransBltter.PAHSE_MAX, effectSpeed);
        }

        public void SetEffectReplace(string newCharaId, int fontEffectType, int frontEfectSpeed, int backEffectType, int backEffectSpeed)
        {
            SetEffect(fontEffectType, frontEfectSpeed);
            this.charaEffectPre.Set(0, TransBltter.PAHSE_MAX, backEffectSpeed);
            this.effectNoBack = backEffectType;
            ReplaceFlg = true;

            // 新しいキャラクターIDをセット
            SetCharId(newCharaId);
        }

        /// フェードイン開始
        public void FadeIn(int speed, int speedPre)
        {
            effectPhaseCounter.Set(0, TransBltter.PAHSE_MAX, speed);
            charaEffectPre.Set(0, TransBltter.PAHSE_MAX, CalcAlphaStep(speed) * speedPre);
        }

        /// フェードアウト開始
        public void FadeOut(int effectType, int speed)
        {
            this.effectNo = effectType;
            effectPhaseCounter.Set(TransBltter.PAHSE_MAX, 0, speed);
            ReplaceFlg = false;
        }

        /// 未使用状態に戻す
        public void Reset()
        {
            effectPhaseCounter.Set(TransBltter.PAHSE_MAX, TransBltter.PAHSE_MAX, 1);
            effectNo = -1;
            charNo = -1;
            preset_pos = PresetPosID.CENTER;
            strCharId = null;
            strCharIdOld = null;
            wait = false;
            replace = false;
            slide = 0;
            posx = 0;
            this.m_color.SetColor(255, 255, 255);
            charaMove.Set(0, 0, 1);
            slideOffsetX = 0;
        }

        /// キャラクタＩＤ文字列を設定する
        public void SetCharId(String str)
        {
            if (null == str) return;
            strCharIdOld = strCharId;
            charNoOld = charNo;
            strCharId = str;

            if (!charMap.ContainsKey(str))
            {
                Console.WriteLine(str + " not find in CharaMap!");
                foreach (KeyValuePair<String, int> keyValue in charMap)
                {
                    Console.WriteLine("chara {0}:{1} )", keyValue.Key, keyValue.Value);
                }
                return;
            }

            charNo = charMap[str];
        }

        /**
         * いまエフェクトを行っているのであれば、それを終了させる
         * また、それがキャラクターアウトならば、自分を、未使用状態にする
         */
        public void EffectFinish()
        {
            int end = charaMove.EndValue;
            charaMove.opAssign(end);
            end = effectPhaseCounter.EndValue;
            effectPhaseCounter.opAssign(end);
            effectNo = -1;

            // char_out 処理なら消す
            if (0 == end)
            {
                Reset();
            }
        }

        /// このキャラクターのＩＤを返却する
        public String GetCharId()
        {
            return strCharId;
        }

        /// キャラクタＩＤ文字列から割り出したテクスチャ番号を返す
        public int GetTextureNo()
        {
            return charNo;
        }

        /// キャラクタＩＤ文字列から割り出した前のテクスチャ番号を返す
        public int GetTextureNoOld()
        {
            return charNoOld;
        }

        /// <summary>
        /// スライドを指定する
        /// </summary>
        /// <param name="slideOffset"></param>
        /// <param name="slideSpeed"></param>
        public void SetSlide(int slideOffset, int slideSpeed)
        {
            slide = 1;
            slideOffsetX = slideOffset;
            charaMove.Set(0, slideOffset, slideSpeed);
        }


        /// <summary>
        /// 描画属性計算
        /// </summary>
        /// <param name="screen">描画するコンテクスト</param>
        /// <returns>処理結果</returns>
        public YanesdkResult OnMove(IScreen screen)
        {
            Debug.Assert(screen != null);
            if (IsShowing)
            {
                Y = C_CHARA_BLT_Y;
                int phase = TransBltter.PAHSE_MAX;

                ITexture t1 = textureLoader.GetTexture(GetTextureNo());
                if (null == t1)
                {
                    //				    Log.printError("Texture load fault!!:%s\n", getCharId());
                    return YanesdkResult.PreconditionError;
                }

                int tx = (int)t1.Width;
                int ty = (int)t1.Height;

                if (EffectNo != -1)
                {
                    effectPhaseCounter.Inc();
                    if (charaEffectPre != null && ReplaceFlg)
                    {
                        ++charaEffectPre;
                    }
                    if (effectPhaseCounter.IsEnd)
                    {
                        wait = false;
                        replace = false;
                        if (0 == (int)effectPhaseCounter && autoClear)
                        {
                            Reset();
                        }
                        /// 消去エフェクト完了ならここで終了
                        if (0 == (int)effectPhaseCounter)
                        {
                            return YanesdkResult.NoError;
                        }
                    }

                    phase = (int)effectPhaseCounter;
                }

                // スライド
                if (slide != 0)
                {
                    if (preset_pos != PresetPosID.UNSET)
                    {
                        X = GetCharaPresetPos(screen, preset_pos, (int)tx);
                    }
                    else
                    {
                        // 位置直接指定
                        Y = posx - tx / 2;
                    }

                    if (!ReplaceFlg)
                    {
                        // 最終描画位置予想
                        preX = X + slideOffsetX;
                        preY = Y;
                    }
                    charaMove.Inc();
                    X += st.Sin((int)charaMove, slideOffsetX);
                }
                else
                {
                    // プリセットポジション
                    if (preset_pos != PresetPosID.UNSET)
                    {
                        X = GetCharaPresetPos(screen, preset_pos, tx);
                    }
                    else
                    {
                        // 位置直接指定
                        X = posx - tx / 2;
                    }
                    if (!ReplaceFlg)
                    {
                        // 最終描画位置予想
                        preX = X + slideOffsetX;
                        preY = Y;
                    }
                }

                if (phase == TransBltter.PAHSE_MAX)
                {
                    preX = X;
                    preY = Y;
                }
                else if (phase == 0)
                {
                    // 初期化しておく
                    Reset();
                }
            }

            return YanesdkResult.NoError;
        }

        /// キャラクターの描画
        public YanesdkResult OnPaint(IScreen screen, bool draw)
        {
            Debug.Assert(screen != null);
            if (null == textureLoader)
            {
                // テクスチャローダが設定されていない
                return YanesdkResult.PreconditionError;
            }

            if (IsShowing)
            {
                if (0 == (int)effectPhaseCounter)
                {
                    return YanesdkResult.NoError;
                }

                ITexture t1 = textureLoader.GetTexture(GetTextureNo());
                if (null == t1)
                {
                    //				    Log.printError("Texture load fault!!:%s\n", getCharId());
                    return YanesdkResult.FileNotFound;
                }

                // Color バックアップ
                Color4ub colorOrg = YanesdkUtil.GetNowScreenColor(screen);
                int phase = (int)effectPhaseCounter;

                if (TransBltter.PAHSE_MAX == (int)effectPhaseCounter)
                {
                    // 定常描画
                    screen.SetColor(m_color.R, m_color.G, m_color.B, m_color.A);
                    screen.Blt(t1, X, Y);
                }
                else if (0 != (int)effectPhaseCounter)
                {
                    if (replace)
                    {
                        // 古い方のテクスチャを表示
                        ITexture oldCharaTexture = textureLoader.GetTexture(GetTextureNoOld());
                        if (null != oldCharaTexture)
                        {
                            screen.SetColor(m_color.R, m_color.G, m_color.B);
                            //TransBltter.Blt(effectNoBack, screen,
                            //    oldCharaTexture, preX + m_offsetx, preY + m_offsety, TransBltter.PAHSE_MAX - charaEffectPre.ValueSined());
                            TransBltter.Blt(effectNoBack, screen,
                                oldCharaTexture, preX + m_offsetx, preY + m_offsety, TransBltter.PAHSE_MAX - phase);
                        }
                    }
                    if (draw)
                    {
                        screen.SetColor(m_color.R, m_color.G, m_color.B, m_color.A);
                        TransBltter.Blt(effectNo, screen, t1, X + m_offsetx, Y + m_offsety, phase);
                    }
                }

                // Color 戻し
                screen.SetColor(colorOrg);
            }

            return YanesdkResult.NoError;
        }


        #region 非公開メソッド


        /// キャラクターのプリセット位置を取得する	
        protected int GetCharaPresetPos(IScreen screen, PresetPosID presetNumber, int textureWidth)
        {
            int halfScreenWidth = screen.Width / 2;
            switch (presetNumber)
            {
                case PresetPosID.LEFT:
                    return (halfScreenWidth - (m_viewWidth / 4)) - (textureWidth / 2);
                case PresetPosID.CENTER:
                    return halfScreenWidth - (textureWidth / 2);
                case PresetPosID.RIGHT:
                    return (halfScreenWidth + (m_viewWidth / 4)) - (textureWidth / 2);
                default:
                    //                    Log.printError("%s#GetCharaPresetPos : undefined Pos :%s", this.toString(), presetNumber);
                    //                    assert(false);
                    return 0;
            }
        }

        /// 指定したステップで、RootCounterを動作させたときに、何回で終端に達するかを返却
        protected static int CalcAlphaStep(int step)
        {
            if (0 == step)
            {
                step = 1;
            }
            int result;
            if (0 > step)
            {
                result = (255 * (step + 1)) + step;
                return result;
            }
            else
            {
                result = 255 / step;
                return (255 % step) == 0 ? result : result + 1;
            }
        }


        #endregion

    }
}
