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

namespace Yamalib.Draw.Effect
{
    /// <summary>
    /// 登録した対象を被写体として、カメラワークを実現します
    /// </summary>
    public class CameraWork : IDisposable
    {
        /// <summary>
        /// 被写体データの属性
        /// </summary>
        private class Subject
        {
            #region メンバフィールド

            private int x;				// 描画位置x
            private int y;				// 描画位置y
            private float rate;			// 拡大レート
            private float rot;			// 回転角

            private ITexture texture;	// テクスチャ

            private List<Subject> sub;
            private int sx;
            private int sy;

            #endregion

            /// <summary>
            /// 被写体に付属するサブオブジェクトを追加する
            /// </summary>
            /// <param name="t"></param>
            /// <param name="x_"></param>
            /// <param name="y_"></param>
            public void AddSubSubject(ITexture t, int x_, int y_)
            {
                Subject s = new Subject();
                s.Texture = t;
                s.x = x_;
                s.y = y_;

                if (null == sub)
                {
                    sub = new List<Subject>();
                }
                sub.Add(s);
            }

            public ITexture Texture
            {
                get { return texture; }
                set
                {
                    texture = value;
                    sx = (int)texture.Width;
                    sy = (int)texture.Height;
                }
            }

            public int X
            {
                get { return x; }
                set { x = value; }
            }

            public int Y
            {
                get { return y; }
                set { y = value; }
            }

            public float Rate
            {
                get { return rate; }
                set { rate = value; }
            }
            public float Rot
            {
                get { return rot; }
                set { rot = value; }
            }
        }

        /// <summary>
        /// カメラのワークフロー属性
        /// </summary>
        private class CameraFlow
        {
            #region メンバフィールド

            private int rotCount;
            private int rateCount;

            private readonly List<int> x = new List<int>();

            public List<int> X
            {
                get { return x; }
            }

            private readonly List<int> y = new List<int>();

            public List<int> Y
            {
                get { return y; }
            }


            private readonly List<int> ratePoints = new List<int>();	// 拡大を反映する位置

            public List<int> RatePoints
            {
                get { return ratePoints; }
            }

            private readonly List<int> rotPoints = new List<int>();	// 回転を反映する位置


            public List<int> RotPoints
            {
                get { return rotPoints; }
            }


            private readonly List<ICounter> rate = new List<ICounter>();	// 拡大

            public List<ICounter> Rate
            {
                get { return rate; }
            }

            private readonly List<ICounter> rot = new List<ICounter>();	// 回転

            public List<ICounter> Rot
            {
                get { return rot; }
            }


            #endregion

            public ICounter NextRot()
            {
                return Rot[rotCount++];
            }

            public ICounter NextRate()
            {
                return Rate[rateCount++];
            }
        }

        #region 定数値

        private const string C_PREFIX_POS = "POS:";
        private const string C_PREFIX_ROT = "ROT:";
        private const string C_PREFIX_RATE = "RATE:";
        private const string C_PREFIX_WAIT = "WAIT:";
        private const int C_SCALE = 1000;

        #endregion

        #region メンバフィールド

        private readonly SinTable sin = SinTable.Instance;	// 角度を求める
        private readonly List<Subject> subjects = new List<Subject>();		// 被写体配列
        private CameraFlow cameraFlow;	// カメラのワークフロー
        private ITexture texBase;
        private TextureLoader textureLoader = new TextureLoader();
        private readonly ConbineTexture conbineTexture = new ConbineTexture();

        private ICounter rot;
        private ICounter rate;
        private int speed = 1;
        private int count;		// 現在のフレームカウント

        #endregion

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public CameraWork(string filename, int column, int row)
        {
            textureLoader.LoadDefFile(filename);
            textureLoader.IsDefRelativePath = false;
            List<List<ITexture>> mat = new List<List<ITexture>>();

            int i = 0;
            for (int j = 0; j < column; ++j)
            {
                List<ITexture> imgRow = new List<ITexture>();
                for (int k = 0; k < row; ++k)
                {
                    imgRow.Add(textureLoader.GetTexture(i++));
                }
                mat.Add(imgRow);
            }
            conbineTexture.SetTextureMatrix(mat);
        }

        #region IDisposable メンバ

        public void Dispose()
        {
            YamalibUtility.Dispose(textureLoader);
        }

        #endregion

        /// <summary>
        /// カメラワークの設定フローを読み込みます
        /// </summary>
        /// <param name="filename"></param>
        public void LoadFlow(string filename)
        {
            CSVReader reader = new CSVReader();
            reader.Read(filename);
            cameraFlow = new CameraFlow();

            foreach (List<string> lineData in reader.CsvData)
            {
                if (lineData.Count == 0)
                {
                    continue;
                }

                ICounter counter;
                long start, end;
                int step;

                string linebuf = lineData[0];

                switch (linebuf)
                {
                    case C_PREFIX_POS:
                        cameraFlow.X.Add(Convert.ToInt32(lineData[1]));
                        cameraFlow.Y.Add(Convert.ToInt32(lineData[2]));
                        break;

                    case C_PREFIX_ROT:
                        counter = new FreeLineCounter();
                        // start
                        start = Convert.ToInt32(lineData[1]);

                        // end
                        end = Convert.ToInt32(lineData[2]);

                        // stap
                        step = Convert.ToInt32(linebuf[3]);

                        counter.Set((int)(start), (int)(end), step);

                        cameraFlow.RotPoints.Add(cameraFlow.X.Count - 1);
                        cameraFlow.Rot.Add(counter);
                        break;

                    case C_PREFIX_RATE:
                        counter = new FreeLineCounter();
                        // start
                        start = Convert.ToInt32(lineData[1]);

                        // end
                        end = Convert.ToInt32(lineData[2]);

                        // stap
                        step = Convert.ToInt32(lineData[3]);

                        counter.Set((int)(start), (int)(end), step);

                        cameraFlow.RatePoints.Add(cameraFlow.X.Count - 1);
                        cameraFlow.Rate.Add(counter);
                        break;

                    default:
                        break;
                }
            }
        }

        /// 背景画像を設定します
        public void SetBaseTexture(ITexture t)
        {
            texBase = t;
        }

        /// 再生スピードを設定する
        public void SetSpeed(int n)
        {
            this.speed = n;
        }

        /// <summary>
        /// カメラフローが終了したか
        /// </summary>
        public bool IsEnd
        {
            get
            {
                return cameraFlow.X.Count - 1 <= count;
            }
        }

        /// <summary>
        /// 移動処理を行う
        /// </summary>
        /// <param name="screen"></param>
        public void OnMove(IScreen screen)
        {
            for (int i = 0; i < this.speed; ++i)
            {
                if (InnerOnMove(screen))
                {
                    break;
                }
            }
        }

        /// <summary>
        /// 描画処理を行う
        /// </summary>
        /// <param name="screen"></param>
        public void OnDraw(IScreen screen)
        {
            if (cameraFlow == null) return;

            int x, y;
            x = cameraFlow.X[count];
            y = cameraFlow.Y[count];
            float rate = 1.0f;
            int rot = 0;

            if (!(null == this.rate))
            {
                rate = ((float)this.rate.Value()) / C_SCALE;
                //printf("Rate %f\n", Rate);
            }

            if (!(null == this.rot))
            {
                rot = this.rot.Value() / C_SCALE;
            }

            // 背景の描画
            conbineTexture.OnDrawRotateFix(screen, x, y, rot, rate);

            // ここでのx,yは中心点であるから、左上座標に変換する
            int top = (int)(y - 240);
            int left = (int)(x - 320);
            foreach (Subject s in subjects)
            {
                screen.BltRotate(s.Texture,
                    (int)((s.X - x) * rate),
                    (int)((s.Y - y) * rate),
                    rot, rate, 0);
                //printf("sx %d, sy%d, left:%d, top:%d\n", s.x, left, s.y, top);
            }
        }

        #region 非公開メンバ

        /// <summary>
        /// 繰り返す内部処理
        /// </summary>
        /// <param name="screen"></param>
        /// <returns></returns>
        private bool InnerOnMove(IScreen screen)
        {
            if (cameraFlow == null || IsEnd) return true;

            int x, y;

            x = cameraFlow.X[count];
            y = cameraFlow.Y[count];

            // rotateの検索
            for (int i = 0; i < cameraFlow.RotPoints.Count; ++i)
            {
                if (cameraFlow.RotPoints[i] == count)
                {
                    rot = cameraFlow.NextRot();
                    break;
                }
            }

            // rateの検索
            for (int i = 0; i < cameraFlow.RatePoints.Count; ++i)
            {
                if (cameraFlow.RatePoints[i] == count)
                {
                    rate = cameraFlow.NextRate();
                    //Log.print("new Rate");
                    break;
                }
            }

            // 回転移動
            if (rot != null)
            {
                rot.Inc();
            }

            // 拡大移動
            if (rate != null)
            {
                rate.Inc();
            }

            // カウンタ増分
            if (cameraFlow.X.Count - 1 > count)
            {
                ++count;
            }
            else
            {
                return true;
            }

            return false;
        }

        #endregion

    }
}
