using System;
using System.Collections.Generic;
using System.Text;
using Yanesdk.Draw;

namespace Yamalib.Draw
{

    /** フラグを持つRect */
    public class RectF
    {
        public float left;	//!< 左
        public float top;	//!< 上
        public float right;	//!< 右
        public float bottom;	//!< 下
        public bool enable;	//<! 有効かどうか（これを使うかは任意）

        public RectF()
        {
            left = .0f;
            top = .0f;
            right = .0f;
            bottom = .0f;
            enable = false;
        }

        ///	各データを設定する
        public void SetRect(float left_, float top_, float right_, float bottom_)
        {
            left = left_;
            top = top_;
            right = right_;
            bottom = bottom_;
        }

        /// 単純Rectとの互換
        public void SetRect(Rect rc)
        {
            left = rc.Left;
            top = rc.Top;
            right = rc.Right;
            bottom = rc.Bottom;
        }

        /// 幅を取得する
        /**
            left - right をしているだけ。符号がマイナスになる場合もありうる。
        */
        public float Width
        {
            get { return right - left; }
        }


        ///	高さを取得する
        /**
            bottom - top をしているだけ。符号がマイナスになる場合もありうる。
        */
        public float Height
        {
            get { return bottom - top; }
        }

        /// 単純Rectへのビューを返す
        public Rect GetRect()
        {
            Rect rc = new Rect();
            rc.SetRect(left, top, right, bottom);
            return rc;
        }
    }

    /// <summary>
    /// リージョンを矩形分解するクラス
    /// 描画エフェクトに使うと便利である
    /// 
    /// ・通常の描画
    /// ・描画矩形のマスク
    /// で分割描画可能
    /// </summary>
    public static class ApartRegion
    {
        /// <summary>
        /// アルファ付きテクスチャから分解矩形を生成します
        /// 結果は二次元配列として返却します(二次元であれば分割された縦横サイズがわかる)
        /// </summary>
        /// <param name="surface"></param>
        /// <param name="divWidth"></param>
        /// <param name="divHeight"></param>
        /// <returns></returns>
        public static RectF[][] ApartFromTexture(Surface surface, int divWidth, int divHeight)
        {
            if (null == surface) return null;

            Rect rc = new Rect();

            int width = (int)surface.Width;
            int height = (int)surface.Height;
            // 分解するサイズ
            int sx;
            int sy;

            sx = width / divWidth;
            sy = height / divHeight;

            // サンプルとする矩形二次元配列を作る
            //RectF[][] rcSample = new RectF[][sy];
            RectF[][] rcSample = new RectF[sy][];
            for (int i = 0; i < rcSample.Length; i++)
            {
                rcSample[i] = new RectF[sx];
                for (int j = 0; j < rcSample[i].Length; j++)
                {
                    rcSample[i][j] = new RectF();
                }
            }

            //debug {
            //    Log.print("rcSample.length:%s\n", rcSample.length);
            //    Log.print("rcSample[].length:%s\n", rcSample[].length);
            //}

            // サンプリング枠となる矩形データを作る
            for (int i = 0; i < sy; ++i)
            {
                for (int j = 0; j < sx; ++j)
                {
                    rcSample[i][j].left = j * divWidth;
                    rcSample[i][j].right = rcSample[i][j].left + divWidth;
                    rcSample[i][j].top = i * divHeight;
                    rcSample[i][j].bottom = rcSample[i][j].top + divHeight;
                }
            }



            // 画像と比較し有効なデータを検索、有効な矩形だけの配列を作る
            for (int i = 0; i < sy; ++i)
            {
                for (int j = 0; j < sx; ++j)
                {
                    // 矩形が有効画像に内に含まれている
                    if (ContainRect(surface, rcSample[i][j]))
                    {
                        // フラグを持つRectなのだー
                        rcSample[i][j].enable = true;
                    }
                }
            }

            return rcSample;
        }

        /// 矩形の四隅のピクセルがアルファ値０でなければtrue
        unsafe private static bool ContainRect(Surface surface, RectF rc)
        {
            int width = (int)surface.Width;
            int height = (int)surface.Height;
            // ピクセルデータはRGBAでならんでるからこれでＯＫでそ？
            Sdl.SDL.SDL_Color* pixels = (Sdl.SDL.SDL_Color*)surface.Pixels;
            int x, y;

            // 左上
            x = (int)rc.left;
            y = (int)rc.top;
            if (pixels[(y * width) + x].unused == 0)
            {
                return false;
            }

            // 右上
            x = (int)rc.right;
            y = (int)rc.top;
            if (pixels[(y * width) + x].unused == 0)
            {
                return false;
            }

            // 左下
            x = (int)rc.left;
            y = (int)rc.bottom;
            if (pixels[(y * width) + x].unused == 0)
            {
                return false;
            }

            // 右下
            x = (int)rc.right;
            y = (int)rc.bottom;
            if (pixels[(y * width) + x].unused == 0)
            {
                return false;
            }

            return true;
        }

        /// 二次元配列を結合する
        /// 結合した結果の配列は１異次元配列となる
        public static List<RectF> UniteRect(RectF[][] rects, int uniteX, int uniteY)
        {
            int sx = rects[0].Length;
            int sy = rects.Length;
            List<RectF> rectResult = new List<RectF>(); ;
            bool[][] flags;

            // こないしないとサイズとれへんのか？
            sx = sy = 0;
            foreach (RectF[] rectLine in rects)
            {
                sy++;
                foreach (RectF rc in rectLine)
                {
                    sx++;
                }
            }
            sx /= sy;
            //debug {
            //    Log.print("sx : %s\n", sx);
            //    Log.print("sy : %s\n", sy);
            //}

            //assert( (sy - uniteY) > 0 );
            //assert( (sx - uniteX) > 0 );

            //flags.Length = sy;
            flags = new bool[sy][];

            for (int j = 0; j < flags.Length; j++)
            {
                flags[j] = new bool[sx];
                for (int i = 0; i < flags[j].Length; i++)
                {
                    flags[j][i] = false;
                }
            }

            for (int i = 0; i < (sy - uniteY); ++i)
            {
                for (int j = 0; j < (sx - uniteX); ++j)
                {
                    // 結合できるんやろか？
                    if (IsUnitable(rects, flags, j, i, uniteX, uniteY))
                    {
                        // できるそうや...
                        RectF rc = new RectF(); ;

                        // 左上位置
                        rc.left = rects[i][j].left;
                        rc.top = rects[i][j].top;

                        // 右下位置
                        rc.right = rects[i + uniteY - 1][j + uniteX - 1].right;
                        rc.bottom = rects[i + uniteY - 1][j + uniteX - 1].bottom;

                        // 有効可否フラグ
                        rc.enable = rects[i][j].enable;

                        rectResult.Add(rc);

                        // 結合されたもののフラグをたてておく
                        FlagOn(flags, j, i, uniteX, uniteY);
                    }
                }
            }

            // 結合されなかったものをつけておく
            for (int i = 0; i < sy; ++i)
            {
                for (int j = 0; j < sx; ++j)
                {
                    if (!flags[i][j])
                    {
                        rectResult.Add(rects[i][j]);
                    }
                }
            }

            return rectResult;
        }


        /// 配列のposx,posyの位置からunitex,uniteyサイズの矩形は結合可能であるか？
        private static bool IsUnitable(RectF[][] rects, bool[][] flags, int posx, int posy, int uniteX, int uniteY)
        {
            int result = 0;

            for (int i = 0; i < uniteY; ++i)
            {
                for (int j = 0; j < uniteX; ++j)
                {

                    // この区画がすでに使用済みであれば結合失敗
                    if (flags[posy + i][posx + j])
                    {
                        return false;
                    }

                    if (rects[posy + i][posx + j].enable)
                    {
                        result += 1;
                    }
                }
            }

            // 結果が0(走査結果すべて固定), またはすべて有効ならば true
            return (bool)((result == 0) || (result == (uniteX * uniteY)));
        }

        /// 結合した区画のフラグをたてる
        private static void FlagOn(bool[][] flags, int posx, int posy, int uniteX, int uniteY)
        {
            for (int i = 0; i < uniteY; ++i)
            {
                for (int j = 0; j < uniteX; ++j)
                {
                    flags[posy + i][posx + j] = true;
                }
            }
        }


    }
}
