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

namespace Yamalib.Draw.Effect
{
    /// <summary>
    /// 落下物（雨や雪）を一つをあらわすクラス
    /// </summary>
    public class DropObject : TaskBase
    {
        /// <summary>
        /// 落下オブジェクトの属性情報
        /// </summary>
        public class DropObjectInfo
        {
            public int x, y, alpha;
            public float depth = 1.0f;		//!< 奥行き

            /// 属性設定
            public void Set(int x, int y, int alpha, float depth)
            {
                this.x = x;
                this.y = y;
                this.alpha = alpha;
                this.depth = depth;
            }
            public DropObjectInfo()
            {
            }

            /// 属性設定コンストラクタ
            public DropObjectInfo(int x, int y, int alpha, float depth)
            {
                Set(x, y, alpha, depth);
            }
        }

        #region メンバフィールド

        private static ObjectPool<DropObjectInfo> sip;		//!< オブジェクト描画値
        private static Surface alphaFilter;
        private static int filter_ox;
        private static int filter_oy;

        private readonly SinTable st = SinTable.Instance;
        private readonly Rand rand = new Rand();
        private bool alive = false;
        private ITexture snow;
        private float x, y;				//!< 描画位置
        private int alpha = 128;				//!< 描画α値
        private float dropspeed = 1.0f;	//!< 落下速度
        private float depth = 1.0f;		//!< 深さ
        private float[] amplitude;	//!< 振幅の配列
        private readonly List<float> count = new List<float>();		//!< 現在の角度
        private float[] anglespeed;	//!< 振動の角速度
        private int weight;			//!< 物体の重さ
        private float x_rand = 1.5f;	//!< 振幅に加算するランダム幅

        private DropObjectManager manager;

        #endregion

        /// <summary>
        /// 静的コンストラクタ
        /// </summary>
        static DropObject()
        {
            sip = new ObjectPool<DropObjectInfo>();
            sip.Max = (500);

            for (int i = 0; i < sip.Max; i++)
            {
                sip.Stack[i] = new DropObjectInfo();
            }
        }

        /// アルファフィルタの設定
        public static void SetAlphaFilter(Surface surface, int ox, int oy)
        {
            alphaFilter = surface;
            filter_ox = ox;
            filter_oy = oy;
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public DropObject()
            : base()
        {
            rand.Randomize();
        }

        /// <summary>
        /// 生きているかどうか
        /// </summary>
        public bool IsAlive
        {
            get { return alive; }
        }

        public override int Task(object o)
        {
            if (!alive)
            {
                return 1;
            }
            IScreen screen = o as IScreen;
            screen.BlendAddColorAlpha();

            // 角属性配列の要素数はすべて同じ長さである
            //assert(count.length == anglespeed.length && count.length == amplitude.length);

            for (int i = 0; i < count.Count; ++i)
            {
                count[i] += anglespeed[i];

                if (count[i] > 512) count[i] = 0.0f;

                float ox = amplitude[i] * 128;
                float rx = 0.0f;
                if (x_rand != 0)
                {
                    rx = rand.GetRand((int)(x_rand * 128)) / 128.0f;
                }
                x += st.Sin((int)count[i], (int)(ox * depth)) / 128.0f;	// 振動
                x += rx - x_rand / 2.0f;		// ランダム性
            }

            y += dropspeed * depth;		// 落下

            if (y > screen.Height)
            {
                // 自殺する
                // まだタスクをけしてはダメ
                Kill();
                return 1;
            }

            screen.SetColor(255, 255, 255,
                (int)(alpha * GetFilteredAlhaRate((int)x, (int)y)));
            screen.BltRotate(snow, (int)x, (int)y, 0, depth, 4);

            return 0;
        }

        /// 雨のスタンダードな設定
        public void SetRain()
        {
            float[] amp = new float[] { 0.0f };
            float[] angle = new float[] { 0.0f };

            SetDropSpeed(10.0f);
            SetDepth((rand.GetRand(10) + 5) / 10.0f);
            SetRadAttribute(angle, amp);
            SetRandX(0);
        }

        /// 雪のスタンダードな設定
        public void SetSnow()
        {
            SetDropSpeed(1.0f);
            SetDepth((rand.GetRand(10) + 5) / 10.0f);
            SetRandX(0.0f);

            float[] amp = new float[] { 0.2f, 0.1f, 0.1f };
            float[] angle = new float[] { 3.5f, 1.0f, 0.5f };
            SetRadAttribute(angle, amp);
        }

        public void Kill()
        {
            if (alive)
            {
                alive = false;
                manager.GetPool().PushForced(this);
            }
        }
        /// 初期位置の設定
        public void InitPos()
        {
            rand.Randomize();
            int x = rand.GetRand(640);
            if (dropspeed > 0.0f)
            {
                SetXY(x, 0);
            }
            else
            {
                SetXY(x, 480);
            }
            alive = true;
        }
        /// 雪テクスチャのセット
        public void SetTexture(ITexture t) { snow = t; }

        /// 落下速度（重さは関係しない絶対値）
        public void SetDropSpeed(float f) { dropspeed = f; }
        /// 描画奥行きとなる拡大レート
        public void SetDepth(float f) { depth = f; }
        public float GetDepth() { return depth; }

        /// 描画位置の設定
        public void SetXY(float x, float y) { this.x = x; this.y = y; }
        public void GetXY(out float x, out float y) { x = this.x; y = this.y; }

        /// 角速度、振幅の設定(複数波形の合成値として表現する)
        public void SetRadAttribute(float[] angleSpeed_, float[] amplitude_)
        {
            anglespeed = angleSpeed_;
            amplitude = amplitude_;

            //  カウンタの初期化
            count.Clear();
            for (int i = 0; i < angleSpeed_.Length; ++i)
            {
                count.Add(rand.GetRand(512));
            }
        }

        /// オブジェクトの重さを設定（未使用）
        public void SetWeight(int n) { weight = n; }
        /// 振幅ランダム幅の設定
        public void SetRandX(float f) { x_rand = f; }

        /// マネージャの参照をセット
        public void SetManager(DropObjectManager v)
        {
            manager = v;
        }

        private static unsafe float GetFilteredAlhaRate(int x, int y)
        {
            if (alphaFilter == null)
            {
                return 1.0f;
            }
            int width = (int)alphaFilter.Width;
            int height = (int)alphaFilter.Height;
            // アルファ有効32bitデータ
            Sdl.SDL.SDL_Color* pixels = (Sdl.SDL.SDL_Color*)alphaFilter.Pixels;
            return pixels[(y * width + x)].unused / 255.0f;
        }
    }


    /// <summary>
    /// 落下を管理するクラス
    /// </summary>
    public class DropObjectManager : IDisposable
    {
        #region メンバフィールド

        private readonly ObjectPool<DropObject> pool;	//!< オブジェクトプール
        private readonly TaskController controller;	//!< タスクコントローラ
        private readonly Rand rand;	// ランダム
        private TextureLoader tl;		// 雪テクスチャのローダ
        private float generate = 0.5345f;	//!< フレーム間に生成する雪の数（等間隔はいやなので適当な端数をいれておく
        private float generatecount = 0.0f;	//!< 生成する雪の数
        private GameTimer timer;
        private int stopTime = -1;
        private bool snow;
        private bool rain;

        #endregion

        // 雪タスクのオブジェクトプール
        public DropObjectManager()
        {
            controller = new TaskController();
            pool = new ObjectPool<DropObject>();
            rand = new Rand();
            tl = new TextureLoader();
            tl.IsDefRelativePath = false;
            tl.LoadDefFile("img/ending/dropobj_img.txt");
        }

        #region IDisposable メンバ

        public void Dispose()
        {
            YamalibUtility.Dispose(tl);
            YamalibUtility.Dispose(pool);
        }

        #endregion

        /// 初期化
        public void Init(int n)
        {
            pool.Max = (800);
            for (int i = 0; i < pool.Max; i++)
            {
                DropObject s = new DropObject();
                if (snow)
                {
                    s.SetTexture(tl.GetTexture(0));
                    s.SetSnow();
                }
                else if (rain)
                {
                    s.SetTexture(tl.GetTexture(1));
                    s.SetRain();
                }
                s.SetXY(rand.GetRand(640), rand.GetRand(480));
                s.SetManager(this);

                pool.Stack[i] = s;

            }

            for (int i = 0; i < n; ++i)
            {
                DropObject s = pool.Pop();
                if (s != null)
                {
                    controller.AddTask(s, 0);
                }
            }
        }

        /// 毎回呼び出すなり
        public void OnDraw(IScreen screen)
        {

            float adGen = generate;
            if (stopTime != -1)
            {
                if (timer.Time >= stopTime)
                {
                    adGen = 0;
                }
                else
                {
                    adGen = adGen - (((float)timer.Time / (float)stopTime) * adGen);
                }
            }

            generatecount += adGen;
            if (generatecount > 1.0f)
            {
                for (int i = 0; i < (int)generatecount; ++i)
                {
                    DropObject s = pool.PopForced();
                    if (s == null)
                    {
                        //Log.printWarn("poolfull!");
                        break;
                    }
                    if (snow)
                    {
                        s.SetSnow();
                        s.InitPos();
                    }
                    else if (rain)
                    {
                        s.SetRain();
                        s.InitPos();
                    }
                    else
                    {
                        // TO DO
                    }

                    controller.AddTask(s, 0);
                }
                // 小数部は生かす
                generatecount = generatecount - (int)generatecount;
            }

            controller.CallTask(screen);
        }

        /// アルファフィルタの設定
        public void SetAlphaFilter(Surface surface, int offsetx, int offsety)
        {
            DropObject.SetAlphaFilter(surface, offsetx, offsety);
        }

        /// これを呼び出しいこう、新規オブジェクトを生成しない
        public void Stop(int n/*=-1*/)
        {
            timer = new GameTimer();
            timer.Reset();
            stopTime = n;
        }

        /// 描画終了したか？
        public bool IsFinish()
        {
            return false;
        }

        /// オブジェクトプールを取得する（デバッグ
        public ObjectPool<DropObject> GetPool() { return pool; }

        /// 雨のお手軽設定
        public void PresetRain()
        {
            rain = true;
            snow = false;
            generate = 5.5345f;
        }

        /// 雪のお手軽設定
        public void PresetSnow()
        {
            snow = true;
            rain = false;
            generate = 0.5345f;
        }
    }

}
