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

namespace Yamalib.Draw.Effect
{
    public class SlideShadeDraw
    {

        public class DrawObj
        {
            #region 定数値

            /// オフセットのタイプ
            public enum OFFSET_TYPE { LT, CENTER };
            /// 描画のタイプ
            public enum DRAW_TYPE : int { NORMAL = 0, DUAL_LR, DUAL_TB };

            private const int MAX_RESOLUTION = 64;
            //private static int SOFT_SIZE = 2;

            #endregion

            /// 分解能の設定
            public void SetResolution(int res)
            {
                if (res > 0)
                {
                    resolution = res;
                }
            }

            /// テクスチャローダを設定する
            public void SetTextureLoader(TextureLoader tl, int elementNum)
            {
                Debug.Assert(tl != null);
                imgTextures = tl;
                //			Texture =  textureLoader.Get(0);
                SetResolution(elementNum);
                // 食っておく
                for (int i = 0; i < elementNum; ++i)
                {
                    imgTextures.GetTexture(i);
                }
            }

            public bool IsEnable(int[] index, int val)
            {
                foreach (int i in index)
                {
                    if (i == val)
                    {
                        return true;
                    }
                }
                return false;
            }

            /// 終了しているか
            public bool IsEnd()
            {
                return m_index.IsEnd;
            }

            /// スピードの設定
            public void SetSpeed(int speed_)
            {
                m_index.Set(0, resolution - 1, speed_);
                m_alpha.Set(0, 255, speed_);
                m_alphaDiv.Set(0, 255, (int)(255 / (speed_ / (float)resolution)));
            }

            /// 描画位置の設定
            public void SetDrawPos(int x_, int y_, OFFSET_TYPE type)
            {
                m_x = x_;
                m_y = y_;
                m_type = type;
            }

            /// 描画タイプの設定
            public void SetDrawType(DRAW_TYPE type_, int dstPos)
            {
                m_drawType = type_;

                int startPos;
                int endPos;

                if (!m_reverse)
                {
                    // 目標位置に近づく
                    startPos = dstPos;
                    endPos = 0;
                }
                else
                {
                    // 目標位置から遠のく
                    endPos = dstPos;
                    startPos = 0;
                }

                switch (m_drawType)
                {
                    case DRAW_TYPE.DUAL_TB:
                        m_offsetx.Set(0, 0, 0);
                        m_offsety.Set(startPos, endPos, m_index.StepValue);
                        break;

                    case DRAW_TYPE.DUAL_LR:
                        m_offsetx.Set(startPos, endPos, m_index.StepValue);
                        m_offsety.Set(0, 0, 0);
                        break;

                    default:
                        m_offsetx.Set(0, 0, 0);
                        m_offsety.Set(0, 0, 0);
                        break;
                }
            }

            /// 再生方向の設定
            public void SetReverse(bool b)
            {
                if (m_reverse != b)
                {
                    m_index.Reset();
                    m_offsetx.Reset();
                    m_offsety.Reset();
                    m_alpha.Reset();
                }
                m_reverse = b;

            }

            /// 再生方向の取得
            public bool IsReverse()
            {
                return m_reverse;
            }

            /// 動作処理
            public void OnMove(IScreen screen)
            {
                int pre = m_index.Value();
                //			m_alphaDiv.inc();
                m_index.Inc();
                int now = m_index.Value();
                if (now != pre)
                {
                    m_textureSub = m_texture;
                    if (!m_reverse)
                    {
                        m_texture = imgTextures.GetTexture(resolution - now - 1);
                    }
                    else
                    {
                        m_texture = imgTextures.GetTexture(now);
                    }
                    m_alphaDiv.Reset();
                }

                m_offsetx.Inc();
                m_offsety.Inc();
                m_alpha.Inc();
            }

            /// 描画処理
            public void OnDraw(IScreen screen)
            {
                if (m_texture == null)
                {
                    return;
                }
                int x, y;
                switch (m_type)
                {
                    case OFFSET_TYPE.CENTER:
                        int w = (int)m_texture.Width;
                        int h = (int)m_texture.Height;
                        x = m_x - (w / 2);
                        y = m_y - (h / 2);
                        break;

                    default:
                        x = m_x;
                        y = m_y;
                        break;
                }

                // 描画タイプに合わせて描画
                switch (m_drawType)
                {
                    case DRAW_TYPE.DUAL_TB:
                    case DRAW_TYPE.DUAL_LR:
                        if (m_offsetx.Value() == 0 && m_offsety.Value() == 0)
                        {
                            screen.SetColor(255, 255, 255, m_alpha.Value());
                            screen.Blt(m_texture, x, y);
                        }
                        else
                        {
                            int alpha;
                            if (m_reverse)
                            {
                                alpha = 255 - m_alpha.Value();
                            }
                            else
                            {
                                alpha = m_alpha.Value();
                            }
                            screen.SetColor(255, 255, 255, alpha);

                            screen.Blt(m_texture, x + m_offsetx.Value(), y + m_offsety.Value());
                            screen.Blt(m_texture, x - m_offsetx.Value(), y - m_offsety.Value());
                        }
                        break;

                    default:
                        screen.SetColor(255, 255, 255, m_alpha.Value());
                        screen.Blt(m_texture, x, y);
                        //				screen.bltRotate(Texture, X, Y, 0,2,4);
                        break;
                }

            }

            /// 解放
            public void Release()
            {
            }

            /// コンストラクタ
            public DrawObj()
            {
                m_index = new InteriorCounter();
                m_index.Set(0, resolution, resolution - 1);

                m_offsetx = new InteriorCounter();
                m_offsetx.Set(0, 0, 1);
                m_offsety = new InteriorCounter();
                m_offsety.Set(0, 0, 1);

                m_alpha = new InteriorCounter();
                m_alpha.Set(0, 128, 128);

                m_type = OFFSET_TYPE.LT;
                m_drawType = DRAW_TYPE.NORMAL;

                m_alphaDiv = new RootCounter();
            }

            ///// デストラクタ
            //~this() {
            //    Release();
            //}

            #region メンバフィールド

            private int resolution = 96;
            private TextureLoader imgTextures;
            private ITexture m_texture;
            private ITexture m_textureSub;
            private RootCounter m_alphaDiv;
            private bool m_reverse;		//!< 注意！！ ぼかし０からぼかしていくならture
            private InteriorCounter m_index;
            private int m_x;
            private int m_y;
            private InteriorCounter m_offsetx;
            private InteriorCounter m_offsety;
            private InteriorCounter m_alpha;
            private OFFSET_TYPE m_type;
            private DRAW_TYPE m_drawType;

            #endregion
        }

        /// <summary>
        /// 描画情報クラス
        /// </summary>
        public class DrawInfo
        {
            private DrawObj.DRAW_TYPE drawtype;

            public DrawObj.DRAW_TYPE Drawtype
            {
                get { return drawtype; }
                set { drawtype = value; }
            }
            private int moveto;

            public int Moveto
            {
                get { return moveto; }
                set { moveto = value; }
            }
            private long time;

            /// <summary>
            /// 開始ms
            /// </summary>
            public long Time
            {
                get { return time; }
                set { time = value; }
            }
            private long wait;

            /// <summary>
            /// 表示時間
            /// </summary>
            public long Wait
            {
                get { return wait; }
                set { wait = value; }
            }
            private long waitMark;

            /// <summary>
            /// 停止開始ms
            /// </summary>
            public long WaitMark
            {
                get { return waitMark; }
                set { waitMark = value; }
            }
            private bool waitFlg;

            public bool IsWait
            {
                get { return waitFlg; }
                set { waitFlg = value; }
            }
            private bool finish;

            public bool IsFinish
            {
                get { return finish; }
                set { finish = value; }
            }
            private DrawObj drawobj;

            public DrawObj Drawobj
            {
                get { return drawobj; }
                set { drawobj = value; }
            }
        }

        #region 定数値

        private static readonly char[] DELIM = ",".ToCharArray();
        // 共通
        private static readonly string PROP_KEY_OBJECTS = "objects";
        //private static readonly string PROP_KEY_RESOLUTION = "resolution";
        private static readonly string PROP_KEY_FADETIME = "fadetime";
        // 繰り返し項目
        private static readonly string PROP_KEY_FILENAME = ".filename";
        private static readonly string PROP_KEY_DRAWTYPE = ".drawtype";
        private static readonly string PROP_KEY_POSX = ".posx";
        private static readonly string PROP_KEY_POSY = ".posy";
        private static readonly string PROP_KEY_SIZEX = ".sizex";
        private static readonly string PROP_KEY_SIZEY = ".sizey";
        private static readonly string PROP_KEY_TIME = ".time";
        private static readonly string PROP_KEY_MOVETO = ".moveto";
        private static readonly string PROP_KEY_WAIT = ".IsWait";

        #endregion

        #region メンバフィールド

        private readonly FixTimer m_timer;
        private List<DrawInfo> draws = new List<DrawInfo>();
        private bool init = false;

        #endregion

        /// 描画する情報オブジェクトを作成する	
        public static DrawInfo CreateDrawInfo(TextureLoader textureLoader, int imgElementNum, int drawType, int x, int y, int sizex,
               int sizey, int time, int moveto, int wait, int fadeTime)
        {
            //textureLoader.setCacheSize(-1);
            DrawObj dobj = new DrawObj();
            dobj.SetTextureLoader(textureLoader, imgElementNum);
            dobj.SetSpeed(fadeTime);
            dobj.SetDrawPos(x, y, DrawObj.OFFSET_TYPE.CENTER);

            DrawInfo di = new DrawInfo();
            di.Drawobj = dobj;
            di.Time = time;
            di.Wait = wait;
            di.Drawtype = (DrawObj.DRAW_TYPE)drawType;
            di.Moveto = moveto;
            return di;
        }

        /// コンフィグから読み込み
        public static SlideShadeDraw CreateInstance(string configFile, SmartTextureLoader smartTextureLoader)
        {
            SlideShadeDraw instance = new SlideShadeDraw();
            try
            {
                Properties prop = Properties.GetInstance(configFile);
                //auto objSig = std.string.split( prop.getProperty((string) PROP_KEY_OBJECTS), DELIM );
                string[] objSig = prop.GetProperty(PROP_KEY_OBJECTS).Split(DELIM);
                Debug.Assert(objSig != null);

                string filename;
                int drawType;
                int posx;
                int posy;
                int sizex;
                int sizey;
                int time;
                int moveto;
                int wait;
                int fadetime = (int)prop.GetPropertyNum(PROP_KEY_FADETIME, -1);
                foreach (string sig in objSig)
                {
                    filename = prop.GetProperty(sig + PROP_KEY_FILENAME);
                    drawType = (int)prop.GetPropertyNum(sig + PROP_KEY_DRAWTYPE, -1);
                    posx = (int)prop.GetPropertyNum(sig + PROP_KEY_POSX, -1);
                    posy = (int)prop.GetPropertyNum(sig + PROP_KEY_POSY, -1);
                    sizex = (int)prop.GetPropertyNum(sig + PROP_KEY_SIZEX, -1);
                    sizey = (int)prop.GetPropertyNum(sig + PROP_KEY_SIZEY, -1);
                    time = (int)prop.GetPropertyNum(sig + PROP_KEY_TIME, -1);
                    moveto = (int)prop.GetPropertyNum(sig + PROP_KEY_MOVETO, -1);
                    wait = (int)prop.GetPropertyNum(sig + PROP_KEY_WAIT, -1);

                    //Log.printLook("NOW LOAD name=[%s], time=%s moveto=%s, IsWait=%s", filename, time, moveto, IsWait);

                    instance.AddDrawInfo(CreateDrawInfo(smartTextureLoader.LoadDefFile(filename, false), GetElementNum(filename), drawType, posx, posy, sizex, sizey,
                        time, moveto, wait, fadetime));
                }
            }
            catch (Exception e)
            {
                //Log.printFatal("SlideShadeDraw#onLoad : [%s]", e.toString());
                throw e;
            }

            //Log.print("Load FINISH");
            return instance;
        }

        /// <summary>
        /// 定義ファイルから、要素数をカウントして返却する
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        private static int GetElementNum(string filename)
        {
            FilenamelistReader reader = new FilenamelistReader();
            YanesdkResult result = reader.LoadDefFile(filename, 1);
            if (result == YanesdkResult.NoError)
            {
                return reader.Data.Count;
            }
            return 0;
        }

        /// 描画オブジェクトを追加する
        public void AddDrawInfo(DrawInfo drawInfo)
        {
            if (drawInfo != null)
            {
                draws.Add(drawInfo);
            }
        }

        /// 毎回呼び出す
        public int OnMove(IScreen screen)
        {
            m_timer.Update();
            int now = m_timer.Time;

            if (!init)
            {
                // タイマーをリセットする
                init = true;
                m_timer.Reset();
            }

            foreach (DrawInfo di in draws)
            {
                if (now >= di.Time && !di.IsFinish)
                {
                    di.Drawobj.OnMove(screen);
                    if (di.Drawobj.IsEnd())
                    {
                        if (!di.Drawobj.IsReverse())
                        {
                            if (!di.IsWait)
                            {
                                di.IsWait = true;
                                di.WaitMark = now;
                            }
                            // 消えていく
                            if (now >= di.WaitMark + di.Wait)
                            {
                                di.Drawobj.SetReverse(true);
                                di.Drawobj.SetDrawType(di.Drawtype, di.Moveto);
                            }
                        }
                        else
                        {
                            // 消え終了
                            di.IsFinish = true;
                        }
                    }
                }
            }

            return 0;
        }

        /// 描画処理
        public int OnDraw(IScreen screen)
        {
            long now = m_timer.Time;
            foreach (DrawInfo di in draws)
            {
                if (now >= di.Time && !di.IsFinish)
                {
                    di.Drawobj.OnDraw(screen);
                }
            }
            return 0;
        }

        public bool IsEnd()
        {
            if (draws == null)
            {
                return true;
            }
            foreach (DrawInfo di in draws)
            {
                if (!di.IsFinish)
                {
                    return false;
                }
            }
            return true;
        }

        /// 状態をリセットする
        public void Reset()
        {
            Destroy();
            draws = null;
            init = false;
        }

        /// 実データを取得する
        public List<DrawInfo> GetDrawInfo()
        {
            return draws;
        }

        /// 占有しているメモリを解放する
        public void Destroy()
        {
            foreach (DrawInfo di in draws)
            {
                if (di.Drawobj != null)
                {
                    di.Drawobj.Release();
                }
            }
        }

        /// コンストラクタ
        public SlideShadeDraw()
        {
            m_timer = new FixTimer();
        }


        /// <summary>
        /// サーフェイスのコピーを返す
        /// </summary>
        /// <param name="surface_"></param>
        /// <returns></returns>
        private static Surface CopySurface(Surface surface_)
        {
            int width = (int)surface_.Width;
            int height = (int)surface_.Height;

            bool alpha = surface_.Alpha;

            // createDIBして転送したほうがはやいんでないかな...
            Surface surface = new Surface();
            surface.CreateDIB(width, height, alpha);

            surface.Blt(surface_, 0, 0);
            return surface;
        }

    }
}
