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

namespace Yamalib.Draw.Effect
{
    public class ScatteredTexture
    {

        /// <summary>
        /// 壊れるオブジェクト
        /// １クラスで１パーティクル
        /// </summary>
        public class BreakObject
        {
            public ITexture t;
            public readonly Rect rect = new Rect();	//!< 描画範囲
            public int posx;	//!< 描画位置
            public int posy;	//!< 描画位置
            public int maxSize; //!< 自分の最大サイズ（矩形の斜辺長）
            public bool death;
            public bool enable;	//!< 分解されるオブジェクトであるか

            public RootCounter angle;	//!< 回転速度
            public RootCounterS dx;	//!< 移動量
            public RootCounterS dy;	//!< 移動量

            /// このオブジェクトを明示的に殺す
            public void Kill()
            {
                death = true;
            }

            /// 単純オブジェクトの動作
            public void OnMove()
            {

                if (death) return;

                dx++;
                dy++;
                angle++;
                posx += dx.Value();
                posy += dy.Value();

                if (posx < -maxSize || posx > 640 + maxSize)
                {
                    death = true;
                    return;
                }
                if (posy < -maxSize || posy > 480 + maxSize)
                {
                    death = true;
                    return;
                }
            }

            /// 単純オブジェクトの描画
            public void OnDraw(IScreen screen)
            {
                if (death) return;

                int height = (int)(rect.Height / 2);
                int width = (int)(rect.Width / 2);
                screen.BltRotate(t, posx, posy, rect, angle.Value(), 1.0f, width, height);
            }

            /// コンストラクタ
            public BreakObject()
            {
                angle = new RootCounter();
                angle.Set(0, 0, 1);
                dx = new RootCounterS();
                dx.Set(0, 0, 1);
                dy = new RootCounterS();
                dy.Set(0, 0, 1);
                enable = true;
            }

        }

        #region 定数値

        private const int ANGLE_RAND = 5;
        private const int X_RAND = 3;
        private const int Y_RAND = 5;

        #endregion

        #region フィールドメンバ

        private Rand rand;
        private bool finish;
        private ITexture tex;

        private bool fade;
        private int fadeStep;
        private RootCounterS alpha;

        private int waitCount;

        private int posx;
        private int posy;
        private readonly List<BreakObject> objects = new List<BreakObject>();

        #endregion

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public ScatteredTexture()
        {
            rand = new Rand();
            rand.Randomize();
            alpha = new RootCounterS();
            alpha.Set(0, 0, 1);
        }

        /// 分解するテクスチャ
        public void SetTexture(ITexture t_)
        {
            tex = t_;
        }

        /// 表示位置
        public void SetXY(int x, int y)
        {
            posx = x;
            posy = y;
        }

        /// 待機時間の設定
        public void SetWaitCount(int u)
        {
            waitCount = u;
        }

        /// フェードの設定
        public void SetFade(bool fade_, int step/*=4*/)
        {
            fade = fade_;
            fadeStep = step;
            alpha.Set(255, 0, step);
        }

        /// 現在の対象テクスチャの取得
        public ITexture GetTexture()
        {
            return tex;
        }

        /// 分解待機状態か
        public bool IsWait()
        {
            return (bool)(waitCount > 0);
        }

        /// 終了したか
        public bool IsFinish()
        {
            return finish;
        }

        /// 分解描画中
        public bool IsBreaking()
        {
            return (bool)(waitCount == 0);
        }

        /// 状態のクリア
        public void Clear()
        {
            objects.Clear();
            finish = false;
            alpha.Set(255, 0, fadeStep);
        }

        /// マスクデータを使用し、sx, sy単位で分割を行う
        /*
            アルファ付きサーフェイスから、アルファを参照し、非０の座標を有効な
            部分を判定し、分割する
        */
        public void DivisionTextureWithSurface(Surface surface, int divWidth, int divHeight)
        {
            if (surface == null) return;

            RectF[][] rects;
            List<RectF> rect1Dim;
            // 分割矩形を取得
            rects = ApartRegion.ApartFromTexture(surface, divWidth, divHeight);

            //assert( !(rects is null) );

            rect1Dim = ApartRegion.UniteRect(rects, 3, 10);

            //Log.print("divResult : %s \n", rect1Dim.length);

            // 矩形を巡回し、パーティクルを生成する
            foreach (RectF rc in rect1Dim)
            {
                BreakObject bo = new BreakObject();
                SetBreakObjAttribute(bo, rc.Width, rc.Height, rc.left, rc.top);
                bo.enable = rc.enable;
                objects.Add(bo);
            }
        }

        /// column × row に分割する
        public void DivisionTexture(int column, int row) {
		if (tex == null) return;
        objects.Clear();

		int width = (int) tex.Width;
		int height = (int) tex.Height;
		float aWidth = width / column;
		float aHeight = height / row;
		
		for(float w=0.0f,h=0.0f; ; ) {
			BreakObject bo = new BreakObject();
			
			// 単位矩形の指定から、BreakObjectの属性を設定する
			SetBreakObjAttribute(bo, aWidth, aHeight, w, h);

			objects.Add(bo);

			w += aWidth;
			if(!((int)w<width)) {
				w = 0.0f;
				h += aHeight;
				if ((int)h>height) break;
			}
		}
	}

        /// 内部パーティクル情報の取得
        public List<BreakObject> GetObjectInfo()
        {
            return objects;
        }

        /// 毎回呼び出すなり
        public void OnMove(IScreen screen)
        {
            if (waitCount > 0)
            {
                waitCount--;
                return;
            }

            if (finish) return;

            if (fade) alpha++;

            bool invoke = false;
            foreach (BreakObject o in objects)
            {
                if (!o.death && o.enable)
                {
                    o.OnMove();
                    invoke = true;
                }
            }

            if (!invoke)
            {
                finish = true;
            }
        }

        /// 毎回呼び出すなり
        public void OnDraw(IScreen screen)
        {
            if (finish) return;
            if (fade)
            {
                screen.SetColor(255, 255, 255, alpha.Value());
            }
            foreach (BreakObject o in objects)
            {
                if (!o.death)
                    o.OnDraw(screen);
            }
            if (fade)
            {
                screen.ResetColor();
            }
        }



        #region 非公開メンバ

        /// <summary>
        /// BreakObject の属性を設定する
        /// </summary>
        /// <param name="bo">設定するBreakObject</param>
        /// <param name="unitWidth">単位幅</param>
        /// <param name="unitHeight">単位高</param>
        /// <param name="w">走査位置、ｘ</param>
        /// <param name="h">走査位置、y</param>
        private void SetBreakObjAttribute(BreakObject bo, float unitWidth, float unitHeight, float w, float h)
        {
            // 自身全体画像の幅
            int width = (int)tex.Width;
            // 自身全体画像の高さ
            int height = (int)tex.Height;
            // 符号変数
            int abs = 1;
            // 飛び散る係数
            float keisu = 1.0f;

            // 底辺を求める
            float bottom = h + unitHeight;

            // 底辺が画像高さを超えている場合は修正する
            if ((int)bottom > height)
            {
                bottom = height;
            }

            // テクスチャのセット
            bo.t = tex;

            // 矩形の設定
            bo.rect.Left = w;
            bo.rect.Top = h;
            bo.rect.Right = bo.rect.Left + unitWidth;
            bo.rect.Bottom = bottom;

            // 斜辺の長さを求める
            float wid = bo.rect.Width;
            float hei = bo.rect.Height;

            bo.maxSize = (int)System.Math.Sqrt(wid * wid + hei * hei);

            // アトリビュートの設定
            bo.posx = posx + (int)bo.rect.Left;
            bo.posy = posy + (int)bo.rect.Top;

            // 自分の位置が画像の左に位置するならば
            if ((int)w < (width / 2))
            {
                abs = -1;
                wid = bo.rect.Left - width / 2;
                keisu = wid / (width / 2);
            }
            else
            {
                abs = 1;
                wid = width / 2 - bo.rect.Left;
                keisu = wid / (width / 2);
            }

            //----- 散らばる処理アトリビュート -----
            // 横方向の移動		
            int xStart = (3 + rand.GetRand(3)) * abs;
            int xStep = -(12 + rand.GetRand(5));

            // 下方向の移動
            int yEnd = 4 + rand.GetRand(2);
            int yStep = -(5 + rand.GetRand(5));

            // 角回転速度
            int angleStep = 5 + rand.GetRand(15);

            bo.dx.Set(xStart, 0, xStep);
            bo.dy.Set(0, yEnd, yStep);
            bo.angle.Set(0, 511, angleStep);
        }


        #endregion
    }
}
