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

namespace Yamalib.Yanesdkext.Draw
{
    /// <summary>
    /// yaneSDK2ndにあったTransBltter
    /// </summary>
    public static class TransBltter
    {
        #region 定数値

        public static readonly int PAHSE_MAX = 255;

        #endregion

        /// <summary>
        /// メソッド呼び出しのデリゲート
        /// </summary>
        /// <param name="screen">スクリーン</param>
        /// <param name="Texture">ソーステクスチャー</param>
        /// <param name="X">描画位置X</param>
        /// <param name="Y">描画位置Y</param>
        /// <param name="phase">位相</param>
        private delegate void transBltter(IScreen screen, ITexture texture, int x, int y, int phase);
        private static readonly transBltter[] transfunc = {
		        TransBltter.BltSlitCurtain1,	// 0
		        TransBltter.BltSlitCurtain2,	// 1
		        TransBltter.BltSlitCurtain3,	// 2
		        TransBltter.BltSlitCurtain4,	// 3
		        TransBltter.BltSlitCurtain5,	// 4
		        TransBltter.BltSlitCurtain6,	// 5
		        TransBltter.BltSlitCurtain7,	// 6
		        TransBltter.BltSlitCurtain8,	// 7
		        TransBltter.BltCircle1,		// 8
		        TransBltter.BltCircle2,		// 9
		        TransBltter.BltCircle3,		// 10
		        TransBltter.BltCircle4,		// 11
		        TransBltter.BltCircle5,		// 12
		        TransBltter.BltWhorl1,			// 13
		        TransBltter.BltWhorl2,			// 14
		        TransBltter.BltWhorl3,			// 15
		        TransBltter.BltWhorl4,			// 16
		        TransBltter.BltWhorl5,			// 17
		        TransBltter.BltWhorl6,			// 18
		        TransBltter.BltFadeBlt,		// 19
		        TransBltter.BltSlitCurtain9,	// 20
		        TransBltter.BltSlitCurtain10,	// 21
		        TransBltter.BltSlitCurtain11,	// 22
		        TransBltter.BltSlitCurtain12,	// 23
	        };


        /// <summary>
        /// 遷移描画を行う
        /// </summary>
        /// <param name="no">エフェクト番号</param>
        /// <param name="dst">描画先Screen</param>
        /// <param name="src">ソーステクスチャー</param>
        /// <param name="X">描画位置X</param>
        /// <param name="Y">描画位置Y</param>
        /// <param name="phase">位相</param>
        /// <returns>常に０</returns>
        public static int Blt(int no, IScreen dst, ITexture src, int x, int y, int phase)
        {	//	番号で呼び出せるのだ
            if (no >= transfunc.Length) return 1; // 範囲外
            transfunc[no](dst, src, x, y, phase);
            return 0;
        }

        ///	左からカーテン。16 dot。
        private static void BltSlitCurtain1(IScreen dst, ITexture src, int x, int y, int phase)
        { BltTransHelper1(dst, src, x, y, phase, true, 16); }

        ///	右からカーテン。16 dot。
        private static void BltSlitCurtain2(IScreen dst, ITexture src, int x, int y, int phase)
        { BltTransHelper1(dst, src, x, y, phase, false, 16); }

        ///	左からカーテン。8 dot。
        private static void BltSlitCurtain3(IScreen dst, ITexture src, int x, int y, int phase)
        { BltTransHelper1(dst, src, x, y, phase, true, 8); }

        ///	右からカーテン。8 dot。
        private static void BltSlitCurtain4(IScreen dst, ITexture src, int x, int y, int phase)
        { BltTransHelper1(dst, src, x, y, phase, false, 8); }

        ///	左からカーテン（FadeBlt）。128 dot。
        private static void BltSlitCurtain9(IScreen dst, ITexture src, int x, int y, int phase)
        { BltTransHelper10(dst, src, x, y, phase, true, 128); }

        ///	右からカーテン（FadeBlt）。128 dot。
        private static void BltSlitCurtain10(IScreen dst, ITexture src, int x, int y, int phase)
        { BltTransHelper10(dst, src, x, y, phase, false, 128); }

        ///	上からカーテン（FadeBlt）。128 dot。
        private static void BltSlitCurtain11(IScreen dst, ITexture src, int x, int y, int phase)
        { BltTransHelper11(dst, src, x, y, phase, true, 128); }

        ///	下からカーテン（FadeBlt）。128 dot。
        private static void BltSlitCurtain12(IScreen dst, ITexture src, int x, int y, int phase)
        { BltTransHelper11(dst, src, x, y, phase, false, 128); }

        ///	上からのカーテン。16 dot。
        private static void BltSlitCurtain5(IScreen dst, ITexture src, int x, int y, int phase)
        { BltTransHelper2(dst, src, x, y, phase, true, 16); }

        ///	下からのカーテン。16 dot。
        private static void BltSlitCurtain6(IScreen dst, ITexture src, int x, int y, int phase)
        { BltTransHelper2(dst, src, x, y, phase, false, 16); }

        ///	上からのカーテン。8 dot。
        private static void BltSlitCurtain7(IScreen dst, ITexture src, int x, int y, int phase)
        { BltTransHelper2(dst, src, x, y, phase, true, 8); }

        ///	下からのカーテン。8 dot。
        private static void BltSlitCurtain8(IScreen dst, ITexture src, int x, int y, int phase)
        { BltTransHelper2(dst, src, x, y, phase, false, 8); }

        ///	上からの円
        private static void BltCircle1(IScreen dst, ITexture src, int x, int y, int phase)
        {
            if (null == dst || null == src) return;
            int sx = (int)src.Width; int sy = (int)src.Height;

            int sm;	// max Size
            sm = (int)(global::System.Math.Sqrt((float)sx * sx / 4 + sy * sy) * 2);
            int sr;	// rest Size
            sr = (sm * phase) >> 8;
            int ssy, py;
            ssy = 0;
            for (py = 0; py < (sr >> 1); py++, ssy++)
            {
                int px, rx;
                rx = (int)(global::System.Math.Sqrt((float)sr * sr / 4 - ssy * ssy) * 2);
                px = (sx >> 1) - (rx >> 1);

                //		bltRect(px,py,rx,1); // クリップ付きHLINE
                //	手動マクロ

                //	bltRect(x_,y_,sx_,sy_)
                int x_ = px, y_ = py, sx_ = rx, sy_ = 1;

                {	//	マクロだと思いねぇ
                    Rect rc = new Rect();
                    int sx2, sy2;
                    sx2 = sx_; sy2 = sy_;
                    int x2, y2;
                    x2 = x_; y2 = y_;
                    if (x2 < 0) { x2 = 0; }
                    if (y2 < 0) { y2 = 0; }
                    if (x2 + sx2 > sx) { sx2 = sx - x2; }
                    if (y2 + sy2 > sy) { sy2 = sy - y2; }
                    rc.SetRect(x2, y2, x2 + sx2, y2 + sy2);
                    dst.Blt(src, x + x2, y + y2, rc);
                }
            }
        }

        ///	下からの円
        private static void BltCircle2(IScreen dst, ITexture src, int x, int y, int phase)
        {
            if (null == dst || null == src) return;
            int sx = (int)src.Width; int sy = (int)src.Height;

            int sm;	// max Size
            sm = (int)(global::System.Math.Sqrt((float)sx * sx / 4 + sy * sy) * 2);
            int sr;	// rest Size
            sr = (sm * phase) >> 8;
            int ssy, py;
            ssy = 0;
            for (py = sy; py > sy - (sr >> 1); py--, ssy++)
            {
                int px, rx;
                rx = (int)(global::System.Math.Sqrt((float)sr * sr / 4 - ssy * ssy) * 2);
                px = (sx >> 1) - (rx >> 1);

                //		bltRect(px,py,rx,1); // クリップ付きHLINE
                //	手動マクロ

                //	bltRect(x_,y_,sx_,sy_)
                int x_ = px, y_ = py, sx_ = rx, sy_ = 1;

                {	//	マクロだと思いねぇ
                    Rect rc = new Rect();
                    int sx2, sy2;
                    sx2 = sx_; sy2 = sy_;
                    int x2, y2;
                    x2 = x_; y2 = y_;
                    if (x2 < 0) { x2 = 0; }
                    if (y2 < 0) { y2 = 0; }
                    if (x2 + sx2 > sx) { sx2 = sx - x2; }
                    if (y2 + sy2 > sy) { sy2 = sy - y2; }
                    rc.SetRect(x2, y2, x2 + sx2, y2 + sy2);
                    dst.Blt(src, x + x2, y + y2, rc);
                }
            }
        }

        ///	左からの円
        private static void BltCircle3(IScreen dst, ITexture src, int x, int y, int phase)
        {
            if (null == dst || null == src) return;
            int sx = (int)src.Width; int sy = (int)src.Height;

            int sm;	// max Size
            sm = (int)(global::System.Math.Sqrt((float)sx * sx + sy * sy / 4) * 2);
            int sr;	// rest Size
            sr = (sm * phase) >> 8;
            int ssy, py;
            ssy = -(sr >> 1);
            for (py = (sy >> 1) - (sr >> 1); py < ((sy >> 1) + (sr >> 1)); py++, ssy++)
            {
                int px, rx;
                rx = (int)(global::System.Math.Sqrt((float)sr * sr / 4 - ssy * ssy));
                px = 0;

                //		bltRect(px,py,rx,1); // クリップ付きHLINE
                //	手動マクロ

                //	bltRect(x_,y_,sx_,sy_)
                int x_ = px, y_ = py, sx_ = rx, sy_ = 1;

                {	//	マクロだと思いねぇ
                    Rect rc = new Rect();
                    int sx2, sy2;
                    sx2 = sx_; sy2 = sy_;
                    int x2, y2;
                    x2 = x_; y2 = y_;
                    if (x2 < 0) { x2 = 0; }
                    if (y2 < 0) { y2 = 0; }
                    if (x2 + sx2 > sx) { sx2 = sx - x2; }
                    if (y2 + sy2 > sy) { sy2 = sy - y2; }
                    rc.SetRect(x2, y2, x2 + sx2, y2 + sy2);
                    dst.Blt(src, x + x2, y + y2, rc);
                }
            }
        }

        ///	右からの円
        private static void BltCircle4(IScreen dst, ITexture src, int x, int y, int phase)
        {
            if (null == dst || null == src) return;
            int sx = (int)src.Width; int sy = (int)src.Height;

            int sm;	// max Size
            sm = (int)(global::System.Math.Sqrt((float)sx * sx + sy * sy / 4) * 2);
            int sr;	// rest Size
            sr = (sm * phase) >> 8;
            int ssy, py;
            ssy = -(sr >> 1);
            for (py = (sy >> 1) - (sr >> 1); py < ((sy >> 1) + (sr >> 1)); py++, ssy++)
            {
                int px, rx;
                rx = (int)(global::System.Math.Sqrt((float)sr * sr / 4 - ssy * ssy));
                px = sx - rx;

                //		bltRect(px,py,rx,1); // クリップ付きHLINE
                //	手動マクロ

                //	bltRect(x_,y_,sx_,sy_)
                int x_ = px, y_ = py, sx_ = rx, sy_ = 1;

                {	//	マクロだと思いねぇ
                    Rect rc = new Rect();
                    int sx2, sy2;
                    sx2 = sx_; sy2 = sy_;
                    int x2, y2;
                    x2 = x_; y2 = y_;
                    if (x2 < 0) { x2 = 0; }
                    if (y2 < 0) { y2 = 0; }
                    if (x2 + sx2 > sx) { sx2 = sx - x2; }
                    if (y2 + sy2 > sy) { sy2 = sy - y2; }
                    rc.SetRect(x2, y2, x2 + sx2, y2 + sy2);
                    dst.Blt(src, x + x2, y + y2, rc);
                }
            }
        }

        ///	中央からの円
        private static void BltCircle5(IScreen dst, ITexture src, int x, int y, int phase)
        {
            if (null == dst || null == src) return;
            int sx = (int)src.Width; int sy = (int)src.Height;

            int sm;	// max Size
            sm = sx + sy;
            int sr;	// rest Size
            sr = (sm * phase) >> 8;
            int ssy, py;
            ssy = -(sr >> 1);
            for (py = (sy >> 1) - (sr >> 1); py < ((sy >> 1) + (sr >> 1)); py++, ssy++)
            {
                int px, rx;
                rx = (int)(global::System.Math.Sqrt((float)sr * sr / 4 - ssy * ssy) * 2); // bug-fixed '00/02/24
                px = (sx >> 1) - (rx >> 1);

                //		bltRect(px,py,rx,1); // クリップ付きHLINE
                //	手動マクロ

                //	bltRect(x_,y_,sx_,sy_)
                int x_ = px, y_ = py, sx_ = rx, sy_ = 1;

                {	//	マクロだと思いねぇ
                    Rect rc = new Rect();
                    int sx2, sy2;
                    sx2 = sx_; sy2 = sy_;
                    int x2, y2;
                    x2 = x_; y2 = y_;
                    if (x2 < 0) { x2 = 0; }
                    if (y2 < 0) { y2 = 0; }
                    if (x2 + sx2 > sx) { sx2 = sx - x2; }
                    if (y2 + sy2 > sy) { sy2 = sy - y2; }
                    rc.SetRect(x2, y2, x2 + sx2, y2 + sy2);
                    dst.Blt(src, x + x2, y + y2, rc);
                }
            }
        }

        private static readonly int[] WHORL11_UZU = {
	         0, 1, 2, 3,
	        11,12,13, 4,
	        10,15,14, 5,
	         9, 8, 7, 6
        };
        ///	うずまき 外から 2*2
        private static void BltWhorl1(IScreen dst, ITexture src, int x, int y, int phase)
        {
            BltTransHelper3(dst, src, x, y, phase, 2, WHORL11_UZU);
        }

        private static readonly int[] WHORL12_UZU = {
	         6, 7, 8, 9,
	         5, 0, 1,10,
	         4, 3, 2,11,
	        15,14,13,12
        };
        ///	うずまき 内から 2*2
        private static void BltWhorl2(IScreen dst, ITexture src, int x, int y, int phase)
        {
            BltTransHelper3(dst, src, x, y, phase, 2, WHORL12_UZU);
        }

        private static readonly int[] WHORL13_UZU = {
	         0, 1, 2, 3,
	        11,12,13, 4,
	        10,15,14, 5,
	         9, 8, 7, 6
        };
        ///	うずまき 外から 4*4
        private static void BltWhorl3(IScreen dst, ITexture src, int x, int y, int phase)
        {
            BltTransHelper3(dst, src, x, y, phase, 4, WHORL13_UZU);
        }

        private static readonly int[] WHORL14_UZU = {
	         6, 7, 8, 9,
	         5, 0, 1,10,
	         4, 3, 2,11,
	        15,14,13,12
        };
        ///	うずまき 内から 4*4
        private static void BltWhorl4(IScreen dst, ITexture src, int x, int y, int phase)
        {
            BltTransHelper3(dst, src, x, y, phase, 4, WHORL14_UZU);
        }

        private static readonly int[] WHORL15_UZU = {
	         0, 1, 2, 3,
	        11,12,13, 4,
	        10,15,14, 5,
	         9, 8, 7, 6
        };
        ///	うずまき 外から 8*8
        private static void BltWhorl5(IScreen dst, ITexture src, int x, int y, int phase)
        {

            BltTransHelper3(dst, src, x, y, phase, 8, WHORL15_UZU);
        }

        private static readonly int[] WHORL16_UZU = {
	         6, 7, 8, 9,
	         5, 0, 1,10,
	         4, 3, 2,11,
	        15,14,13,12
        };
        ///	うずまき 内から 8*8
        private static void BltWhorl6(IScreen dst, ITexture src, int x, int y, int phase)
        {
            BltTransHelper3(dst, src, x, y, phase, 8, WHORL16_UZU);
        }

        // --------------- 以下はhelper -------------------------------------------

        /// 左右からのカーテン。
        /**
	        SlitCurtainBltから呼び出されるヘルパ関数。
	        bLeft == trueならば左から。falseなら右からのカーテン。
        */
        private static void BltTransHelper1(IScreen dst, ITexture src, int x, int y, int phase, bool bLeft, int width)
        {
            if (null == dst || null == src) return;
            int sx = (int)src.Width; int sy = (int)src.Height;

            int ColumnNum = (sx + width + 1) / width;
            int c = (phase * (ColumnNum + width)) / 256;
            int i, j;
            Rect rc = new Rect();

            rc.Top = 0;
            rc.Bottom = sy;
            for (i = 0; i < ColumnNum && i < c; ++i)
            {
                j = c - i;
                if (j > width)
                    j = width;
                rc.Left = i * width;
                if (bLeft)
                {
                    rc.Right = rc.Left + j;
                }
                else
                {
                    rc.Right = sx - rc.Left;
                    rc.Left = rc.Right - j;
                }
                if (rc.Right > sx)
                    rc.Right = sx - 1;
                if (rc.Left < 0)
                    rc.Left = 0;

                dst.Blt(src, (int)(x + rc.Left), y, rc);
            }
        }

        /// 上下のカーテン。
        /**
	        SlitCurtainBltから呼び出されるヘルパ関数。
	        bTop == trueならば上から。falseなら下からのカーテン。
        */
        private static void BltTransHelper2(IScreen dst, ITexture src, int x, int y, int phase, bool bTop, int height)
        {

            if (null == dst || null == src) return;
            int sx = (int)src.Width; int sy = (int)src.Height;

            int ColumnNum = (sy + height + 1) / height;
            int c = (phase * (ColumnNum + height)) / 256;
            int i, j;
            Rect rc = new Rect();

            rc.Left = 0;
            rc.Right = sx;
            for (i = 0; i < ColumnNum && i < c; ++i)
            {
                j = c - i;
                if (j > height)
                    j = height;
                rc.Top = i * height;
                if (bTop)
                {
                    rc.Bottom = rc.Top + j;
                }
                else
                {
                    rc.Bottom = sy - rc.Top;
                    rc.Top = rc.Bottom - j;
                }
                if (rc.Bottom > sy)
                    rc.Bottom = sy - 1;
                if (rc.Top < 0)
                    rc.Top = 0;

                dst.Blt(src, x, (int)(y + rc.Top), rc);
            }
        }

        ///	うずまきetc..
        /**
	        uzuは4*4の配列。使いかたはbltWhorl1を参考にすること。
        */
        private static void BltTransHelper3(IScreen dst, ITexture src, int x, int y, int phase, int a, int[] uzu)
        {
            if (null == dst || null == src) return;
            int sx = (int)src.Width; int sy = (int)src.Height;

            phase >>= 4;
            for (int py = 0, py2 = 0; py < sy; py += a, ++py2)
            {
                for (int px = 0, px2 = 0; px < sx; px += a, ++px2)
                {
                    if (uzu[(px2 & 3) + ((py2 & 3) << 2) & 15] <= phase)
                    {

                        //		bltRect(px,py,a,a); // クリップ付きHLINE
                        //	手動マクロ

                        //	bltRect(x_,y_,sx_,sy_)
                        int x_ = px, y_ = py, sx_ = a, sy_ = a;

                        {	//	マクロだと思いねぇ
                            Rect rc = new Rect();
                            int sx2, sy2;
                            sx2 = sx_; sy2 = sy_;
                            int x2, y2;
                            x2 = x_; y2 = y_;
                            if (x2 < 0) { x2 = 0; }
                            if (y2 < 0) { y2 = 0; }
                            if (x2 + sx2 > sx) { sx2 = sx - x2; }
                            if (y2 + sy2 > sy) { sy2 = sy - y2; }
                            rc.SetRect(x2, y2, x2 + sx2, y2 + sy2);
                            dst.Blt(src, x + x2, y + y2, rc);
                        }

                    }
                }
            }
        }

        /// 左右からのカーテン（FadeBltter）。
        /**
	        SlitCurtainBltから呼び出されるヘルパ関数。
	        bLeft == trueならば左から。falseなら右からのカーテン。
        */

        private static void BltTransHelper10(IScreen dst, ITexture src, int x, int y, int phase, bool bLeft, int width)
        {
            if (null == dst || null == src) return;

            if (phase == 0) return;

            if (phase == 255)
            {
                dst.Blt(src, x, y);
                return;
            }

            Color4ub color = YanesdkUtil.GetNowScreenColor(dst);
            int sx = (int)src.Width;
            int sy = (int)src.Height;

            float alpha = 0.0f;
            // 現在のphase値における描画確定位置の端を求める
            int c = (int)(((sx + width) / 255.0) * phase);
            float aAlpha = 255.0f / (float)width;
            Rect rc = new Rect();

            rc.Top = 0;
            rc.Bottom = sy;
            if (bLeft)
            {
                rc.Left = 0;
                rc.Right = c - width;
            }
            else
            {
                rc.Left = sx - c + width;
                rc.Right = sx;
            }

            // 255 部分描画
            dst.SetColor(color.R, color.G, color.B, 255);
            if (rc.Left < rc.Right)
            {
                dst.Blt(src, (int)(x + rc.Left), y, rc);
            }

            // fade部分描画
            int i;
            for (i = 0, alpha = 255.0f; i < width; ++i, alpha -= aAlpha)
            {

                if (bLeft)
                {
                    rc.Left = rc.Right;
                    rc.Right = rc.Left + 1;

                    if (rc.Right > sx) break;
                }
                else
                {
                    rc.Right = rc.Left;
                    rc.Left = rc.Right - 1;

                    if (rc.Left < 0) break;
                }
                dst.SetColor(color.R, color.G, color.B, (int)alpha);
                dst.Blt(src, (int)(x + rc.Left), y, rc);
            }

            dst.SetColor(color);
        }

        /// 上下からのカーテン（FadeBltter）。
        /**
	        SlitCurtainBltから呼び出されるヘルパ関数。
	        bLeft == trueならば左から。falseなら右からのカーテン。
        */

        private static void BltTransHelper11(IScreen dst, ITexture src, int x, int y, int phase, bool bTop, int height)
        {
            if (null == dst || null == src) return;

            if (phase == 0) return;

            if (phase == 255)
            {
                dst.Blt(src, x, y);
                return;
            }

            Color4ub colorOrg = YanesdkUtil.GetNowScreenColor(dst);
            int sx = (int)src.Width;
            int sy = (int)src.Height;

            float alpha;
            int c = (int)(((sy + height) / 255.0) * phase);
            float aAlpha = 255.0f / (float)height;
            Rect rc = new Rect();

            rc.Left = 0;
            rc.Right = sx;
            if (bTop)
            {
                rc.Top = 0;
                rc.Bottom = c - height;
            }
            else
            {
                rc.Top = sy - c + height;
                rc.Bottom = sy;
            }

            dst.SetColor(colorOrg.R, colorOrg.G, colorOrg.B, 255);
            if (rc.Top <= rc.Bottom)
            {
                dst.Blt(src, x, (int)(y + rc.Top), rc);
            }

            int i;
            for (i = 0, alpha = 255.0F; i < height; ++i, alpha -= aAlpha)
            {

                if (bTop)
                {
                    rc.Top = rc.Bottom;
                    rc.Bottom = rc.Top + 1;

                    if (rc.Bottom > sy) break;
                }
                else
                {
                    rc.Bottom = rc.Top;
                    rc.Top = rc.Bottom - 1;
                    if (rc.Top < 0) break;
                }
                dst.SetColor(colorOrg.R, colorOrg.G, colorOrg.B, (int)alpha);
                dst.Blt(src, x, (int)(y + rc.Top), rc);
            }
            dst.SetColor(colorOrg);

        }

        /// 単純フェードアウト描画（互換性の為に導入
        private static void BltFadeBlt(IScreen dst, ITexture src, int x, int y, int phase)
        {
            if (phase == 0) return;

            Color4ub color = YanesdkUtil.GetNowScreenColor(dst); ;
            dst.SetColor(color.R, color.G, color.B, phase);
            dst.Blt(src, x, y);
            dst.SetColor(color);
        }

    }
}
