using System;
using System.Collections.Generic;
using System.Text;
using Yanesdk.Math;
using Yanesdk.Timer;
using Yamalib.Math;

namespace Yamalib.Util
{
    abstract public class ICounter
    {
        /// <summary>
        /// カウンター値の設定
        /// </summary>
        /// <param name="start_">開始値</param>
        /// <param name="end_">終了値</param>
        /// <param name="step_">加算値</param>
        public virtual void Set(int start_, int end_, int step_)
        {
            step = step_;
            start = start_;
            end = end_;
            Reset();
        }

        /// <summary>
        /// 状態の初期化
        /// </summary>
        abstract public void Reset();

        public virtual int Value() { return rootCount; }

        abstract public bool IsEnd
        {
            get;
        }

        /// <summary>
        /// 現在の値のsin乗算値の取得
        /// </summary>
        /// <returns></returns>
        public virtual int ValueSined()
        {
            int w = end - start;
            int offset = rootCount - w;
            return start + sin.Sin((int)(((float)w) / offset * 128.0f), offset);
        }

        /// <summary>
        /// 加算処理を行う
        /// </summary>
        abstract public void Inc();

        #region プロパティ
        /// <summary>
        ///　加算値の設定取得
        /// </summary>
        virtual public int StepValue
        {
            get { return step; }
            set { step = value; }
        }

        /// <summary>
        /// 開始値の設定取得
        /// </summary>
        virtual public int StartValue
        {
            get { return start; }
            set { start = value; }
        }

        /// <summary>
        /// 終了値の設定取得
        /// </summary>
        virtual public int EndValue
        {
            get { return end; }
            set { end = value; }
        }
        #endregion

        protected static SinTable sin = SinTable.Instance;

        protected int rootCount;
        protected int start;
        protected int end;
        protected int step;
    }

    /// ゲームで使うカウンタ
    public class RootCounter : ICounter
    {

        public static RootCounter operator ++(RootCounter counter) { counter.Inc(); return counter; }
        //	intとの相互変換
        public static explicit operator int(RootCounter counter) { return counter.Value(); }

        #region プロパティ
        /// <summary>
        /// リバースするか
        /// </summary>
        public bool ReverseFlg
        {
            set { reverse = value; }
            get { return reverse; }
        }

        /// <summary>
        /// Incした結果、周回したか？／IncSした結果、終値になったか？
        /// </summary>
        public bool IsLapAround
        {
            get { return lapAround; }
        }

        /// <summary>
        /// Incした結果、初期値に戻ったか？
        /// </summary>
        public bool IsLapAroundI
        {
            get { return lapAroundI; }
        }

        #endregion


        #region publicメソッド

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public RootCounter()
        {
            Set(0, int.MaxValue, 1);
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="end_">終了値</param>
        public RootCounter(int end_)
        {
            Set(0, end_, 1);
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="start_">開始値</param>
        /// <param name="end_">終了値</param>
        /// <param name="step_">加算地</param>
        public RootCounter(int start_, int end_, int step_)
        {
            Set(start_, end_, step_);
            Reset();
        }

        ////	初期値
        public void SetInit(int n) { init = true; initNum = n; }

        //	カウンタのリセット
        public override void Reset()
        {
            rate = 0;
            lapAround = false;
            lapAroundI = false;
            reversing = false;
            if (!init)
            {
                rootCount = start;
            }
            else
            {
                rootCount = initNum;
            }
        }

        //	カウンタのインクリメント(終端まで達すると、再度、初期値に戻る)
        public override void Inc()
        {
            if (reverse)
            {
                if (!reversing)
                {
                    if (step > 0)
                    {
                        rootCount += step;
                        if (rootCount >= end)
                        {
                            rootCount = end;
                            lapAround = true;
                            lapAroundI = false;
                            reversing = true;	//	ダウンカウントの開始
                        }
                        else
                        {
                            lapAround = false;
                            lapAroundI = false;
                        }
                    }
                    else
                    {
                        rate++;
                        if (rate == -step)
                        {
                            rate = 0;
                            rootCount++;
                            if (rootCount >= end)
                            {
                                rootCount = start;
                                lapAround = true;
                                lapAroundI = false;
                                reversing = true;	//	ダウンカウントの開始
                            }
                            else
                            {
                                lapAround = false;
                                lapAroundI = false;
                            }
                        }
                    }
                }
                else
                {
                    if (step > 0)
                    {
                        rootCount -= step;
                        if (rootCount <= start)
                        {
                            rootCount = start;
                            lapAround = true;
                            lapAroundI = true;
                            reversing = false;	//	アップカウントの開始
                        }
                        else
                        {
                            lapAround = false;
                            lapAroundI = false;
                        }
                    }
                    else
                    {
                        rate++;
                        if (rate == -step)
                        {
                            rate = 0;
                            rootCount--;	//	ダウンカウント
                            if (rootCount <= start)
                            {
                                rootCount = start;
                                lapAround = true;
                                lapAroundI = true;	//	この時に限りtrue
                                reversing = false;	//	アップカウントの開始
                            }
                            else
                            {
                                lapAround = false;
                                lapAround = false;
                            }
                        }
                    }
                }
            }
            else
            {
                //	リバースカウンタでないならば、リバース状態であるかに関わらず前方加算
                if (step > 0)
                {
                    rootCount += step;
                    if (rootCount >= end)
                    {
                        rootCount = start;
                        lapAround = true;
                    }
                    else
                    {
                        lapAround = false;
                    }
                }
                else
                {
                    rate++;
                    if (rate == -step)
                    {
                        rate = 0;
                        rootCount++;
                        if (rootCount >= end)
                        {
                            rootCount = start;
                            lapAround = true;
                        }
                        else
                        {
                            lapAround = false;
                        }
                    }
                }
            }
        }//	加算


        //	カウンタのサチュレーションインクリメント（終端まで達すると、そこで停止する）
        public void incS()
        {
            //	if (m_bReverse){
            //	↑リバースカウンタかどうかにかかわらず、
            //	　　　↓これを見て判断すべき
            if (!reversing)
            {
                if (step > 0)
                {
                    rootCount += step;
                    if (rootCount >= end)
                    {
                        rootCount = end;
                        lapAround = true;
                        //	m_bReversing = true;	//	ダウンカウントの開始
                    }
                    else
                    {
                        lapAround = false;
                    }
                }
                else
                {
                    rate++;
                    if (rate == -step)
                    {
                        rate = 0;
                        rootCount++;
                        if (rootCount >= end)
                        {
                            rootCount = end;
                            lapAround = true;
                            //	m_bReversing = true;	//	ダウンカウントの開始
                        }
                        else
                        {
                            lapAround = false;
                        }
                    }
                }
            }
            else
            {
                if (step > 0)
                {
                    rootCount -= step;
                    if (rootCount <= start)
                    {
                        rootCount = start;
                        lapAround = true;
                        //	m_bReversing = false;	//	アップカウントの開始
                    }
                    else
                    {
                        lapAround = false;
                    }
                }
                else
                {
                    rate++;
                    if (rate == -step)
                    {
                        rate = 0;
                        rootCount--;	//	ダウンカウント
                        if (rootCount <= start)
                        {
                            rootCount = start;
                            lapAround = true;
                            //		m_bReversing = false;	//	アップカウントの開始
                        }
                        else
                        {
                            lapAround = false;
                        }
                    }
                }
            }
            //	}
        }

        public int opAssign(int n) { rootCount = n; return n; }

        /// <summary>
        /// 現在の値の取得
        /// </summary>
        /// <returns></returns>
        # endregion

        #region  メンバフィールド
        protected int rate;	    //	nStep<0のときは、ｎ回のInc()で+1される
        protected bool lapAround;
        protected bool lapAroundI;

        protected bool reverse;		//	リバースカウンタ
        protected bool reversing;		//	リバース中か？
        protected bool init;			//	初期値が設定されているか？
        protected int initNum;		//	設定されている初期値
        #endregion

        public override bool IsEnd
        {
            get { return IsLapAround; }
        }
    }



    //	こちらは、nStart≦nEndでなくて良い
    public class RootCounterS : ICounter
    {

        public static RootCounterS operator ++(RootCounterS counter) { counter.Inc(); return counter; }
        //	intとの相互変換
        public static explicit operator int(RootCounterS counter) { return counter.Value(); }

        //	カウンタのリセット
        public override void Reset() { rootCount = start; rate = 0; }

        //	property..
        public override bool IsEnd
        {
            get
            {
                return (rootCount == end);
            }
        }

        public RootCounterS()
        {
            Set(0, int.MaxValue, 1);
            Reset();
        }

        public RootCounterS(int end_)
        {
            Set(0, end_, 1);
            Reset();
        }

        public RootCounterS(int start_, int end_, int step_)
        {
            Set(start_, end_, step_);
            Reset();
        }

        public int opAssign(int n) { rootCount = n; return n; }

        public override int ValueSined()
        {
            int w = end - start;
            int offset = rootCount - start;
            float f = (float)offset / (float)w;
            offset = sin.Sin((int)(f * 128.0f), offset);
            return start + sin.Sin((int)(f * 128.0f), offset);
        }

        //	カウンタのインクリメント(終端まで達すると、そこで停止する)
        //	加算（＝End方向へインクリメント）／減算（＝Start方向へのインクリメント）
        public void Inc(bool add)
        {
            bool bInc = (bool)((start > end) ^ add); // 逆方向カウンタ？
            if (bInc)
            {
                //	インクリメント
                if (step > 0)
                {
                    //	整数インクリメント
                    rootCount += step;
                }
                else
                {
                    //	分数インクリメント
                    rate++; if (rate >= (-step)) { rate = 0; rootCount++; }
                }
                //	サチュレートしたのか？
                int max = start < end ? end : start;
                if (rootCount > max) rootCount = max;
            }
            else
            {
                //	デクリメント
                if (step > 0)
                {
                    //	整数デクリメント
                    rootCount -= step;
                }
                else
                {
                    //	分数デクリメント
                    rate--; if (rate <= (step)) { rate = 0; rootCount--; }
                    //	⇒　Rate++でないことに注意。
                    //	++のあと--して、数の整合性がとれなくてはならない
                    //	かつ、nStep<0のとき最初の１回目の--でRootCounterが
                    //	1減ってはいけない。よってこういう実装になる
                }
                //	サチュレートしたのか？
                int min = start < end ? start : end;
                if (rootCount < min) rootCount = min;
            }
        }

        // intarface 互換
        public override void Inc()
        {
            Inc(true);
        }

        protected int rate;	//	nStep<0のときは、ｎ回のInc()で+1される
    };


    /// 内分カウンタ
    public class InteriorCounter : ICounter
    {

        public static InteriorCounter operator ++(InteriorCounter counter) { counter.Inc(); return counter; }
        public static InteriorCounter operator --(InteriorCounter counter) { counter.Dec(); return counter; }
        //	intとの相互変換
        public static explicit operator int(InteriorCounter counter) { return counter.Value(); }

        public InteriorCounter()
        {
        }

        public InteriorCounter(int start_, int end_, int frames_)
        {
            Set(start_, end_, frames_);
        }

        /// 加算
        public override void Inc()
        {

            // errorやん...
            if (frames == 0) return;

            if (framesNow >= frames)
            {
                rootCount = end;
                return;
            }
            framesNow++;
            //	内分処理
            rootCount = start + framesNow * (end - start) / frames;
        }

        /// 減算
        public void Dec()
        {

            // errorやん...
            if (frames == 0) return;

            //	カウンタは初期値か？
            if (framesNow == 0) return;
            framesNow--;
            //	内分処理
            rootCount = start + framesNow * (end - start) / frames;
        }

        /// 設定
        public override void Set(int start_, int end_, int frames_)
        {
            start = start_;
            end = end_;
            frames = frames_;
            framesNow = 0;
            StepValue = frames_;
        }

        /// 現在の値の設定
        public void Set(int now_) { rootCount = now_; }

        /// 現在のフレームの設定
        public void SetFlame(int now_)
        {
            if (frames >= rootCount)
            {
                framesNow = now_;
            }
        }

        public override int ValueSined()
        {
            float temp1 = ((float)(rootCount - start));
            if (temp1 == 0)
            {
                return 0;
            }

            float nowRad = temp1 / ((float)(end - start));
            return sin.Sin((int)(nowRad * 128.0f), rootCount);

            //    float a = ((float)frames) / rootCount * 128.0f;
            //float b = rootCount * 128.0f;
            //return sin.Sin((int)(((float)frames) / rootCount * 128.0f), rootCount);
        }

        /// 終了したか？
        public override bool IsEnd
        {
            get
            {
                return (bool)(rootCount == end);
            }
        }

        // interface互換
        int getStep() { return frames; }
        public override void Reset()
        {
            Set(start, end, frames);
        }

        private int frames;		//	フレーム分割数（終了値になるまで何回Incをすればいいのか）
        private int framesNow;	//	現在、何フレーム目か？
    };

    /// <summary>
    /// FSPに依存しないタイマーカウンター
    /// </summary>
    public class TimerCounter : ICounter
    {
        private int time;
        private FixTimer timer;
        private bool reverse;
        private bool initFlg = false;
        private bool lapAroundI;
        private bool reversing;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="time"></param>
        public TimerCounter(int start, int end, int time)
        {
            StartValue = start;
            EndValue = end;
            this.timer = new FixTimer();
            this.time = time;
        }

        /// <summary>
        /// リバースカウントかどうか
        /// </summary>
        public bool Reverse
        {
            get { return reverse; }
            set { reverse = value; }
        }

        /// <summary>
        /// Incした結果、初期値に戻ったか？
        /// </summary>
        public bool IsLapAroundI
        {
            get { return lapAroundI; }
        }

        /// <summary>
        /// 終値に達したか
        /// </summary>
        /// <returns></returns>
        public override bool IsEnd
        {
            get
            {
                return time <= timer.Time;
            }
        }

        /// <summary>
        /// 取得
        /// </summary>
        /// <returns></returns>
        public override int Value()
        {
            if (IsEnd)
            {
                return EndValue;
            }

            if (StartValue < EndValue)
            {
                return (int)(StartValue + (EndValue - StartValue) * (timer.Time / (float)time));
            }
            return (int)(StartValue - (StartValue - EndValue) * (timer.Time / (float)time)); ;

        }

        /// <summary>
        /// カウント開始
        /// </summary>
        public override void Reset()
        {
            timer.Reset();
        }

        /// <summary>
        /// 現在の時間を元に、値を増分する
        /// </summary>
        public override void Inc()
        {
            if (!initFlg)
            {
                timer.Reset();
                initFlg = true;
            }
            timer.Update();
            if (IsEnd)
            {
                if (reverse)
                {
                    int tmp = StartValue;
                    StartValue = EndValue;
                    EndValue = tmp;
                    timer.Reset();
                    lapAroundI = reversing;
                }
                reversing = !reversing;
            }
        }
    }

    /// <summary>
    /// ユーザ定義の自由曲線カウンタ
    /// </summary>
    public class FreeLineCounter : ICounter
    {

        public override void Set(int start_, int end_, int step_)
        {
            counter.Set(start_, end_, step_);
            createPoints();
        }

        public override int StartValue
        {
            get
            {
                return counter.StartValue;
            }
            set
            {
                counter.StartValue = value;
            }
        }

        public override int StepValue
        {
            get
            {
                return counter.StepValue;
            }
            set
            {
                counter.StepValue = value;
            }
        }

        public override int EndValue
        {
            get
            {
                return counter.EndValue;
            }
            set
            {
                counter.EndValue = value;
            }
        }

        public override bool IsEnd
        {
            get
            {
                return counter.IsEnd;
            }
        }

        public override void Reset()
        {
            counter.Reset();
        }

        public override int Value()
        {
            int index = counter.Value();
            int st = counter.StartValue;
            int ed = counter.EndValue;
            int offset = st > ed ? ed : st;

            index -= offset;
            if (index >= ptSpline.Count)
            {
                index = ptSpline.Count - 1;
            }
            return offset + getRound(ptSpline[index].Y);
        }

        public override int ValueSined()
        {
            return 0;
        }

        public override void Inc()
        {
            counter.Inc();
        }

        ///
        public void setSamplePt(float[] x_, float[] y_)
        {
            sx.Clear();
            sy.Clear();

            for (int i = 0; i < LC_S_PRE_POINT.Length; ++i)
            {
                sx.Add(LC_S_PRE_POINT[i][0]);
                sy.Add(LC_S_PRE_POINT[i][1]);
            }

            foreach (int x in x_)
            {
                sx.Add(x);
            }
            foreach (int y in y_)
            {
                sx.Add(y);
            }

            for (int i = 0; i < LC_S_POST_POINT.Length; ++i)
            {
                sx.Add(LC_S_POST_POINT[i][0]);
                sy.Add(LC_S_POST_POINT[i][1]);
            }
        }

        /// 位置を生成する
        public void createPoints()
        {
            int st = counter.StartValue;
            int ed = counter.EndValue;
            int dataNum = ed - st;

            if (dataNum < 0)
            {
                dataNum = -dataNum;
                int tmp = st;
                st = ed;
                ed = tmp;
            }

            List<Position> pts = new List<Position>();
            for (int i = 0; i < sx.Count; ++i)
            {
                Position pt = new Position();
                pt.X = (int)(dataNum * sx[i]);
                pt.Y = (int)(dataNum * sy[i]);

                pts.Add(pt);
            }

            // サンプルのポイントを dataNum/5 の点で補完する
            ptSpline = BSpline.CalcBSpline(pts, dataNum / 5);

            /*		
                    printf("PosNum %d\n", ptSpline.length);
                    foreach (inout Position p ; ptSpline) {
                        printf("X %f, Y %f\n", p.X, p.Y);
                    }
            */
        }

        public FreeLineCounter()
        {
            for (int i = 0; i < LC_S_POINT.Length; ++i)
            {
                sx.Add(LC_S_POINT[i][0]);
                sy.Add(LC_S_POINT[i][1]);
            }
        }

        /// 
        public FreeLineCounter(ICounter counter_)
            : this()
        {
            this.counter = counter_;
        }


        /// 丸め込みを行う
        private static int getRound(float f)
        {
            return (int)f;
        }

        private ICounter counter;

        private static readonly float[][] LC_S_PRE_POINT = {
		        new float[] {0.0f ,  0.0f},	
	        };

        private static readonly float[][] LC_S_POST_POINT = {
		         new float[] {1.0f, 1.0f},	
		         new float[] {1.0f, 1.0f},	
		         new float[] {1.0f, 1.0f},
	        };

        private static readonly float[][] LC_S_POINT = {
		         new float[] {0.0f ,  0.0f},	
		         new float[] {0.0f ,  0.0f},	//1 START
		         new float[] {0.1f , 0.1f},	//2
		         new float[] {0.2f , 0.2f},	//3
		         new float[] {0.6f , 0.6f},	//4
		         new float[] {0.8f, 0.8f},	//5
		         new float[] {0.9f, 0.9f},	//6
		         new float[] {1.0f, 1.0f},	//7 END
		         new float[] {1.0f, 1.0f},	
		         new float[] {1.0f, 1.0f},
            };

        private List<float> sx = new List<float>();	//　ユーザ定義曲線のサンプル点
        private List<float> sy = new List<float>();

        private List<Position> ptSpline = new List<Position>();

    }

    /// <summary>
    /// 小数点も扱えるカンターのためのインターフェース 
    /// </summary>
    public abstract class IDecimalCounter : ICounter
    {
        public abstract double GetDecimal();
    }


    /// <summary>
    /// ステップレートを設定できるCounterパッケージ。
    /// このパッケージに記述されているクラスはすべて、レートによってステップ比率が操作される。
    /// </summary>
    public class PowIncCounter : IDecimalCounter
    {

        public const float STEP_SCALE = 1024.0f;

        #region メンバフィールド

        // デフォルトの底
        protected const double DEFAULT_BASE = 0.915;

        protected double baseValue = DEFAULT_BASE;
        protected new double rootCount = 0.0f;
        protected int incCount;
        //protected int start;
        //protected int end;
        //protected int step;
        protected int rate;	//	nStep<0のときは、ｎ回のInc()で+1される

        protected bool init;			//	初期値が設定されているか？
        protected int initNum;		//	設定されている初期値

        #endregion

        public override int StartValue
        {
            get
            {
                return start;
            }
            set
            {
                start = value;
            }
        }

        public override int StepValue
        {
            get
            {
                return step;
            }
            set
            {
                step = value;
            }
        }

        public override int EndValue
        {
            get
            {
                return end;
            }
            set
            {
                end = value;
            }
        }

        public override int Value()
        {
            return (int)GetDecimal();
        }

        public override int ValueSined()
        {
            return Value();
        }

        //	設定
        public override void Set(int start_, int end_, int step_)
        {
            step = step_;
            start = start_;
            end = end_;
            incCount = 0;
        }

        public void SetIncCount(int value)
        {
            incCount = value;
        }

        ////	初期値
        public void SetInit(int n)
        {
            init = true;
            initNum = n;
        }

        //	カウンタのリセット
        public override void Reset()
        {
            rate = 0;
            incCount = 0;
            if (!init)
            {
                rootCount = 0.0f;
            }
        }

        /// <summary>
        /// 終了したか.
        /// 常に false を返却する
        /// </summary>
        public override bool IsEnd
        {
            get { return false; }
        }

        //	カウンタのインクリメント(終端まで達すると、再度、初期値に戻る)
        public override void Inc()
        {
            ++incCount;
            rootCount = GetIncValue() * incCount;
        }
        public void Dec()
        {
            --incCount;
            rootCount = GetIncValue() * incCount;
        }

        public int opAssign(int n)
        {
            rootCount = n;
            return n;
        }

        public override double GetDecimal()
        {
            return start + ((end - start) * (1.0f - (global::System.Math.Pow(baseValue, (double)rootCount))));
        }

        /// 使用している底を返却する
        public double GetBase()
        {
            return baseValue;
        }
        /// 使用する底を設定する(0 < baseValue_ < 1)
        public void SetBase(double base_)
        {
            baseValue = base_;
        }

        public PowIncCounter()
        {
            Set(0, int.MaxValue, 1);
            Reset();
        }

        public PowIncCounter(int end_)
        {
            Set(0, end_, 1);
            Reset();
        }

        public PowIncCounter(int start_, int end_, int step_)
        {
            Set(start_, end_, step_);
            Reset();
        }


        protected double GetIncValue()
        {
            return step / STEP_SCALE;
        }
    }


}
