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

namespace Yamalib.Draw
{
    public class NumberCounter
    {
        #region メンバフィールド

        private readonly TextureLoader numberTextures;
        private readonly LinkedList<CountingData> countData = new LinkedList<CountingData>();
        private readonly int unitWidth;
        private readonly int unitHeight;
        private int currentNo = 0;
        private int countingNo = 0;
        private int targetNo = 0;
        private bool nowCounting = false;
        private int x;
        private int y;

        #endregion

        #region 公開メンバ

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="textureLoader">
        /// 0〜9番にそれぞれその数字がリストされているローダ
        /// またそれぞれの画像はすべて同じサイズであること！
        /// </param>
        public NumberCounter(TextureLoader textureLoader, int defaultKeta)
        {
            this.numberTextures = textureLoader;
            ITexture tmp = numberTextures.GetTexture(0);
            unitWidth = (int)tmp.Width;
            unitHeight = (int)tmp.Height;

            for (int i = 0; i < defaultKeta; i++)
            {
                countData.AddLast(new CountingData(numberTextures));
            }
        }

        /// <summary>
        /// 表示座標の設定
        /// </summary>
        public int X
        {
            get { return x; }
            set { x = value; }
        }

        /// <summary>
        /// 表示座標の設定
        /// </summary>
        public int Y
        {
            get { return y; }
            set { y = value; }
        }

        /// <summary>
        /// カウントアップ中か
        /// </summary>
        public bool IsCounting
        {
            get { return nowCounting; }
        }

        /// <summary>
        /// カウントアップしているとき、強制的にカウントアップを終わらせ、
        /// 終値にする
        /// </summary>
        public void CountUpFinish()
        {
        }

        /// <summary>
        /// 現在の数字からカウントアップしながら数字を増やしていく
        /// </summary>
        /// <param name="nextNo"></param>
        /// <param name="countSpeed"></param>
        public void CountUp(int nextNo, int countSpeed)
        {
            if (currentNo != nextNo)
            {
                countingNo = currentNo;
                targetNo = nextNo;
                nowCounting = true;
                foreach (CountingData data in countData)
                {
                    data.CountSpeed(countSpeed);                  
                }
            }
        }

        /// <summary>
        /// 表示する番号をエフェクトなしで設定する
        /// </summary>
        public int Number
        {
            get { return currentNo; }
            set 
            { 
                currentNo = value;
                byte[] numbers = NumberArray(currentNo);
                if (numbers.Length > countData.Count)
                {
                    for (int i = 0; i < numbers.Length - countData.Count; ++i)
                    {
                        countData.AddLast(new CountingData(numberTextures));
                    }
                }

                LinkedList<CountingData>.Enumerator enumerator = countData.GetEnumerator();
                for(int i = 0; enumerator.MoveNext(); ++i)
                {
                    enumerator.Current.Number = numbers[numbers.Length - i - 1];
                }
            }
        }

        /// <summary>
        /// 移動処理
        /// </summary>
        /// <param name="screen"></param>
        /// <returns></returns>
        public YanesdkResult OnMove(IScreen screen)
        {
            if (nowCounting)
            {
                if (!IsCountEffecting())
                {
                    if (countingNo != targetNo)
                    {
                        nextValue();
                    }
                    else
                    {
                        currentNo = targetNo;
                        nowCounting = false;
                    }
                }
                for (LinkedList<CountingData>.Enumerator enumerator = countData.GetEnumerator(); enumerator.MoveNext(); )
                {
                    enumerator.Current.OnMove(screen);
                }
            }
            return YanesdkResult.NoError;
        }

        /// <summary>
        /// 描画処理
        /// </summary>
        /// <param name="screen"></param>
        /// <returns></returns>
        public YanesdkResult OnPaint(IScreen screen)
        {
            OnPaintCounting(screen);
            return YanesdkResult.NoError;
        }

        #endregion


        #region 非公開メソッド

        /// <summary>
        /// 桁毎の数字分解して返却する
        /// </summary>
        /// <param name="number"></param>
        /// <returns></returns>
        private byte[] NumberArray(int number)
        {
            String strNum = FullLengthStr(number.ToString(), countData.Count);
            byte[] result = new byte[strNum.Length];
            for (int i = 0; i < strNum.Length; i++)
            {
                result[i] = Convert.ToByte(strNum[i].ToString());
            }
            return result;
        }

        /// <summary>
        /// ０埋めする
        /// </summary>
        /// <param name="str"></param>
        /// <param name="rank"></param>
        /// <returns></returns>
        private String FullLengthStr(String str, int rank)
        {
            StringBuilder sb = new StringBuilder();
            if (str == null)
            {
                for (int i = 0; i < rank; ++i)
                {
                    sb.Append('0');
                }
                return sb.ToString();
            }
            for (int i = 0; i < rank - str.Length; ++i)
            {
                sb.Append('0');
            }
            sb.Append(str);
            return sb.ToString();
        }


        /// <summary>
        /// カウンタが回転中か？
        /// </summary>
        /// <returns></returns>
        private bool IsCountEffecting()
        {
            LinkedList<CountingData>.Enumerator enumerator = countData.GetEnumerator();
            for(int i = 0; enumerator.MoveNext(); ++i)
            {
                if (enumerator.Current.IsCounting)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 指定した数字を描画する
        /// </summary>
        /// <param name="screen"></param>
        /// <param name="number"></param>
        private void OnPaintNumber(IScreen screen, int number)
        {
            byte[] numbers = NumberArray(number);
            int offset = 0;
            foreach (byte num in numbers)
            {
                screen.Blt(numberTextures.GetTexture(num), x + offset, y);
                offset += unitWidth;
            }
        }

        /// <summary>
        /// カウントアップで表示
        /// </summary>
        /// <param name="screen"></param>
        private void OnPaintCounting(IScreen screen)
        {
            int offsetx = unitWidth * countData.Count;
            for (LinkedList<CountingData>.Enumerator enumerator = countData.GetEnumerator(); enumerator.MoveNext(); )
            {
                enumerator.Current.OnPaint(screen, x + offsetx, y);
                offsetx -= unitWidth;
            }
        }

        /// <summary>
        /// 次の値に進める
        /// </summary>
        private void nextValue()
        {
            if (nowCounting)
            {
                ++countingNo;
                for (LinkedList<CountingData>.Enumerator enumerator = countData.GetEnumerator(); enumerator.MoveNext(); )
                {
                    if (!enumerator.Current.Next())
                    {
                        break;
                    }
                }
            }
        }

        #endregion


        internal class CountingData
        {
            private readonly TextureLoader numberTextures;
            private readonly int unitWidth;
            private readonly int unitHeight;
            private RootCounterS imgOffsetCounter;
            private int number;
            private int nextNumber;
            private bool counting;

            /// <summary>
            /// コンストラクタ
            /// </summary>
            /// <param name="numberTextures"></param>
            public CountingData(TextureLoader numberTextures)
            {
                this.numberTextures = numberTextures;
                ITexture tmp = numberTextures.GetTexture(0);
                unitWidth = (int)tmp.Width;
                unitHeight = (int)tmp.Height;
                imgOffsetCounter = new RootCounterS(0, 0, 1);
            }

            /// <summary>
            /// 0-9 番号
            /// </summary>
            public int Number
            {
                get { return number; }
                set 
                { 
                    number = value % 10;
                }
            }

            /// <summary>
            /// カウント中か？
            /// </summary>
            public bool IsCounting
            {
                get { return counting; }
            }

            /// <summary>
            /// 一つ進める
            /// </summary>
            public bool Next()
            {
                imgOffsetCounter.Set(0, unitHeight, imgOffsetCounter.StepValue);
                imgOffsetCounter.Reset();
                counting = true;
                nextNumber = (number + 1) % 10;
                return 0 == nextNumber;
            }

            public void CountSpeed(int speed)
            {
                imgOffsetCounter.StepValue = speed;
            }

            /// <summary>
            /// 移動処理
            /// </summary>
            /// <param name="screen"></param>
            /// <returns></returns>
            public YanesdkResult OnMove(IScreen screen)
            {
                if (counting)
                {
                    imgOffsetCounter.Inc();
                    if (imgOffsetCounter.IsEnd)
                    {
                        counting = false;
                        Number = nextNumber;
                    }
                }
                return YanesdkResult.NoError;
            }

            /// <summary>
            /// 描画処理
            /// </summary>
            /// <param name="screen"></param>
            /// <returns></returns>
            public YanesdkResult OnPaint(IScreen screen, int x, int y)
            {
                if (counting)
                {
                    int offsety = imgOffsetCounter.Value();
                    Rect upRc = new Rect(0, offsety, unitWidth, unitHeight);
                    Rect downRc = new Rect(0, 0, unitWidth, offsety);

                    // 上部分
                    screen.Blt(numberTextures.GetTexture(number), x, y, upRc);
                    // 下部分
                    screen.Blt(numberTextures.GetTexture(nextNumber), x, y + (int)upRc.Height, downRc);
                }
                else
                {
                    screen.Blt(numberTextures.GetTexture(number), x, y);
                }

                return YanesdkResult.NoError;
            }

        }


    }
}
