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

namespace Kyojin.Scene.Title
{
    class TitleDial
    {
        /** 角度制御クラス */
        private class AngleControl
        {
            /// 回転状態定義
            public enum ROTATE { STOP, LEFT, RIGHT };
            /// 今のところ加速度は固定値で
            public const float ACCELERATION = 0.025F;
            /// 今のところ加速度は固定値で
            public const float MAX_SPEED = 1.0F;
            /// 減速を開始する位置
            public const int STOPPING_DIST = 15;
            public const float DEACCELERATION = 40.0F;

            /// 今の角度から、引数で指定された角度に回転する
            public void TurnTo(int rad)
            {
                if (rad == (int)m_nowAngle)
                {
                    return;
                }
                m_rotate = GetRoteTo(rad);
                m_dstAngle = rad;

                // 回転方向決定
                if (ROTATE.LEFT == m_rotate)
                {
                    m_sgn = -1;
                }
                else if (ROTATE.RIGHT == m_rotate)
                {
                    m_sgn = 1;
                }

                //Log.print("AngleControl#TurnTo : %s to %s with %s", (int)m_nowAngle, Rad, m_sgn);
            }

            /// 動作処理
            public void OnMove()
            {
                // 回転中であること
                if (ROTATE.STOP != m_rotate)
                {

                    m_speed += GetAccel();

                    // 回転方向に従って、速度制限			
                    if (m_sgn < 0)
                    {
                        if (-MAX_SPEED > m_speed)
                        {
                            m_speed = -MAX_SPEED;
                        }
                    }
                    else
                    {
                        if (MAX_SPEED < m_speed)
                        {
                            m_speed = MAX_SPEED;
                        }
                    }

                    // 現在の速度で角移動
                    m_nowAngle += m_speed;
                    NormalizeAngle(ref m_nowAngle);

                    if (m_targetAngleStopFlg)
                    {
                        // 回転停止処理
                        if (m_sgn < 0)
                        {
                            if (m_dstAngle >= m_nowAngle)
                            {
                                //Log.print("AngleControl#OnMove : STOP LEFT %s to %s", m_nowAngle, m_dstAngle);
                                m_rotate = ROTATE.STOP;
                            }
                        }
                        else
                        {
                            if (m_dstAngle <= m_nowAngle)
                            {
                                //Log.print("AngleControl#OnMove : STOP LEFT %s to %s", m_nowAngle, m_dstAngle);
                                m_rotate = ROTATE.STOP;
                            }
                        }
                    }

                }
                else
                {
                    // ストップ初期化
                    m_speed = 0.0f;
                }
            }

            /// 現在の角度を取得
            public float GetNowAngle()
            {
                return m_nowAngle;
            }

            /// コンストラクタ
            public AngleControl()
            {
                m_sgn = 1;
            }


            private ROTATE GetRoteTo(int rad)
            {
                int now = (int)m_nowAngle;
                rad -= now;
                // 0 - 511 に補正
                if (rad < 0)
                {
                    rad = 512 + now;
                }
                return rad < 256 ? ROTATE.RIGHT : ROTATE.LEFT;
            }

            /// 引数の角を0 - 511 に正規化する
            public static void NormalizeAngle(ref float rad)
            {
                if (rad < 0)
                {
                    rad = 511.0F - rad;
                }
                else if (511.0F < rad)
                {
                    rad -= 511.0f;
                }
            }

            /// 加速度を取得する
            private float GetAccel()
            {
                if (m_targetAngleStopFlg)
                {	// 停止処理を行うのであれば
                    // 減速域であるか？
                    if (GetDistance() <= STOPPING_DIST)
                    {
                        // 逆制動を行う
                        return m_speed / DEACCELERATION * -m_sgn;
                    }
                }
                return m_accel * m_sgn;
            }

            /// 距離を取得する
            private int GetDistance()
            {
                return Math.Abs((int)m_nowAngle - (int)m_dstAngle);
            }

            /// 目標となる角度に達したとき停止する
            public void TargetAngleThenStop(bool b)
            {
                m_targetAngleStopFlg = b;
            }

            private float m_accel = ACCELERATION; 	//!< 加速度
            private float m_speed = 0.0f;	//!< 速度
            private ROTATE m_rotate;	//!< 回転の状態
            private float m_nowAngle = 0.0f;
            private float m_dstAngle = 0.0f;
            private int m_sgn;	//! サイン
            private bool m_targetAngleStopFlg;
        }


        #region メンバフィールド

        /* 項目描画位置の端からのマージン */
        private const int LC_ITEM_L_MARGIN = 20;

        static SinTable sin = SinTable.Instance;

        private int x;	//!< ダイアルの表示中心位置x
        private int y;	//!< ダイアルの表示中心位置y
        private int sx;	//!< ダイアルテクスチャサイズX
        private int sy; //!< ダイアルテクスチャサイズY

        private int selectedPos;	//!< 選択中項目インデックス
        private int rotateSpeed;	//!< 回転速度
        private bool rotate;			//!< 回転中かどうかを表すフラグ
        private RootCounterS angle;			//!< 角度

        private ITexture t_dial;		//!< ダイアルの基本テクスチャ
        private List<ITexture> vItems = new List<ITexture>(); 	//!< こいつはダイアルにつけるボタン。追加削除を行いたいのでVector
        private List<string> vNames = new List<string>(); 	//!< 項目に対する名前付けリスト
        private List<Point> itemPoints = new List<Point>();	//!< アイテム描画基本位置を保持する

        private AngleControl m_angleCtl;

        #endregion

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

        /// ダイアル表示中心位置の取得
        public void GetXY(out int x_, out int y_)
        {
            x_ = x;
            y_ = y;
        }

        /// 基本ダイアルテクスチャの設定
        public void SetDialTexture(ITexture tex)
        {

            t_dial = tex;
            sx = (int)t_dial.Width;
            sy = (int)t_dial.Height;

            //Log.print("SetDialTexture sx: %s, sy : %s", sx, sy);
        }

        /// ダイアル項目テクスチャの追加
        public void AddItem(ITexture t_)
        {
            vItems.Add(t_);
        }

        /// 項目を名前付きで追加する
        /// 現在何を選択されているか名前をgetNameで取得することができる
        public void AddItemName(ITexture t_, string name)
        {
            vNames.Add(name);
            AddItem(t_);
        }

        /// 選択いしている項目に関連づけられた名前を返却します
        public string GetSelectedItemName()
        {
            if (selectedPos == 0)
            {
                return vNames[0];
            }

            return vNames[vNames.Count - selectedPos];
        }

        /// 選択済み番号を取得
        public int GetSelectedPos()
        {
            return selectedPos;
        }

        /// 項目に関連している名前を指定して、そのアイテムを選択させる
        /// 指定した名前が存在しなければfalseが返る
        bool SelectByName(string str)
        {
            int selected = -1;	// not found marker
            for (int i = 0; i < vNames.Count; i++)
            {
                if (vNames[i] == str)
                {
                    selected = vItems.Count - i;
                    break;
                }
            }

            if (-1 == selected)
            {
                return false;
            }
            selectedPos = selected;
            int dstAngle = (int)((512.0f / vItems.Count) * (selectedPos));
            angle.Set(dstAngle, dstAngle, rotateSpeed);
            return true;
        }

        /// ダイアル項目を全消去します
        public void ClearItems()
        {
            vItems.Clear();
        }

        /// 指定したアイテムに直行する
        public void TurnTo(int itemID)
        {
            if (selectedPos == itemID)
            {
                //何もしない
                return;
            }
            if (itemID >= vItems.Count)
            {
                //何もしない
                return;
            }

            // 左まわりか、右周りを決める
            int dstAngle = (int)((512.0f / vItems.Count) * itemID);
            m_angleCtl.TurnTo(dstAngle);
            selectedPos = itemID;
        }


        /// 右回り（時計回り）させる
        public void TurnLeft()
        {

            // 項目もないのにすることないのだ...
            if (0 == vItems.Count) return;

            // 回転中であれば、なにもしない
            if (rotate) return;

            // 選択位置を一つ増やす
            ++selectedPos;

            if (selectedPos >= vItems.Count)
            {
                selectedPos = 0;
            }

            // 現在の角度
            int nowAngle = angle.Value();
            int dstAngle = (int)((512.0f / vItems.Count) * selectedPos);

            if (nowAngle == Y4dConst.C_RAD_MAX)
            {
                // 一周後の値。０にリセットする
                nowAngle = 0;
            }

            if (dstAngle == 0)
            {
                // これは一周したということ。
                dstAngle = Y4dConst.C_RAD_MAX;
            }

            angle.Set(nowAngle, dstAngle, rotateSpeed);

            //		    Log.print("TurnLeft : src %s, dst : %s, pos : %s", nowAngle, dstAngle, selectedPos );

            rotate = true;

        }

        /// 左回り（反時計回り）させる
        public void TurnRight()
        {

            // 項目もないのにすることないのだ...
            if (0 == vItems.Count) return;

            // 回転中であれば、なにもしない
            if (rotate) return;

            // 選択位置を一つ減らす
            --selectedPos;

            if (selectedPos < 0)
            {
                selectedPos = vItems.Count - 1;
            }

            // 現在の角度
            int nowAngle = angle.Value();
            int dstAngle = (int)((512.0f / vItems.Count) * selectedPos);

            if (nowAngle == 0)
            {
                // 一周後の値。０にリセットする
                nowAngle = Y4dConst.C_RAD_MAX;
            }

            if (dstAngle == Y4dConst.C_RAD_MAX)
            {
                // これは一周したということ。
                dstAngle = 0;
            }

            angle.Set(nowAngle, dstAngle, rotateSpeed);


            //		Log.print("TurnRight : src %s, dst : %s, pos : %s", nowAngle, dstAngle, selectedPos );

            rotate = true;
        }

        /// ダイアル回転中かどうか
        public bool IsAround()
        {
            return rotate;
        }

        /// 目標となる角度に達したとき、停止する
        public void TargetAngleThenStop(bool b)
        {
            m_angleCtl.TargetAngleThenStop(b);
        }

        /// 毎回よびだすといいなり
        public void OnMove(IScreen scree)
        {
            m_angleCtl.OnMove();
        }

        /// できるだけ毎回呼び出すなり
        public void OnDraw(IScreen screen)
        {

            // X, yを画像の中心点として描画する 
            screen.BltRotate(t_dial, x, y, (int)m_angleCtl.GetNowAngle(), 1.0f, 4);
        }

        /// コンストラクタ
        public TitleDial()
        {
            angle = new RootCounterS();
            angle.Set(0, Y4dConst.C_RAD_MAX, 2);
            rotateSpeed = 2;
            m_angleCtl = new AngleControl();
        }


        /// アイテムを描画する位置を取得します
        private void CalcItemPos(out int[] xs, out int[] ys, out int[] angles)
        {
            // 現在の角度
            int agl = angle.Value();
            int itemCnt = vItems.Count;

            xs = new int[itemCnt];
            ys = new int[itemCnt];
            angles = new int[itemCnt];

            // ダイアルテクスチャの半径
            int r = (sy / 2) - LC_ITEM_L_MARGIN;

            for (int i = 0; i < vItems.Count; i++)
            {
                ITexture t = vItems[i];
                int baseAngle = 256 + agl + (Y4dConst.C_RAD_MAX / itemCnt) * i;
                // ここが基点
                xs[i] = x + sin.Cos(baseAngle, r);
                ys[i] = y + sin.Sin(baseAngle, r);
                angles[i] = baseAngle - 256;
            }
        }

    }
}
