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

namespace Yamalib.Scenario
{
    /// <summary>
    /// サムネールデータクラス
    /// </summary>
    public class ThumbnailInfo
    {

        #region メンバフィールド

        private static readonly Dictionary<string, int> charMap = new Dictionary<string, int>();
        private int m_bgNo;
        private string m_charaCenter;
        private string m_charaLeft;
        private string m_charaRight;
        private int m_bgmNo = -1;
        private int m_bgmLoop = 1;
        private int m_seNo = -1;
        private int m_seLoop;
        private string m_text;
        private int m_textOffset;
        private int m_pageNo;

        #endregion

        public static void SetCharactorImgMap(string filename)
        {
            if (null == filename)
            {
                throw new Exception("param is null");
            }
            CSVReader lineParser = new CSVReader();
            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++;
            }
        }

        public int BgNo
        {
            get { return m_bgNo; }
            set { m_bgNo = value; }
        }
        public string CharaLeft
        {
            get { return m_charaLeft; }
            set { m_charaLeft = value; }
        }
        public int CharaLeftNo
        {
            get
            {
                if (m_charaLeft == null || !charMap.ContainsKey(m_charaLeft))
                {
                    return -1;
                }
                return charMap[m_charaLeft];
            }
        }

        public string CharaCenter
        {
            get { return m_charaCenter; }
            set { m_charaCenter = value; }
        }
        public int CharaCenterNo
        {
            get
            {
                if (m_charaCenter == null || !charMap.ContainsKey(m_charaCenter))
                {
                    return -1;
                }
                return charMap[m_charaCenter];
            }
        }

        public string CharaRight
        {
            get { return m_charaRight; }
            set { m_charaRight = value; }
        }
        public int CharaRightNo
        {
            get
            {
                if (m_charaRight == null || !charMap.ContainsKey(m_charaRight))
                {
                    return -1;
                }
                return charMap[m_charaRight];
            }
        }

        public int BgmNo
        {
            get { return m_bgmNo; }
            set { m_bgmNo = value; }
        }
        public int BgmLoop
        {
            get { return m_bgmLoop; }
            set { m_bgmLoop = value; }
        }
        public int SeNo
        {
            get { return m_seNo; }
            set { m_seNo = value; }
        }
        public int SeLoop
        {
            get { return m_seLoop; }
            set { m_seLoop = value; }
        }
        public string Text
        {
            get { return m_text; }
            set { m_text = value; }
        }
        public int TextOffset
        {
            get { return m_textOffset; }
            set { m_textOffset = value; }
        }
        public int PageNo
        {
            get { return m_pageNo; }
            set { m_pageNo = value; }
        }

    }


    /// <summary>
    /// サムネール情報よりサムネールイメージを作成する
    /// </summary>
    public class ThumbnailCreator
    {
        #region メンバフィールド

        private SurfaceLoader bgLoader;
        private SurfaceLoader charaLoader;
        private ScenarioParserSurface m_scenarioDraw;
        private int m_viewWidth = 640;	//default

        #endregion


        /// <summary>
        /// コンストラクタ
        /// </summary>
        public ThumbnailCreator(SurfaceLoader bgLoader_, SurfaceLoader charaLoader_, ScenarioParserSurface scenarioDraw)
        {
            bgLoader = bgLoader_;
            charaLoader = charaLoader_;
            m_scenarioDraw = scenarioDraw;
        }

        public int ViewWidth
        {
            get { return m_viewWidth; }
            set { m_viewWidth = value; }
        }

        public Surface Create(int width, int height, ThumbnailInfo thumbInfo)
        {
            Debug.Assert(bgLoader != null);
            Debug.Assert(charaLoader != null);
            Debug.Assert(thumbInfo != null);
            Surface surface = new Surface();
            surface.CreateDIB(width, height, true);

            // 背景描画
            //printf("thumb bg no %d\n", thumbInfo.bgNo);
            if (0 <= thumbInfo.BgNo)
            {
                surface.Blt(bgLoader.GetSurface(thumbInfo.BgNo), 0, 0);
            }

            // キャラクター
            int chImgNo = thumbInfo.CharaLeftNo;
            if (-1 != chImgNo)
            {
                //printf("chara left %d\n",thumbInfo.charaLeftNo);
                Surface charaImg = charaLoader.GetSurface(chImgNo);
                surface.Blt(charaImg,
                    GetCharaPresetPos(width, 0, charaImg.Width), 0, 0);
            }

            chImgNo = thumbInfo.CharaCenterNo;
            if (-1 != chImgNo)
            {
                //printf("chara center %d\n",thumbInfo.charaCenterNo);
                Surface charaImg = charaLoader.GetSurface(chImgNo);
                surface.Blt(charaImg,
                    GetCharaPresetPos(width, 1, charaImg.Width), 0, 0);
            }

            chImgNo = thumbInfo.CharaRightNo;
            if (-1 != chImgNo)
            {
                //printf("chara right %d\n",thumbInfo.charaRightNo);
                Surface charaImg = charaLoader.GetSurface(chImgNo);
                surface.Blt(charaImg,
                    GetCharaPresetPos(width, 2, charaImg.Width), 0, 0);
            }

            m_scenarioDraw.GetScenarioDrawContext().TextOffsetPos = thumbInfo.TextOffset;
            m_scenarioDraw.UpdateText();
            m_scenarioDraw.OnPaint(surface, 45, 28);

            return surface;
        }


        /// キャラクターのプリセット位置を取得する	
        private int GetCharaPresetPos(int screenWidth, int presetNumber, int textureWidth)
        {
            int halfScreenWidth = screenWidth / 2;
            switch (presetNumber)
            {
                case 0:
                    return (halfScreenWidth - (ViewWidth / 4)) - (textureWidth / 2);
                case 1:
                    return halfScreenWidth - (textureWidth / 2);
                case 2:
                    return (halfScreenWidth + (ViewWidth / 4)) - (textureWidth / 2);
                default:
                    //Log.printError("ThumbnailCreator#GetCharaPresetPos : undefined pos :%s", presetNumber);
                    Debug.Assert(false);
                    break;
            }
            return 0;
        }
    }


    /// <summary>
    /// サムネール描画クラス
    /// </summary>
    public class ThumbnailDraw
    {
        #region 定数値

        private static readonly Point[] FADE_OUT_POINT = new Point[]{
		    new Point(412,  280),	new Point(600, 266),	new Point(599, 423),	new Point(416,452),
        };

        private static readonly Point[] FADE_IN_POINT = new Point[]{
		    new Point(0,   99),	new Point(62, 101),	new Point(66, 198),	new Point(40,200)
        };

        /// シーセンス図のパーツの描画位置
        private static readonly Point[][] DRAW_POINT = new Point[][]{
            new Point[]{new Point(412,  280),	new Point(600, 266),	new Point(599, 423),	new Point(416,452)},
            new Point[]{new Point(304,  214),	new Point(483, 206),	new Point(485, 351),	new Point(311,370)},
            new Point[]{new Point(219,  168),	new Point(389, 165),	new Point(394, 299),	new Point(228,310)},
            new Point[]{new Point(154,  137),	new Point(314, 137),	new Point(319, 259),	new Point(164,265)},
            new Point[]{new Point(104,  117),	new Point(252, 118),	new Point(256, 233),	new Point(113,235)},
            new Point[]{new Point( 62,  105),	new Point(202, 107),	new Point(203, 210),	new Point( 72,213)},
            new Point[]{new Point( 30,   99),	new Point(162, 101),	new Point(166, 198),	new Point( 40,200)}
        };

        #endregion

        public delegate void FadeFinishEvent();

        #region メンバフィールド

        private static InteriorCounter[][] slideCounters;
        private static InteriorCounter fadeInAlpha;
        private static InteriorCounter fadeOutAlpha;
        private readonly int screenWidth;
        private readonly int screenHeight;
        private readonly List<InteriorCounter> selectPaneCounter = new List<InteriorCounter>();

        // サムネール構築クラス
        private ThumbnailCreator m_thumbnailCreator;
        private List<ThumbnailInfo> m_thumbnailInfos = new List<ThumbnailInfo>();

        // 表示数
        private int maxPain = 7;
        private int infoIndex;

        private int infoIndexLimit = 10;
        //	int infoIndexLimit = int.max;
        // 
        private bool moving = false;
        private int selectedPane = 0;
        private bool selecting = false;
        private bool endSlecting = false;
        private bool fadeInFlg = false;
        private bool fadeOutFlg = false;
        private readonly List<ICounter> m_fadeCounters = new List<ICounter>();

        private readonly List<ITexture> textures = new List<ITexture>();
        private List<ThumbnailInfo> activeInfo = new List<ThumbnailInfo>();
        private ITexture fadeInTexture;
        private ITexture fadeOutTexture;

        private FadeFinishEvent fadeInFinishEvent;
        private FadeFinishEvent fadeOutFinishEvent;
        private FadeFinishEvent selectFinishEvent;
        private FadeFinishEvent finishEventPostPeint;

        #endregion

        #region 公開メンバ

        static ThumbnailDraw()
        {
            int speed = 40;
            List<Point[]> allPoint = new List<Point[]>();
            allPoint.Add(FADE_OUT_POINT);
            foreach (Point[] pts in DRAW_POINT)
            {
                allPoint.Add(pts);
            }

            List<InteriorCounter[]> result = new List<InteriorCounter[]>();
            for (int i = 0; i < allPoint.Count - 1; ++i)
            {
                List<InteriorCounter> counters = new List<InteriorCounter>();
                for (int j = 0; j < allPoint[i].Length; ++j)
                {
                    {
                        InteriorCounter counter = new InteriorCounter();
                        counter.Set((int)(allPoint[i + 1][j].X),
                            (int)(allPoint[i][j].X), speed);
                        counters.Add(counter);
                    }
                    {
                        InteriorCounter counter = new InteriorCounter();
                        counter.Set((int)allPoint[i + 1][j].Y,
                            (int)(allPoint[i][j].Y), speed);
                        counters.Add(counter);
                    }
                }
                result.Add(counters.ToArray());
            }
            slideCounters = result.ToArray();

            fadeInAlpha = new InteriorCounter();
            fadeInAlpha.Set(0, 255, speed);
            fadeOutAlpha = new InteriorCounter();
            fadeOutAlpha.Set(255, 0, speed);
        }

        /// コンストラクタ
        public ThumbnailDraw(int screenWidth_, int screenHeight_)
        {
            // ペインを選択したときに移動変数
            for (int i = 0; i < FADE_OUT_POINT.Length; ++i)
            {
                selectPaneCounter.Add(new InteriorCounter());
                selectPaneCounter.Add(new InteriorCounter());
            }
            screenWidth = screenWidth_;
            screenHeight = screenHeight_;
        }

        public ThumbnailCreator ThumbnailCreator
        {
            get { return m_thumbnailCreator; }
            set { m_thumbnailCreator = value; }
        }
        public List<ThumbnailInfo> ThumbnailInfos
        {
            get { return m_thumbnailInfos; }
            set { m_thumbnailInfos = value; }
        }

        /// 現在表示中のサムネール情報 (frontを先頭に)
        public List<ThumbnailInfo> GetActiveInfo()
        {
            return activeInfo;
        }

        /// 先頭のサムネール番号を設定する
        public void SetThumbnailNo(int value)
        {
            if (value < 0)
            {
                value = 0;
            }
            if (value < m_thumbnailInfos.Count)
            {
                infoIndex = value;
            }
            foreach (ITexture t in textures)
            {
                if (t != null)
                {
                    t.Dispose();
                }
            }

            textures.Clear();
            activeInfo.Clear();
            FillPaneTexture();
        }
        public int GetThumbnailNo()
        {
            return infoIndex;
        }

        /// インデックスの制限値を指定できる (default int.max)
        public void SetThumbnailLimitNo(int value)
        {
            infoIndexLimit = value;
        }
        public int GetThumbnailLimitNo()
        {
            return infoIndexLimit;
        }

        /// フェードインする
        public void FadeIn(int speed, int delay)
        {
            if (selecting || fadeInFlg || fadeOutFlg || moving)
            {
                return;
            }

            m_fadeCounters.Clear();
            for (int i = 0; i < textures.Count; ++i)
            {
                RootCounterS counter = new RootCounterS();
                counter.Set(-((textures.Count - 1 - i) * speed * delay), 255, speed);
                m_fadeCounters.Add(counter);
            }

            fadeInFlg = true;
        }
        /// フェードアウトする
        public YanesdkResult FadeOut(int speed, int delay)
        {
            if (selecting || fadeInFlg || fadeOutFlg || moving)
            {
                // まだ動いているのでフェード・アウトできない
                return YanesdkResult.PreconditionError;
            }
            m_fadeCounters.Clear();
            for (int i = 0; i < textures.Count; ++i)
            {
                RootCounterS counter = new RootCounterS();
                counter.Set(255 + (i * speed * delay), 0, speed);
                m_fadeCounters.Add(counter);
            }
            fadeOutFlg = true;
            return YanesdkResult.NoError;
        }
        /// <summary>
        /// フェードインが終わったときのデリゲート
        /// </summary>
        public FadeFinishEvent FadeInFinishEvent
        {
            get { return fadeInFinishEvent; }
            set { fadeInFinishEvent = value; }
        }

        /// <summary>
        /// フェードアウトが終わったときのデリゲート
        /// </summary>
        public FadeFinishEvent FadeOutFinishEvent
        {
            get { return fadeOutFinishEvent; }
            set { fadeOutFinishEvent = value; }
        }

        public FadeFinishEvent SelectFinishEvent
        {
            get { return selectFinishEvent; }
            set { selectFinishEvent = value; }
        }

        public bool IsFadeIn()
        {
            return fadeInFlg;
        }
        public bool IsFadeOut()
        {
            return fadeOutFlg;
        }

        // 進む
        public void Next()
        {
            if (selecting || fadeInFlg || fadeOutFlg || moving)
            {
                return;
            }
            if (textures == null)
            {
                return;
            }
            if (textures.Count >= 2 && textures[1] == null)
            {
                // 最後に到達しているので無理
                return;
            }

            fadeInTexture = CreateNextTexture();

            fadeOutTexture = textures[0];

            moving = true;
            foreach (InteriorCounter[] painCounters in slideCounters)
            {
                foreach (InteriorCounter counter in painCounters)
                {
                    counter.Reset();
                }
            }
            fadeInAlpha.Reset();
            fadeOutAlpha.Reset();
        }

        // 戻る
        public void Prev()
        {
        }

        public void SelectPane(int paneNo, int speed)
        {
            if (moving)
            {
                return;
            }
            if (0 > paneNo || textures.Count <= paneNo)
            {
                // 引数エラー
                return;
            }

            if (textures[paneNo] != null)
            {
                selectedPane = paneNo;
                ITexture targetTexture = textures[paneNo];
                selectPaneCounter[0].Set((int)DRAW_POINT[paneNo][0].X, 0, speed);
                selectPaneCounter[1].Set((int)DRAW_POINT[paneNo][0].Y, 0, speed);
                selectPaneCounter[2].Set((int)DRAW_POINT[paneNo][1].X, (int)targetTexture.Width, speed);
                selectPaneCounter[3].Set((int)DRAW_POINT[paneNo][1].Y, 0, speed);
                selectPaneCounter[4].Set((int)DRAW_POINT[paneNo][2].X, (int)targetTexture.Width, speed);
                selectPaneCounter[5].Set((int)DRAW_POINT[paneNo][2].Y, (int)targetTexture.Height, speed);
                selectPaneCounter[6].Set((int)DRAW_POINT[paneNo][3].X, 0, speed);
                selectPaneCounter[7].Set((int)DRAW_POINT[paneNo][3].Y, (int)targetTexture.Height, speed);
                selecting = true;
                endSlecting = false;
            }
        }

        /// 選択アクションが完了したかどうか
        public bool IsFinishSelect()
        {
            return endSlecting;
        }

        /// 選択状態をキャンセルする
        public void ResetSelect()
        {
            selecting = false;
            endSlecting = false;
        }

        /// 動作処理
        public void OnMove(IScreen screen)
        {
            if (textures == null)
            {
                FillPaneTexture();
            }

            if (finishEventPostPeint != null)
            {
                finishEventPostPeint();
                finishEventPostPeint = null;
            }



            if (moving)
            {
                bool endFlg = false;
                foreach (InteriorCounter[] painCounters in slideCounters)
                {
                    foreach (InteriorCounter counter in painCounters)
                    {
                        Debug.Assert(counter != null);
                        counter.Inc();
                        endFlg = counter.IsEnd;
                    }
                }
                fadeInAlpha.Inc();
                fadeOutAlpha.Inc();

                // 移動終了
                if (endFlg)
                {
                    moving = false;
                    // 先頭を削除
                    textures.RemoveAt(0);
                    activeInfo.RemoveAt(0);
                    // 後方追加
                    textures.Add(fadeInTexture);
                    activeInfo.Add(m_thumbnailInfos[infoIndex - 1]);
                    fadeOutTexture.Dispose();
                    fadeOutTexture = null;
                }
            }
            else if (selecting)
            {
                foreach (InteriorCounter counter in selectPaneCounter)
                {
                    counter.Inc();
                    if (counter.IsEnd)
                    {
                        endSlecting = counter.IsEnd;
                    }
                }

                if (selectPaneCounter[selectPaneCounter.Count - 1].IsEnd)
                {
                    if (selectFinishEvent != null)
                    {
                        finishEventPostPeint += selectFinishEvent;
                    }
                }
            }
            else if (fadeInFlg || fadeOutFlg)
            {
                bool fadeEnd = true;
                foreach (ICounter counter in m_fadeCounters)
                {
                    counter.Inc();
                    if (!counter.IsEnd)
                    {
                        fadeEnd = false;
                    }
                }
                if (fadeEnd)
                {
                    if (fadeOutFlg && fadeOutFinishEvent != null)
                    {
                        finishEventPostPeint += fadeOutFinishEvent;
                    }
                    if (fadeInFlg && fadeInFinishEvent != null)
                    {
                        finishEventPostPeint += fadeInFinishEvent;
                    }

                    fadeInFlg = fadeOutFlg = false;
                }
            }
        }


        /// 描画処理
        public void OnDraw(IScreen screen)
        {
            if (moving)
            {
                Color4ub colorOrg = YanesdkUtil.GetNowScreenColor(screen);
                // フェードインテクスチャ
                screen.SetColor(255, 255, 255, fadeInAlpha.Value());
                screen.Blt(fadeInTexture, (Rect)null, DRAW_POINT[textures.Count - 1]);

                screen.SetColor(255, 255, 255, 255);
                Point[] points = new Point[4];
                for (int i = textures.Count - 1; i > 0; --i)
                {
                    points[0].X = slideCounters[i][0].Value();
                    points[0].Y = slideCounters[i][1].Value();
                    points[1].X = slideCounters[i][2].Value();
                    points[1].Y = slideCounters[i][3].Value();
                    points[2].X = slideCounters[i][4].Value();
                    points[2].Y = slideCounters[i][5].Value();
                    points[3].X = slideCounters[i][6].Value();
                    points[3].Y = slideCounters[i][7].Value();

                    screen.Blt(textures[i], (Rect)null, points);
                }

                // フェードアウトテクスチャ
                screen.SetColor(255, 255, 255, fadeOutAlpha.Value());
                screen.Blt(fadeOutTexture, (Rect)null, DRAW_POINT[0]);

                screen.SetColor(colorOrg);
            }
            else if (selecting)
            {
                // 選択
                Point[] points = new Point[4];
                for (int i = textures.Count - 1; i >= 0; --i)
                {
                    if (i != selectedPane)
                    {
                        screen.Blt(textures[i], (Rect)null, DRAW_POINT[i]);
                    }
                    else
                    {
                        // 選択したペイン
                        points[0].X = selectPaneCounter[0].Value();
                        points[0].Y = selectPaneCounter[1].Value();
                        points[1].X = selectPaneCounter[2].Value();
                        points[1].Y = selectPaneCounter[3].Value();
                        points[2].X = selectPaneCounter[4].Value();
                        points[2].Y = selectPaneCounter[5].Value();
                        points[3].X = selectPaneCounter[6].Value();
                        points[3].Y = selectPaneCounter[7].Value();

                        screen.Blt(textures[i], (Rect)null, points);
                    }
                }

            }
            else if (fadeInFlg || fadeOutFlg)
            {
                Color4ub colorOrg = YanesdkUtil.GetNowScreenColor(screen);
                for (int i = textures.Count - 1; i >= 0; --i)
                {
                    int alpha = m_fadeCounters[i].Value();
                    if (alpha < 0)
                    {
                        alpha = 0;
                    }
                    else if (alpha > 255)
                    {
                        alpha = 255;
                    }
                    screen.SetColor(255, 255, 255, alpha);
                    screen.Blt(textures[i], (Rect)null, DRAW_POINT[i]);
                }
                screen.SetColor(colorOrg);

            }
            else
            {
                for (int i = textures.Count - 1; i >= 0; --i)
                {
                    screen.Blt(textures[i], (Rect)null, DRAW_POINT[i]);
                }
            }

        }

        #endregion


        #region 非公開メンバ

        void FillPaneTexture()
        {
            while (textures.Count < maxPain)
            {
                textures.Add(CreateNextTexture());
                activeInfo.Add(ThumbnailInfos[infoIndex - 1]);
            }
        }

        ITexture CreateNextTexture()
        {
            if (infoIndex >= ThumbnailInfos.Count || infoIndex >= infoIndexLimit)
            {
                return null;
            }
            Surface surface = ThumbnailCreator.Create(screenWidth, screenHeight, ThumbnailInfos[infoIndex]);
            ITexture texture = YamalibConst.CreateTexture();
            texture.SetSurface(surface);
            ++infoIndex;
            return texture;
        }


        #endregion

    }

}
