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 class ClockHand
    {
        #region フィールドメンバ

        private int x;
        private int y;
        private bool around;
        private ITexture texture;

        private ClockHand coopeHand;	//!< 連携元針
        private bool reverse;
        private bool rotation = true;	//!< 回転中か
        private bool bDivTick;  //!< １クロックを分割動作するか
        //private ITexture hand;	//!< 針テクスチャ
        private readonly InteriorCounter divTickCounter = new InteriorCounter();
        private readonly InteriorCounter divRoundCounter = new InteriorCounter();
        private int divTick; 	//!< １クロックの分割数を設定します
        private int divRound;	//!< １回転の分割数

        #endregion

        /// <summary>
        /// 針テクスチャを設定します
        /// </summary>
        public ITexture Texture
        {
            get { return texture; }
            set { texture = value; }
        }

        /// <summary>
        /// 反時計回りか？
        /// </summary>
        public bool IsReverse
        {
            get { return reverse; }
            set { reverse = value; }
        }

        /// <summary>
        /// 回転状態を設定します
        /// </summary>
        public bool Rotate
        {
            get { return rotation; }
            set { rotation = value; }
        }

        /// <summary>
        /// 針が１周したか
        /// </summary>
        public bool IsAround
        {
            get
            {
                if (around)
                {
                    around = false;
                    return true;
                }
                return false;

            }
        }

        /// <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 void GetXY(out int x_, out int y_)
        {
            x_ = x;
            y_ = y;
        }

        /// <summary>
        /// １クロックの動作の分割数を設定します
        /// </summary>
        /// <param name="div"></param>
        public void SetDivTick(int div)
        {
            divTick = div;
            divTickCounter.Set(0, 512, div);
            // １クロック分割を有効にする
            bDivTick = true;
        }

        /// <summary>
        /// １回転の分割数を設定します
        /// </summary>
        /// <param name="div"></param>
        public void SetDivRound(int div)
        {
            divRound = div;
            divRoundCounter.Set(0, 512, div);
        }

        /// <summary>
        /// 連携する小針を設定します
        /// </summary>
        /// <param name="hand_"></param>
        public void SetCooperateHand(ClockHand hand_)
        {
            coopeHand = hand_;
            /// 連携元があるなら自立的には回転しない
            rotation = false;
        }

        /// <summary>
        /// 初期値を設定します
        /// </summary>
        /// <param name="i"></param>
        public void SetInit(int i)
        {
            divRoundCounter.SetFlame(i);
            divRoundCounter++;
            SetTickCounter(divRoundCounter.Value());
        }

        /// <summary>
        /// 移動処理
        /// </summary>
        /// <param name="screen"></param>
        public void OnMove(IScreen screen)
        {
            // 回転中か？
            if (rotation)
            {
                // 分割移動するの？
                if (bDivTick)
                {
                    // １移動
                    divTickCounter++;
                    if (divTickCounter.IsEnd)
                    {
                        rotation = false;
                    }
                }
                else
                {
                    divRoundCounter++;
                    if (divRoundCounter.IsEnd)
                    {
                        divRoundCounter.Set(0, 512, divRound);
                        around = true;
                    }
                }
            }
            else
            {

                if (coopeHand != null)
                {
                    // 連携元針が動いたか？
                    if (coopeHand.IsAround)
                    {

                        if (divRoundCounter.IsEnd)
                        {
                            divRoundCounter.Set(0, 512, divRound);
                            divRoundCounter++;
                            around = true;
                        }

                        // 針が動く前rad
                        int radPre = divRoundCounter.Value();
                        divRoundCounter++;
                        int rad = divRoundCounter.Value();
                        divTickCounter.Set(radPre, rad, divTick);

                        rotation = true;
                    }
                }
            }
        }

        /// <summary>
        /// 描画処理
        /// </summary>
        /// <param name="screen"></param>
        /// <param name="baseX"></param>
        /// <param name="baseY"></param>
        public void OnDraw(IScreen screen, int baseX, int baseY)
        {
            // テクスチャセットしてくれ
            if (texture == null) return;

            int rad;
            if (bDivTick)
            {
                rad = divTickCounter.Value();
            }
            else
            {
                rad = divRoundCounter.Value();
            }

            // 逆回転なら戻るのだー
            if (reverse)
            {
                rad = 512 - rad;
            }

            // 描画
            screen.BltRotate(texture, x + baseX, y + baseY, rad, 1.0f, 7);
        }


        #region 非公開メンバ

        private void SetTickCounter(int start_)
        {
            divTickCounter.Set(start_, start_, 1);
            divTickCounter++;
        }

        #endregion
    }

    /// <summary>
    /// 時計の針を管理するクラス
    /// </summary>
    public class ClockHandManager
    {
        #region フィールドメンバ

        private readonly List<ClockHand> clockHands = new List<ClockHand>();

        #endregion

        /// 毎回呼び出すなり
        public void OnMove(IScreen screen)
        {
            foreach (ClockHand hand in clockHands)
            {
                hand.OnMove(screen);
            }
        }

        /// 毎回呼び出すなり
        public void OnDraw(IScreen screen, int baseX, int baseY)
        {
            foreach (ClockHand hand in clockHands)
            {
                hand.OnDraw(screen, baseX, baseY);
            }
        }

        /// 針を追加する
        public void AddClockHand(ClockHand hand_)
        {
            clockHands.Add(hand_);
        }

    }

    /// <summary>
    /// 時計表示用クラス
    /// なんも連携しない
    /// </summary>
    public class ClockDisplaySimple
    {
        #region 定数値

        public const int RENGE = 100;
        public const int RATE = 128;

        #endregion

        #region フィールドメンバ

        private readonly SinTable sin;
        private readonly Rand rand;

        private ITexture oldTexture;	//!< すでにある古いテクスチャ
        private ITexture newTexture;	//!< 新しいテクスチャ

        private TextureLoader tl;
        private int elementNum;

        private readonly Rect dstRect = new Rect();
        private bool drawOld;
        private bool drawNew;
        private int x;
        private int y;
        private int newx;
        private int newy;
        private int intx;
        private int inty;
        private int rotateRenge;

        private int ox;		//!< 回転の中心
        private int oy; 	//!< 回転の中心
        private bool move;
        private int count;

        private int textureCounter;

        private RootCounter stopCounter;
        private RootCounterS moveCounter;
        private RootCounter radCounter;
        private RootCounter changeRoteCounter;

        #endregion

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public ClockDisplaySimple()
        {
            sin = SinTable.Instance;
            rand = new Rand();
            rand.Randomize();
            stopCounter = new RootCounter();
            moveCounter = new RootCounterS();
            radCounter = new RootCounter(0, 512, 30);
            changeRoteCounter = new RootCounter(0, 512, 3);
            rotateRenge = RENGE;
        }

        /// <summary>
        /// 表示すべき画像のテクスチャセット
        /// </summary>
        /// <param name="tl_"></param>
        /// <param name="elementNum"></param>
        public void SetTextureLoader(TextureLoader tl_, int elementNum)
        {
            this.elementNum = elementNum;
            int size = elementNum;
            if (2 <= size)
            {
                tl = tl_;

                for (int i = 0; i < elementNum; ++i)
                {
                    ITexture t = tl.GetTexture(i);
                }

                oldTexture = tl.GetTexture(0);
                newTexture = tl.GetTexture(rand.GetRand(size));
            }
        }

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

        /// <summary>
        /// 表示矩形
        /// </summary>
        /// <param name="drawRc"></param>
        public void SetRect(Rect rc)
        {
            dstRect.SetRect(rc.Left, rc.Top, rc.Right, rc.Bottom);
            SetXY((int)dstRect.Left, (int)dstRect.Top);
        }

        /// <summary>
        /// 表示矩形
        /// </summary>
        /// <param name="left"></param>
        /// <param name="top"></param>
        /// <param name="right"></param>
        /// <param name="bottom"></param>
        public void SetRect(int left, int top, int right, int bottom)
        {
            dstRect.SetRect(left, top, right, bottom);
            SetXY(left, top);
        }

        /// <summary>
        /// 原回転の初期角度の設定
        /// </summary>
        /// <param name="rad_"></param>
        public void SetInitRad(int rad_)
        {
            radCounter.SetInit(rad_);
            radCounter.Reset();
        }

        /// <summary>
        /// 回転ｒの長さの設定
        /// </summary>
        public int RotateRenge
        {
            get { return rotateRenge; }
            set { rotateRenge = value; }
        }


        /// <summary>
        /// 表示を切り替えるまでのフレーム数
        /// </summary>
        /// <param name="time_"></param>
        public void SetStopTime(int time_)
        {
            stopCounter.Set(0, time_, 1);
        }

        /// <summary>
        /// 移動処理
        /// </summary>
        /// <param name="screen"></param>
        public void OnMove(IScreen screen)
        {
            stopCounter++;

            // 表示を入れ替えるのか？
            if (stopCounter.IsLapAround)
            {
                // まず回転中心を設定する
                radCounter++;

                ox = intx + ((rotateRenge * (sin.Cos(radCounter.Value()))) >> 16);
                oy = inty + ((rotateRenge * (sin.Sin(radCounter.Value()))) >> 16);

                // 入れ替え回転カウンタに初期値を設定
                changeRoteCounter.SetInit(radCounter.Value());
                changeRoteCounter.Reset();
                count = 0;
                move = true;
            }

            // 入れ替え中なんか？
            if (move)
            {
                ++count;
                x = ox - ((rotateRenge * (sin.Cos(changeRoteCounter.Value()))) >> 16);
                y = oy - ((rotateRenge * (sin.Sin(changeRoteCounter.Value()))) >> 16);
                newx = ox - ((rotateRenge * (sin.Cos(changeRoteCounter.Value() - RATE))) >> 16);
                newy = oy - ((rotateRenge * (sin.Sin(changeRoteCounter.Value() - RATE))) >> 16);
                changeRoteCounter++;
                if (count >= RATE / changeRoteCounter.StepValue)
                {
                    move = false;
                    x = intx;
                    y = inty;
                    newx = -1;
                    newy = -1;
                    SwapTexture();
                }
            }

            Rect rc = new Rect();
            rc.SetRect(x, y, x + (int)oldTexture.Width, y + (int)oldTexture.Height);
            if (CollisionRect(dstRect, rc))
            {
                drawOld = true;
            }
            else
            {
                drawOld = false;
            }

            rc.SetRect(newx, newy, newx + (int)newTexture.Width, newy + (int)newTexture.Height);
            if (CollisionRect(dstRect, rc))
            {
                drawNew = true;
            }
            else
            {
                drawNew = false;
            }
        }

        /// <summary>
        /// 描画処理
        /// </summary>
        /// <param name="screen"></param>
        /// <param name="baseX"></param>
        /// <param name="baseY"></param>
        public void OnDraw(IScreen screen, int baseX, int baseY)
        {
            // テクスチャセットしてけれ
            if (oldTexture == null || newTexture == null) return;
            // 描画
            if (drawOld)
            {
                screen.Blt(oldTexture, x + baseX, y + baseY);
            }
            if (drawNew)
            {
                screen.Blt(newTexture, newx + baseX, newy + baseY);
            }
        }


        #region 非公開メンバ

        /// <summary>
        /// 表示位置の設定
        /// </summary>
        /// <param name="x_"></param>
        /// <param name="y_"></param>
        private void SetXY(int x_, int y_)
        {
            intx = x = x_;
            inty = y = y_;

            ox = x + (int)((RotateRenge * (sin.Cos(radCounter.Value()))) >> 16);
            oy = y + (int)((RotateRenge * (sin.Sin(radCounter.Value()))) >> 16);
        }

        /// <summary>
        ///  新旧のテクスチャをスワップします
        /// </summary>
        private void SwapTexture()
        {
            ITexture tmp = oldTexture;
            oldTexture = newTexture;
            newTexture = tmp;
            //int size = noiseTextureLoader.getInfoList().size();

            newTexture = tl.GetTexture(rand.GetRand(elementNum));

            ++textureCounter;
        }

        /// <summary>
        /// 矩形同士のの当たり判定	
        /// </summary>
        /// <param name="rc1"></param>
        /// <param name="rc2"></param>
        /// <returns></returns>
        private bool CollisionRect(Rect rc1, Rect rc2)
        {
            // 高速判定 refered by D5（＾＾
            long a = (int)(rc1.Left - rc2.Right) & (int)(rc2.Left - rc1.Right)
                           & (int)(rc1.Top - rc2.Bottom) & (int)(rc2.Top - rc1.Bottom);
            return (((int)a) >> 31) != 0 ? true : false;
        }

        #endregion
    }

    /// <summary>
    /// 装飾用時計クラス
    /// 実際の時間とは何の関係もありません
    /// ClockHand と ClockManager による実現
    /// </summary>
    public class DecorativeColck : IDisposable
    {
        #region 定数値

        private const int OX = 308;
        private const int OY = 175;

        /// 表示穴の配列
        private static readonly int[][] ayPt = new int[][] {
		    new int[] { 297, 83, 327, 111 },
		    new int[] {  337, 94, 367, 122 },
		    new int[] {  369, 126, 399, 154 },
		    new int[] {  375, 170, 405, 198 },
		    new int[] {  353, 212, 383, 240 },
		    new int[] {  313, 237, 343, 265 },
		    new int[] {  253, 227, 283, 245 },
		    new int[] {  220, 191, 250, 219 },
		    new int[] {  217, 146, 247, 174 },
		    new int[] {  237, 106, 267, 134 }
	    };

        #endregion

        #region フィールドメンバ

        private ITexture t_clockBase;
        private ITexture t_clock;		/// 時計のテクスチャ
        private ITexture t_bigHand;		/// 時計の大針
        private ITexture t_littelHand;	/// 時計の小針

        private int x;	//!< 表示位置
        private int y;	//!< 表示位置

        private ClockHand hand1;
        private ClockHand hand2;
        private ClockHand hand3;
        private ClockHand hand4;
        private ClockHandManager manager;	//!< 時計の針管理人

        private List<ClockDisplaySimple> items;	//!< 表示計のベクター
        private TextureLoader tl1;
        private Rand rand;

        #endregion

        #region IDisposable メンバ

        public void Dispose()
        {
            YamalibUtility.Dispose(t_clockBase);
            YamalibUtility.Dispose(t_clock);
            YamalibUtility.Dispose(t_bigHand);
            YamalibUtility.Dispose(t_littelHand);
            YamalibUtility.Dispose(tl1);
        }

        #endregion

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

            // 時計下敷き
            t_clockBase = YamalibConst.CreateTexture("img/demo/clock_base.png");

            //  時計
            t_clock = YamalibConst.CreateTexture("img/demo/clock.png");

            // 大針
            t_bigHand = YamalibConst.CreateTexture("img/demo/hand.png");

            // 小針
            t_littelHand = YamalibConst.CreateTexture("img/demo/lhand.png");

            hand1 = new ClockHand();
            hand2 = new ClockHand();
            hand3 = new ClockHand();
            hand4 = new ClockHand();
            DateTime date = DateTime.Now;

            // 分
            hand1.Texture = t_bigHand;
            hand1.SetXY(OX, OY);
            hand1.SetDivRound(60);
            hand1.SetDivTick(5);
            hand1.SetInit(date.Minute);

            // 1 / 60
            hand2.Texture = t_littelHand;
            hand2.SetXY(OX, OY);
            hand2.SetDivRound(60);
            hand2.IsReverse = (true);

            // 秒
            hand3.Texture = t_bigHand;
            hand3.SetXY(OX, OY);
            hand3.SetDivRound(60);
            hand3.SetDivTick(10);
            hand3.SetInit(date.Second);

            // 時
            hand4.Texture = t_bigHand;
            hand4.SetXY(OX, OY);
            hand4.SetDivRound(12);
            hand4.SetDivTick(5);
            hand4.SetInit(date.Hour % 12);

            // 連携元針の設定
            hand3.SetCooperateHand(hand2);
            hand1.SetCooperateHand(hand3);
            hand4.SetCooperateHand(hand1);

            manager = new ClockHandManager();

            manager.AddClockHand(hand2);
            manager.AddClockHand(hand3);
            manager.AddClockHand(hand1);
            manager.AddClockHand(hand4);

            // 時計計
            tl1 = new TextureLoader();
            tl1.IsDefRelativePath = false;
            tl1.LoadDefFile("img/demo/clock_item/item1.lst");

            //// キャッシュに喰っておく
            //tl1.setCacheSize(-1);	// 無限キャッシュ
            //for(int i = 0; i < tl1.getInfoList().size(); ++i) {
            //    Texture t = tl1.Get(i);
            //}

            items = new List<ClockDisplaySimple>();

            foreach (int[] pt in ayPt)
            {
                ClockDisplaySimple item = new ClockDisplaySimple();
                item.SetRect(pt[0], pt[1], pt[2], pt[3]);
                item.SetStopTime(60 + rand.GetRand(30));
                item.SetTextureLoader(tl1, YanesdkUtil.GetElementNum("img/demo/clock_item/item1.lst"));
                item.SetInitRad(rand.GetRand(512));
                items.Add(item);
            }
        }


        /// <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 void GetXY(out int x_, out int y_)
        {
            x_ = x;
            y_ = y;
        }

        /// 毎回呼び出すなり
        public void OnMove(IScreen screen)
        {
            manager.OnMove(screen);

            // 表示描画
            foreach (ClockDisplaySimple item in items)
            {
                item.OnMove(screen);
            }
        }


        /// 毎回呼び出すなり
        public void OnDraw(IScreen screen)
        {
            screen.SetColor(255, 255, 255);
            // 時計下敷き
            screen.Blt(t_clockBase, x, y);

            // 表示描画
            foreach (ClockDisplaySimple item in items)
            {
                item.OnDraw(screen, x, y);
            }

            // 時計素材描画
            screen.Blt(t_clock, x, y);

            // 描画
            manager.OnDraw(screen, x, y);
        }
    }
}
