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

namespace Yamalib.Draw.Effect
{

    public class FilmNoise
    {
        /// <summary>
        /// FilmNoiseクラスの動作を決めるエッセンス
        /// </summary>
        public class FilmNoiseEssence
        {
            #region フィールドメンバ

            private readonly Rand rand;
            private ITexture m_texture;	//!< ノイズテクスチャ

            public ITexture Texture
            {
                get { return m_texture; }
                set { m_texture = value; }
            }
            private int m_alpha;

            public int Alpha
            {
                get { return m_alpha; }
                set { m_alpha = value; }
            }
            private bool m_reverse;	//!< 折り返し描画を行うか

            public bool Reverse
            {
                get { return m_reverse; }
                set { m_reverse = value; }
            }
            private float m_speed;	// 移動速度

            public float Speed
            {
                get { return m_speed; }
                set { m_speed = value; }
            }
            private int m_startY;	// 開始位置

            public int Start
            {
                get { return m_startY; }
                set { m_startY = value; }
            }
            private int m_endY;	// 終了位置

            public int End
            {
                get { return m_endY; }
                set { m_endY = value; }
            }
            private int m_posRand = 5;	//! 描画位置のズレ

            public int PosRand
            {
                get { return m_posRand; }
                set { m_posRand = value; }
            }

            private float m_y;
            private bool m_revesing;
            private bool m_finish;


            #endregion

            /// <summary>
            /// コンストラクタ
            /// </summary>
            public FilmNoiseEssence()
            {
                rand = new Rand();
                rand.Randomize();
            }

            public bool IsFinish
            {
                get { return m_finish; }
                set { m_finish = value; }
            }

            /// 有効なエッセンスか
            public bool IsValid()
            {
                if (this.Texture == null)
                {
                    return false;
                }
                return true;
            }

            /// 内部変数を初期化し、再動作可能にします
            public void Reset()
            {
                m_y = m_startY;
                m_revesing = false;
                IsFinish = false;
            }

            /// 移動処理
            public void OnMove(IScreen screen)
            {
                if (!IsValid() || m_finish)
                {
                    return;
                }

                if ((Start > End))
                {
                    // 右から左

                    if (m_revesing)
                    {
                        m_y += m_speed;
                        if (m_y > Start)
                        {
                            CheckFlg();
                        }
                    }
                    else
                    {
                        m_y -= m_speed;
                        if (m_y < End)
                        {
                            CheckFlg();
                        }
                    }
                }
                else
                {
                    // 左から右

                    if (m_revesing)
                    {
                        m_y = m_speed;
                        if (m_y < Start)
                        {
                            CheckFlg();
                        }
                    }
                    else
                    {
                        m_y += m_speed;
                        if (m_y > End)
                        {
                            CheckFlg();
                        }
                    }
                }
            }

            /// 描画処理
            public void OnDraw(IScreen screen)
            {
                if (!IsValid() || m_finish)
                {
                    return;
                }

                Color4ub colorOrg = YanesdkUtil.GetNowScreenColor(screen);
                screen.SetColor(colorOrg.R, colorOrg.G, colorOrg.B, this.m_alpha);
                screen.Blt(this.m_texture, (int)m_y + rand.GetRand(this.m_posRand), 0);
                screen.SetColor(colorOrg);
            }

            /// 描画拡大あり
            public void OnDraw(IScreen screen, float rate)
            {
                if (!IsValid() || m_finish)
                {
                    return;
                }

                Color4ub colorOrg = YanesdkUtil.GetNowScreenColor(screen);
                screen.SetColor(colorOrg.R, colorOrg.G, colorOrg.B, this.m_alpha);
                screen.BltRotate(this.m_texture,
                            (int)((m_y + rand.GetRand(this.m_posRand) * rate)), 0,
                            0,
                            rate,
                            0);
                screen.SetColor(colorOrg);
            }

            /// <summary>
            /// フラグ遷移を行います
            /// </summary>
            private void CheckFlg()
            {
                if (m_reverse)
                {
                    if (m_revesing)
                    {
                        m_finish = true;
                    }
                    else
                    {
                        m_revesing = true;
                    }
                }
                else
                {
                    m_finish = true;
                }
            }
        }

        /// <summary>
        /// FilmNoiseクラスの全体の動作を決めるエッセンス
        /// </summary>
        public class FilmNoiseEssenceManager
        {
            #region メンバフィールド

            private readonly Rand rand = new Rand();
            private readonly List<FilmNoiseEssence> m_essencese = new List<FilmNoiseEssence>();
            private TextureLoader m_noiseTextureLoader;
            private int m_noiseTextureNum = 0;
            private int m_showNum = 3;	//!< 一画面に表示する最大ノイズ数
            private int m_createInterval = 100;
            private int m_drawNoiseMax;
            private int m_drawNoiseRand;
            private readonly List<bool> m_unuseFlg = new List<bool>();
            private float m_rate = 1.0f;

            #endregion

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

            /// ノイズテクスチャセットの設定		
            public void SetNoiseTextureLoader(TextureLoader tl, int elementNum)
            {
                this.m_noiseTextureLoader = tl;
                m_noiseTextureNum = elementNum;
            }
            public TextureLoader GetNoiseTextureLoader()
            {
                return this.m_noiseTextureLoader;
            }
            public int GetNoiseTextureNum()
            {
                return m_noiseTextureNum;
            }

            /// ノイズを描画する数
            public void SetDrawNoiseNum(int max_, int rand_)
            {
                this.m_drawNoiseMax = max_;
                this.m_drawNoiseRand = rand_;
            }
            public int GetDrawNoiseMax()
            {
                return this.m_drawNoiseMax;
            }
            public int GetDrawNoiseRand()
            {
                return this.m_drawNoiseRand;
            }

            /// 一画面に表示する最大ラインノイズ数
            public void SetMaxShowNum(int num)
            {
                m_showNum = num;
            }
            public int GetMaxShowNum()
            {
                return this.m_showNum;
            }

            /// ノイズを生成するまでのインターバルを設定する
            public void SetCreateInterval(int interval)
            {
                this.m_createInterval = interval;
            }
            public int GetCreateInterval()
            {
                return this.m_createInterval;
            }

            /// 拡大率の設定
            public void SetRate(float rate)
            {
                this.m_rate = rate;
            }
            public float GetRate()
            {
                return this.m_rate;
            }


            /// すべてのエッセンスを取得
            public List<FilmNoiseEssence> GetEssenceAll()
            {
                return this.m_essencese;
            }

            /// 指定したエッセンスを取得する		
            public FilmNoiseEssence GetEssence(int index)
            {
                if (index >= this.m_essencese.Count)
                {
                    return null;
                }
                return this.m_essencese[index];
            }

            /// 未使用のエッセンスをランダムで取得する
            /// 使用／未使用のマーカーはここで付与するので、他からの参照で使われていても関知しない
            public FilmNoiseEssence GetUnuseEssenceRand()
            {
                List<int> unuseIndex = new List<int>();
                int i = 0;
                foreach (bool b in m_unuseFlg)
                {
                    if (b)
                    {
                        unuseIndex.Add(i);
                    }
                    ++i;
                }

                int index = rand.GetRand(unuseIndex.Count);
                int num = unuseIndex[index];

                //printf(" %d", num);
                return GetEssence(num);
            }

            /// 使用・未使用マーカーを巡回し、終了したものを未使用にマークする
            public void CheckUsedEssence()
            {
                int i = 0;
                foreach (bool b in m_unuseFlg)
                {
                    if (!b)
                    {
                        if (m_essencese[i].IsFinish)
                        {
                            m_unuseFlg[i] = true;
                        }
                    }
                    ++i;
                }
            }

            /// 削除する
            public void Clear()
            {
                m_essencese.Clear();
                m_unuseFlg.Clear();
            }

            /// 追加する
            public void AddEssence(FilmNoiseEssence essence)
            {
                if (essence.IsValid())
                {
                    m_unuseFlg.Add(true);
                    m_essencese.Add(essence);
                }
            }
        }

        #region メンバフィールド

        private readonly Rand rand = new Rand();
        private FilmNoiseEssenceManager m_essenceMgr;	//!< このクラスの動作を決めるエッセンス
        private List<FilmNoiseEssence> m_useEssence;
        private long m_intervalCount;
        private long m_nextCreate;

        #endregion

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public FilmNoise(FilmNoiseEssenceManager essence)
        {
            rand.Randomize();
            this.m_essenceMgr = essence;
            this.m_useEssence = new List<FilmNoiseEssence>();
            // ノイズを生成する時間を決めておく
            m_nextCreate = rand.GetRand(this.m_essenceMgr.GetCreateInterval());
        }

        /// マネージャ取得
        public FilmNoiseEssenceManager GetManager()
        {
            return this.m_essenceMgr;
        }

        /// 移動処理
        public void OnMove(IScreen screen)
        {

            if (m_intervalCount >= m_nextCreate)
            {

                // まずは終了したノイズをクリアする
                int idx = 0;
                List<int> delIndex = new List<int>();
                foreach (FilmNoiseEssence essence in m_useEssence)
                {
                    if (essence.IsFinish)
                    {
                        delIndex.Add(idx);
                    }
                    ++idx;
                }
                for (int i = delIndex.Count - 1; i >= 0; --i)
                {
                    m_useEssence.RemoveAt(i);
                }
                this.m_essenceMgr.CheckUsedEssence();

                // サイズチェック
                if (m_useEssence.Count < this.m_essenceMgr.GetMaxShowNum())
                {
                    m_useEssence.Add(GetEssence());
                }
            }

            foreach (FilmNoiseEssence essence in m_useEssence)
            {
                essence.OnMove(screen);
            }

            ++m_intervalCount;
        }

        /// マネージャからエッセンスの取得
        public FilmNoiseEssence GetEssence()
        {
            FilmNoiseEssence essence = this.m_essenceMgr.GetUnuseEssenceRand();
            essence.Reset();
            m_nextCreate = rand.GetRand(this.m_essenceMgr.GetCreateInterval());
            m_intervalCount = 0;
            return essence;
        }

        public ITexture GetNoiseTextureRand()
        {
            if (m_essenceMgr.GetNoiseTextureLoader() == null)
            {
                return null;
            }
            TextureLoader noiseTextureLoader = m_essenceMgr.GetNoiseTextureLoader();
            return noiseTextureLoader.GetTexture(rand.GetRand(m_essenceMgr.GetNoiseTextureNum()));
        }

        /// 描画処理
        public void OnDraw(IScreen screen)
        {

            Color4ub colorOrg = YanesdkUtil.GetNowScreenColor(screen);
            float rate = m_essenceMgr.GetRate();

            // ノイズ描画
            if (!(m_essenceMgr.GetNoiseTextureLoader() == null))
            {
                int drawNum = m_essenceMgr.GetDrawNoiseRand() == 0 ?
                    m_essenceMgr.GetDrawNoiseMax() :
                     m_essenceMgr.GetDrawNoiseMax() - rand.GetRand(m_essenceMgr.GetDrawNoiseRand());
                int sx = screen.Width;
                int sy = screen.Height;
                screen.SetColor(colorOrg.R, colorOrg.G, colorOrg.B, 128);

                if (rate == 1.0f || rate == float.NaN)
                {
                    for (int i = 0; i < drawNum; ++i)
                    {
                        screen.Blt(GetNoiseTextureRand(), rand.GetRand(sx), rand.GetRand(sy));
                    }
                }
                else
                {
                    for (int i = 0; i < drawNum; ++i)
                    {
                        screen.BltRotate(GetNoiseTextureRand(),
                                    (int)(rand.GetRand(sx) * rate),
                                    (int)(rand.GetRand(sy) * rate),
                                    0,
                                    rate,
                                    0);
                    }
                }

                screen.SetColor(colorOrg);
            }

            foreach (FilmNoiseEssence essence in m_useEssence)
            {
                if (rate == 1.0f || rate == float.NaN)
                {
                    essence.OnDraw(screen);
                }
                else
                {
                    essence.OnDraw(screen, rate);
                }
            }
        }
    }

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

        private static readonly Rand rand = new Rand();
        private static int maxWidthRand = 0;
        private static int maxWidth = 100;
        private static int maxAlpha = 255;
        private static int maxAlphaRand = 0;
        private static int start;
        private static int end;
        private static float maxSpeed = 1.0f;
        private static float maxSpeedRand = 0.0f;
        private static bool reverseDefault = false;
        private static bool reverseDefaultRand = true;

        #endregion


        /// エッセンスの最大幅と、その最大幅のランダム値
        public static void SetMaxWidth(int width, int randWidth)
        {
            maxWidth = width;
            maxWidthRand = randWidth;
        }

        /// エッセンスの最大速度と、その最大速度のランダム値
        public static void SetMaxSpeed(float maxSpeed_, float randSpeed_)
        {
            maxSpeed = maxSpeed_;
            maxSpeedRand = randSpeed_;
        }

        /// 開始位置と終了位置のの範囲
        public static void SetRange(int start_, int end_)
        {
            if (start > end)
            {
                start = end_;
                end = start_;
            }
            else
            {
                start = start_;
                end = end_;
            }
        }

        /// エッセンスの最大アルファと、その最大アルファのランダム値
        public static void SetMaxAlpha(int maxAlpha_, int alphaRand_)
        {
            maxAlpha = maxAlpha_;
            maxAlphaRand = alphaRand_;
        }

        /// エッセンスのデフォルトの折り返し設定と、その折り返しをランダムで決定するか
        public static void SetReverseDefault(bool def, bool reverseRand)
        {
            reverseDefault = def;
            reverseDefaultRand = reverseRand;
        }

        /// エッセンスを作成し返却する
        public static FilmNoise.FilmNoiseEssence Create()
        {
            FilmNoise.FilmNoiseEssence essence = new FilmNoise.FilmNoiseEssence();

            int width = maxWidthRand == 0 ? maxWidth : maxWidth - rand.GetRand(maxWidthRand);
            int alpha = maxAlphaRand == 0 ? maxAlpha : maxAlpha - rand.GetRand(maxAlphaRand);
            float speed = (maxSpeedRand == 0.0f || maxSpeedRand == float.NaN) ? maxSpeed :
                maxSpeed - (rand.GetRand((int)(maxSpeedRand * 256)) / 256.0f);
            bool reverse = reverseDefaultRand ? rand.GetRand(2) == 0 : reverseDefault;

            essence.Start = start + rand.GetRand(end - start);
            essence.End = rand.GetRand(2) == 0 ? essence.Start - width : essence.Start + width;
            essence.Speed = speed;
            essence.Alpha = alpha;
            essence.Reverse = reverse;

            return essence;
        }


    }

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

        private Rand rand;
        private int m_interval;
        private int m_duration;
        private int m_orgDuration;
        private int m_yRand;

        private ITexture m_oldTexture;
        private bool m_noiseDraw;
        private int m_randSize;
        private long m_nextCount;
        private long m_durationCount;
        private long m_intervalCount;

        #endregion

        /// コンストラクタ
        public FilmNoiseBgDraw(int interval, int yRand, int duration)
        {
            rand = new Rand();
            rand.Randomize();
            m_interval = interval;
            m_yRand = yRand;
            m_orgDuration = m_duration = duration;
            m_nextCount = rand.GetRand(interval);
        }

        /// ノイズを今ＯＮにする
        public void NoiseDrawNow(int dy, int duration)
        {
            NoiseDrawOn(dy);
            m_duration = duration;
        }

        /// 今、ずれ描画しているところか？
        public bool IsNoising()
        {
            return this.m_noiseDraw;
        }

        /// 移動処理
        public void OnMove(IScreen screen)
        {

            if (m_noiseDraw)
            {
                ++m_durationCount;
                if (m_durationCount >= m_duration)
                {
                    m_noiseDraw = false;
                    m_durationCount = 0;
                    m_duration = m_orgDuration;
                    m_oldTexture = null;
                }
            }
            else
            {
                if (m_intervalCount >= m_nextCount)
                {
                    NoiseDrawOn(rand.GetRand(m_yRand) - (m_yRand / 2));
                }
                else
                {
                    m_noiseDraw = false;
                }
                ++m_intervalCount;
            }
        }

        /// 描画処理
        public void OnDraw(IScreen screen, ITexture texture, int x, int y)
        {
            Color4ub colorOrg = YanesdkUtil.GetNowScreenColor(screen);

            if (m_noiseDraw)
            {
                if (m_oldTexture == null)
                {
                    screen.Blt(texture, x, y);
                }
                else
                {
                    screen.Blt(m_oldTexture, x, y);
                }
                screen.SetColor(255, 255, 255, colorOrg.A / 2);
                screen.Blt(texture, x, y + m_randSize);
            }
            else
            {
                screen.Blt(texture, x, y);
                m_oldTexture = texture;

            }

            screen.SetColor(colorOrg);
        }

        /// DrawAssistを使用して描画
        public void OnDraw(DrawAssist.Context context,
                IScreen screen, ITexture texture, int x, int y)
        {
            Color4ub colorOrg = YanesdkUtil.GetNowScreenColor(screen);

            if (m_noiseDraw)
            {
                if (m_oldTexture == null)
                {
                    DrawAssist.Blt(screen, texture, x, y, context);
                }
                else
                {
                    DrawAssist.Blt(screen, m_oldTexture, x, y, context);
                }
                screen.SetColor(255, 255, 255, colorOrg.A / 2);
                DrawAssist.Blt(screen, texture, x, y + m_randSize, context);
            }
            else
            {
                DrawAssist.Blt(screen, texture, x, y, context);
                m_oldTexture = texture;
            }

            screen.SetColor(colorOrg);
        }

        /// <summary>
        /// noiseDrawの準備をする dy = ずれ幅
        /// </summary>
        /// <param name="dy"></param>
        private void NoiseDrawOn(int dy)
        {
            m_nextCount = rand.GetRand(m_interval);
            m_randSize = dy;
            m_intervalCount = 0;
            m_noiseDraw = true;
        }
    }
}
