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

namespace Yamalib.Draw.Effect
{
    /// <summary>
    /// タイトルテロップ等での表題表示を行います
    /// </summary>
    public abstract class Headlinear : IDisposable
    {
        #region 定数値

        /// <summary>
        /// スケーリングする値の定義
        /// </summary>
        protected const float RATE_SCALER = 128.0f;

        #endregion

        #region フィールドメンバ

        protected bool init;
        protected bool finish;
        protected bool useSprite;	//!< スプライトで描画を行うか？
        protected bool m_reverse;


        // 表示スタイルの指定
        protected int styleType;

        protected int m_disposeStep;	//!< 処理が終了するまでのステップ

        protected int m_interval;		//!< 次の文字を表示するまでの休止フレーム

        protected int intervalCount;
        protected int drawCount;

        protected int moveX;	//!< 移動量X
        protected int moveY;	//!< 移動量X

        protected int topMargin;
        protected int leftMargin;
        protected int bottomMargin;
        protected int rightMargin;

        protected List<int> vx;	//!< 表示位置の列
        protected List<int> vy;
        protected List<RootCounterS> vAlpha;	//!< テクスチャのα値
        protected List<InteriorCounter> vRate;	//!< テクスチャの拡大レート値 RATE_SCALER でスケーリングしてある
        protected List<InteriorCounter> vMoveX;	//!< 移動X
        protected List<InteriorCounter> vMoveY;	//!< 移動Y
        protected List<InteriorCounter> vRotation;	//!< 回転角
        protected List<bool> roteFinish;	//!< 各テクスチャの移動終了フラグ

        protected List<ITexture> vt;	//!< 表示していくテクスチャ列
        //protected Sprite.SpriteVector sv;	//!< 表示する文字列のスプライト情報

        #endregion

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Headlinear()
        {
            vx = new List<int>();
            vy = new List<int>();
            vAlpha = new List<RootCounterS>();
            vRate = new List<InteriorCounter>();
            vMoveX = new List<InteriorCounter>();
            vMoveY = new List<InteriorCounter>();
            vRotation = new List<InteriorCounter>();
            vt = new List<ITexture>();
            roteFinish = new List<bool>();
        }

        /// <summary>
        /// 表示スタイルを設定する
        /// </summary>
        /// <param name="style"></param>
        public void SetStyle(int style)
        {
            if (styleType != style)
            {
                styleType = style;
                init = false;
                IsFinish = false;
            }
        }

        /// 移動量を設定します
        public void SetMoveXY(int x_, int y_)
        {
            moveX = x_;
            moveY = y_;
        }

        /// 移動量を取得します
        public void GetMoveXY(out int x_, out int y_)
        {
            x_ = moveX;
            y_ = moveY;
        }


        /// <summary>
        /// 文字と文字との インターバルの設定
        /// これで総合的な描画速度が決まる
        /// </summary>
        public int Interval
        {
            get { return m_interval; }
            set { m_interval = value; }
        }

        /// <summary>
        /// 最後尾から描画を始めるか
        /// </summary>
        public bool Reverse
        {
            get { return m_reverse; }
            set { m_reverse = value; }
        }

        /// <summary>
        /// 特殊描画処理のステップ数
        /// </summary>
        public int DisposeStep
        {
            get { return m_disposeStep; }
            set { m_disposeStep = value; }
        }

        /// <summary>
        /// ロードされたテクスチャを追加します
        /// </summary>
        /// <param name="t_"></param>
        public void AddTexture(ITexture t_)
        {
            vt.Add(t_);

            // 属性も増やしとく
            vx.Add(0);
            vy.Add(0);
            vAlpha.Add(new RootCounterS(0, 255, 1));
            vRate.Add(new InteriorCounter());
            vMoveX.Add(new InteriorCounter(0, 0, 1));	// 無効にしとく
            vMoveY.Add(new InteriorCounter(0, 0, 1));	// 無効にしとく
            vRotation.Add(new InteriorCounter());
            roteFinish.Add(false);
        }

        /// 余白を設定します
        public void SetMargin(int top, int left, int right, int bottom)
        {
            topMargin = top;
            leftMargin = left;
            bottomMargin = bottom;
            rightMargin = right;
        }

        /// 全オブジェクトの描画アルファを設定します
        public void SetAlphaCounterAll(int start, int end, int step)
        {
            foreach (RootCounterS c in vAlpha)
            {
                c.Set(start, end, step);
            }
        }

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

        /// テクスチャと属性のクリア
        public void Clear()
        {
            vt.Clear();
            vx.Clear();
            vy.Clear();
            vAlpha.Clear();
            vRate.Clear();
            vMoveX.Clear();
            vMoveY.Clear();
            vRotation.Clear();
            useSprite = false;
            init = false;
            finish = false;
            drawCount = 0;
            moveX = 0;
            moveY = 0;
            topMargin = 0;
            leftMargin = 0;
            bottomMargin = 0;
            rightMargin = 0;
            //sv = null;
            roteFinish = null;
        }

        /// 描画
        public abstract void OnDraw(IScreen screen);

        /// 描画結果をもらう
        public virtual void OnDraw(out Textures tv)
        {
            tv = null;
        }

        /// 連結後の仮想座標位置の取得
        public virtual void GetXY(out int x_, out int y_)
        {
            x_ = y_ = 0;
        }

        #region IDisposable メンバ

        public void Dispose()
        {
        }

        #endregion
    }

    /// <summary>
    /// タイトルテロップ等での表題表示を行います
    /// 不透明度をさげながらなめらかにスライドインしてから定常描画します
    /// </summary>
    public class SlideInHeadlinear : Headlinear
    {
        #region 定数値


        #endregion

        #region フィールドメンバ

        private readonly SinTable sin = SinTable.Instance;	//!< 移動量制御用
        private int x;	//!< 描画位置X
        private int y;	//!< 描画位置Y
        private bool fadeOut;	//!< フェードアウトさせるか

        #endregion

        /// 表示位置の設定
        public void SetXY(int x_, int y_)
        {
            x = x_;
            y = y_;
        }

        /// 処理完了後にフェードアウトさせるか
        public void SetFadeOut(bool b_)
        {
            fadeOut = b_;
        }

        /// 描画位置の取得
        public override void GetXY(out int x_, out int y_)
        {
            x_ = y_ = 0;
            if (finish)
            {
                int i = 0, posx, posy, minX = int.MaxValue, minY = int.MaxValue;
                foreach (ITexture t in vt)
                {
                    posx = vx[i] + moveX;
                    posy = vy[i] + moveY;
                    if (posx < minX)
                    {
                        minX = posx;
                    }
                    if (posy < minY)
                    {
                        minY = posy;
                    }
                    i++;
                }
                x_ = minX;
                y_ = minY;
            }
        }

        /// <summary>
        /// 毎回呼び出すなり
        /// </summary>
        /// <param name="screen"></param>
        public override void OnDraw(IScreen screen)
        {
            // 設定してからにしてくれ
            if (vt.Count == 0) return;

            if (!init)
            {
                OnInit(screen);
                init = true;
            }

            int posx, posy;
            if (!finish)
            {
                intervalCount++;
                if (intervalCount >= m_interval)
                {
                    intervalCount = 0;
                    drawCount++;
                }

                // インクリメント
                int i, j = 0;
                if (m_reverse)
                {
                    for (i = vAlpha.Count - 1; i >= 0; --i)
                    {
                        if (j > drawCount) break;

                        vAlpha[i]++;
                        vRotation[i]++;

                        // フェードアウト処理設定
                        if (fadeOut && vRotation[i].IsEnd && !roteFinish[i])
                        {
                            roteFinish[i] = true;
                            int alphaNow = vAlpha[i].Value();
                            vAlpha[i].Set(alphaNow, 0, 2);
                        }
                        j++;
                    }

                    // 描画
                    for (i = vAlpha.Count - 1; i >= 0; --i)
                    {
                        if (j > drawCount) break;

                        screen.SetColor(255, 255, 255, vAlpha[i].Value());

                        posx = vx[i] + sin.Sin(vRotation[i].Value(), moveX);
                        posy = vy[i] + sin.Sin(vRotation[i].Value(), moveY);

                        screen.Blt(vt[i], posx, posy);
                        j++;
                    }

                }
                else
                {	// !IsReverse

                    // 各カウンタの増分
                    for (i = 0; i < vAlpha.Count; ++i)
                    {
                        if (i > drawCount) break;

                        vAlpha[i]++;
                        vRotation[i]++;

                        // フェードアウト処理設定
                        if (fadeOut && vRotation[i].IsEnd && !roteFinish[i])
                        {
                            roteFinish[i] = true;
                            int alphaNow = vAlpha[i].Value();
                            vAlpha[i].Set(alphaNow, 0, 2);
                        }
                    }

                    // 描画
                    j = 0;
                    foreach (ITexture t in vt)
                    {
                        if (j > drawCount) break;

                        screen.SetColor(255, 255, 255, vAlpha[j].Value());

                        posx = vx[j] + sin.Sin(vRotation[j].Value(), moveX);
                        posy = vy[j] + sin.Sin(vRotation[j].Value(), moveY);

                        screen.Blt(t, posx, posy);
                        ++j;
                    }
                }
            }
            else if (!fadeOut)
            {
                // 定常描画
                int i = 0;
                foreach (ITexture t in vt)
                {
                    posx = vx[i] + moveX;
                    posy = vy[i] + moveY;
                    screen.Blt(t, posx, posy);
                    i++;
                }
            }

            // エフェクトおわったんか？
            for (int i = vAlpha.Count - 1; i >= 0; --i)
            {
                if (!vAlpha[i].IsEnd)
                {
                    break;
                }
                finish = true;
            }

            screen.ResetColor();
        }


        #region 非公開メンバ

        /// <summary>
        /// 初期化処理
        /// </summary>
        /// <param name="screen"></param>
        void OnInit(IScreen screen)
        {
            int size = vt.Count;	// テクスチャの数
            int sunWidth = 0;	// 全テクスチャの連結幅
            int maxHeight = 0;	// 一番高いテクスチャの高さ
            int maxWidth = 0;	// 一番幅が広いテクスチャの幅

            foreach (ITexture t in vt)
            {
                int height = int.MinValue;
                int width = int.MinValue;
                width = (int)t.Width;
                height = (int)t.Height;

                sunWidth += width;

                if (maxWidth < width)
                {
                    maxWidth = width;
                }

                if (maxHeight < height)
                {
                    maxHeight = height;
                }
            }

            int tWidth = 0;
            int tHeight;
            int offsetX = x;
            int offsetY = y;
            int adjustY;

            int i = 0;
            foreach (ITexture t in vt)
            {
                // 一つ前のテクスチャの幅に合わせてずらしていく
                offsetX += tWidth;

                tWidth = (int)t.Width;
                tHeight = (int)t.Height;

                if (tHeight != maxHeight)
                {
                    adjustY = (maxHeight - tHeight) / 2;
                }
                else
                {
                    adjustY = 0;
                }

                vx[i] = offsetX;
                vy[i] = offsetY + adjustY;

                vAlpha[i].Set(0, 255, 3);

                vRotation[i].Set(0, 128, m_disposeStep);
                ++i;
            }
        }

        #endregion
    }

    public class SequentialDrawAlpha : Headlinear
    {

        #region フィールドメンバ

        private int x;
        private int y;
        private int rateStep;
        private float startRate = 0.0f;

        #endregion

        /// <summary>
        /// 表示開始時の拡大レート
        /// </summary>
        public float StartRate
        {
            get { return startRate; }
            set { startRate = value; }
        }

        /// <summary>
        /// 縮小エフェクトフレーム数の設定
        /// </summary>
        /// <param name="step"></param>
        public void SetEffectingFrame(int step)
        {
            if (0 >= step)
            {
                step = 1;
            }
            rateStep = step;
        }

        /// <summary>
        /// 描画位置の設定
        /// </summary>
        /// <param name="x_"></param>
        /// <param name="y_"></param>
        public void SetXY(int x_, int y_)
        {
            x = x_;
            y = y_;
        }

        /// <summary>
        /// 描画位置の取得
        /// </summary>
        /// <param name="x_"></param>
        /// <param name="y_"></param>
        public override void GetXY(out int x_, out int y_)
        {
            x_ = x;
            y_ = y;
        }

        /// <summary>
        /// 描画処理
        /// </summary>
        /// <param name="screen"></param>
        public override void OnDraw(IScreen screen)
        {
            if (!init)
            {
                OnInit(screen);
                init = true;
            }

            if ((drawCount < vt.Count) && (intervalCount++ >= m_interval))
            {
                intervalCount = 0;
                drawCount++;
            }

            int i = 0;
            foreach (ITexture t in vt)
            {

                if (drawCount == i) break;

                if (!roteFinish[i])
                {
                    vRate[i]++;
                    vAlpha[i]++;

                    float fRate = (float)vRate[i].Value() / RATE_SCALER;
                    screen.SetColor(255, 255, 255, vAlpha[i].Value());
                    screen.BltRotate(t, x, y, 0, fRate, 4);	// 描画

                    if (vRate[i].IsEnd && vAlpha[i].IsEnd)
                    {
                        roteFinish[i] = true;
                    }
                }
                ++i;
            }

            i = 0;
            for (i = 0; i < roteFinish.Count; ++i)
            {
                if (!roteFinish[i]) break;
            }

            if (i == roteFinish.Count)
            {
                finish = true;
            }
        }


        #region 非公開メンバ

        /// <summary>
        /// 初期化処理
        /// </summary>
        /// <param name="screen"></param>
        private void OnInit(IScreen screen)
        {
            // テクスチャの数
            int size = vt.Count;

            for (int i = 0; i < size; ++i)
            {
                // α値
                vAlpha[i].Set(200, 0, 5);
                vRate[i].Set((int)(StartRate * RATE_SCALER), (int)(0.8f * RATE_SCALER), rateStep);
            }
        }

        #endregion
    }
}
