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

namespace Yamalib.Draw
{
    public class ConbineTexture
    {
        #region 定数値

        private const int LC_ATAN_RAD = (65535 / 512);
        private const int LC_SIZE = 512;

        #endregion

        #region フィールドメンバ

        private readonly SinTable sin = SinTable.Instance;
        private List<List<ITexture>> texMat;
        //private bool bMod = false;
        private float rr = 0.0f;

        #endregion

        /// 2次元配列のテクスチャをもとに、一枚のテクスチャと見なす
        public void SetTextureMatrix(List<List<ITexture>> mat)
        {
            texMat = mat;
        }

        /// 描画処理	
        public void OnDraw(IScreen screen, int x, int y)
        {
            if (texMat == null) return;

            int h = 0, w = 0;
            foreach (List<ITexture> texs in texMat)
            {
                foreach (ITexture t in texs)
                {

                    // 描画
                    screen.Blt(t, x + w, y + h);

                    w += (int)t.Width;
                }
                w = 0;
                h += (int)texs[0].Height;
            }

        }

        /// 縮小拡大、回転ありバージョン x,yは画像の中心が描画される位置固定...
        public void OnDrawRotate(IScreen screen, int x, int y, int rad, float rate)
        {

            if (rate <= 0.0f) return;

            bool rot = (bool)((rad % 512) != 0);
            int baseRad;

            //if (rr == Rate)
            //{
            //    bMod = false;
            //}
            //else
            //{
            //    bMod = true;
            //}
            rr = rate;

            float w = GetWidth() * rate;
            float h = GetHeight() * rate;

            // 拡縮後の画像サイズより描画開始位置を計算
            int startX = (int)(x - (w / 2));
            int startY = (int)(y - (h / 2));

            // 残念な呼び出しされている...
            if (1.0f == rate && !rot)
            {
                OnDraw(screen, startX, startY);
                return;
            }

            int r = (int)Dist(startX, startY, x, y);

            if (rot)
            {
                baseRad = 512 - (sin.Atan(-(int)(w / 2), (int)(h / 2)) >> 7);
                startX = x + sin.Cos(baseRad + rad, r);
                startY = y + sin.Sin(baseRad + rad, r);
            }

            float dh = 0, dw = 0;
            int sh = 0;
            foreach (List<ITexture> texs in texMat)
            {
                foreach (ITexture t in texs)
                {
                    screen.BltRotate(t, (int)(startX + dw),
                                            (int)(startY + dh), rad, rate, 0);

                    if (rot)
                    {
                        dw += sin.Cos(rad, (int)(t.Width * rate));
                        dh += sin.Sin(rad, (int)(t.Width * rate));
                    }
                    else
                    {
                        dw += (int)(t.Width * rate);
                    }
                    //if (bMod)				
                    //printf("sx %d, sy%d  dw %d, dh %d, cnt %d \n ", startX, startY, dw, dh, ++cnt);
                }
                if (rot)
                {
                    sh = (int)(texs[0].Height * rate);

                    startX -= sin.Sin(rad, sh);
                    startY += sin.Cos(rad, sh);

                    dw = 0.0f;
                    dh = 0.0f;
                }
                else
                {
                    dw = 0.0f;
                    dh += (int)(texs[0].Height * rate);
                }
            }

        }

        /// 縮小拡大、回転ありバージョン x,yは画像の中心が描画される位置固定...
        public void OnDrawRotateFix(IScreen screen, int x, int y, int rad, float rate)
        {
            if (rate <= 0.0f) return;
            bool rot = (bool)((rad % 512) != 0);
            int baseRad;

            // 残念な呼び出しされている...
            if (1.0f == rate && !rot)
            {
                //			return;
            }

            int startX;
            int startY;
            int sh = 0;
            //int ad = 0;
            //bool fst = true;
            //float tmpF;

            int ix, iy;
            ITexture curTex = null;
            int tpx = (int)((x % LC_SIZE) * rate);
            int tpy = (int)((y % LC_SIZE) * rate);
            int dh = 0, dw = 0;
            int sxHalf = screen.Width / 2;
            int syHalf = screen.Height / 2;

            ix = x / LC_SIZE;
            iy = y / LC_SIZE;


            // 今描画したい点を基点として周囲の情報描画する
            startX = sxHalf - tpx;
            startY = syHalf - tpy;

            // 現在の対象画像の描画開始点までの距離を求める
            int r = (int)Dist(startX, startY, sxHalf, syHalf);
            if (rot)
            {
                baseRad = 512 - (sin.Atan((int)(startX - sxHalf), (int)(syHalf - startY)) >> 7);
                startX = sxHalf + sin.Cos(baseRad + rad, r);
                startY = syHalf + sin.Sin(baseRad + rad, r);
            }

            // 左上に遡って描画
            for (iy = y / LC_SIZE; iy >= 0; --iy)
            {
                for (ix = x / LC_SIZE; ix >= 0; --ix)
                {
                    curTex = texMat[iy][ix];
                    screen.BltRotate(curTex, (int)(startX + dw), (int)(startY + dh),
                                            rad, rate, 0);
                    if (rot)
                    {
                        dw -= sin.Cos(rad, (int)(curTex.Width * rate));
                        dh -= sin.Sin(rad, (int)(curTex.Width * rate));
                    }
                    else
                    {
                        dw -= (int)(curTex.Width * rate);
                    }
                }
                if (rot)
                {
                    sh = (int)(curTex.Height * rate);

                    startX += sin.Sin(rad, sh);
                    startY -= sin.Cos(rad, sh);

                    dw = 0;
                    dh = 0;
                }
                else
                {
                    dw = 0;
                    dh -= (int)(curTex.Height * rate);
                }
            }

            startX = sxHalf - tpx;
            startY = syHalf - tpy;
            startX += (int)(curTex.Width * rate);
            dw = 0;
            dh = 0;

            r = (int)Dist(startX, startY, sxHalf, syHalf);
            if (rot)
            {
                baseRad = 512 - (sin.Atan((int)(startX - sxHalf), (int)(syHalf - startY)) >> 7);
                startX = sxHalf + sin.Cos(baseRad + rad, r);
                startY = syHalf + sin.Sin(baseRad + rad, r);
            }

            // 右上に遡って描画
            for (iy = y / LC_SIZE; iy >= 0; --iy)
            {
                for (ix = x / LC_SIZE + 1; ix < texMat[iy].Count; ++ix)
                {
                    curTex = texMat[iy][ix];
                    screen.BltRotate(curTex, (int)(startX + dw), (int)(startY + dh),
                                            rad, rate, 0);
                    if (rot)
                    {
                        dw += sin.Cos(rad, (int)(curTex.Width * rate));
                        dh += sin.Sin(rad, (int)(curTex.Width * rate));
                    }
                    else
                    {
                        dw += (int)(curTex.Width * rate);
                    }
                }
                if (rot)
                {
                    sh = (int)(curTex.Height * rate);

                    startX += sin.Sin(rad, sh);
                    startY -= sin.Cos(rad, sh);

                    dw = 0;
                    dh = 0;
                }
                else
                {
                    dw = 0;
                    dh -= (int)(curTex.Height * rate);
                }
            }

            startX = sxHalf - tpx;
            startY = syHalf - tpy;
            startX += (int)(LC_SIZE * rate);
            startY += (int)(LC_SIZE * rate);
            dw = 0;
            dh = 0;
            r = (int)Dist(startX, startY, sxHalf, syHalf);
            if (rot)
            {
                baseRad = 512 - (sin.Atan((int)(startX - sxHalf), (int)(syHalf - startY)) >> 7);
                startX = sxHalf + sin.Cos(baseRad + rad, r);
                startY = syHalf + sin.Sin(baseRad + rad, r);
            }

            // 右下に描画
            for (iy = y / LC_SIZE + 1; iy < texMat.Count; ++iy)
            {
                for (ix = x / LC_SIZE + 1; ix < texMat[iy].Count; ++ix)
                {
                    curTex = texMat[iy][ix];
                    screen.BltRotate(curTex, (int)(startX + dw), (int)(startY + dh),
                                            rad, rate, 0);
                    if (rot)
                    {
                        dw += sin.Cos(rad, (int)(curTex.Width * rate));
                        dh += sin.Sin(rad, (int)(curTex.Width * rate));
                    }
                    else
                    {
                        dw += (int)(curTex.Width * rate);
                    }
                }
                if (rot)
                {
                    sh = (int)(curTex.Height * rate);

                    startX -= sin.Sin(rad, sh);
                    startY += sin.Cos(rad, sh);

                    dw = 0;
                    dh = 0;
                }
                else
                {
                    dw = 0;
                    dh += (int)(curTex.Height * rate);
                }
            }

            startX = sxHalf - tpx;
            startY = syHalf - tpy;
            startY += (int)(LC_SIZE * rate);
            dw = 0;
            dh = 0;
            r = (int)Dist(startX, startY, sxHalf, syHalf);
            if (rot)
            {
                baseRad = 512 - (sin.Atan((int)(startX - sxHalf), (int)(syHalf - startY)) >> 7);
                startX = sxHalf + sin.Cos(baseRad + rad, r);
                startY = syHalf + sin.Sin(baseRad + rad, r);
            }
            // 左下に描画
            for (iy = y / LC_SIZE + 1; iy < texMat.Count; ++iy)
            {
                for (ix = x / LC_SIZE; ix >= 0; --ix)
                {
                    curTex = texMat[iy][ix];
                    screen.BltRotate(curTex, (int)(startX + dw), (int)(startY + dh),
                                            rad, rate, 0);
                    if (rot)
                    {
                        dw -= sin.Cos(rad, (int)(curTex.Width * rate));
                        dh -= sin.Sin(rad, (int)(curTex.Width * rate));
                    }
                    else
                    {
                        dw -= (int)(curTex.Width * rate);
                    }
                }
                if (rot)
                {
                    sh = (int)(curTex.Height * rate);

                    startX -= sin.Sin(rad, sh);
                    startY += sin.Cos(rad, sh);

                    dw = 0;
                    dh = 0;
                }
                else
                {
                    dw = 0;
                    dh += (int)(curTex.Height * rate);
                }
            }
        }

        /// 仮想テクスチャの幅を返却する
        public float GetWidth()
        {

            if (texMat == null) return 0.0f;

            float w = 0.0f;
            foreach (ITexture t in texMat[0])
            {
                w += t.Width;
            }

            return w;
        }

        /// 仮想テクスチャの高さを返却する
        public float GetHeight()
        {
            if (texMat == null) return 0.0f;

            float h = 0.0f;
            foreach (List<ITexture> texs in texMat)
            {
                h += (int)texs[0].Height;
            }

            return h;
        }

        /// 2点間の距離を返す
        private static double Dist(double x1, double y1, double x2, double y2)
        {
            return System.Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
        }
    }
}
