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

namespace Yamalib.Draw.Effect
{
    /// <summary>
    /// 波紋を描画できるテクスチャクラス
    /// ただし、テクスチャサイズを越えて波紋を描画することはできません。
    /// </summary>
    public class RipplingTexture
    {
        #region 定数値

        private const int WEIGHT = 8;
        private const int WEIGHTO = WEIGHT - 4;
        private const int HSIZE = 4;
        private const int ZM = 256;	//ZMは波紋の精度です。2のべき乗で表せる数にすると処理が速くなります。

        // 座標間の距離を表します
        private static readonly int[][] POW;

        #endregion

        #region メンバフィールド

        private int m_w;
        private int m_h;
        private bool alpha;
        private Surface surfaceOrg;	//!< 画像のオリジナル
        private Surface surface;
        private ITexture texture;	// 画面に表示するためのテクスチャ

        // 波紋の情報
        private int[] m_pixelsWaveInfo;
        // 波紋の前の状態
        private int[] pbOldState;
        private int[] pmOldState;

        #endregion

        /// <summary>
        /// 静的コンストラクタ
        /// </summary>
        static RipplingTexture()
        {
            POW = new int[HSIZE * 2][];
            for (int x = 0; x < POW.Length; x++)
            {
                POW[x] = new int[HSIZE * 2];
                for (int y = 0; y < POW.Length; y++)
                {
                    long tmp = (x - HSIZE) * (x - HSIZE) + (y - HSIZE) * (y - HSIZE);
                    POW[x][y] = (int)System.Math.Sqrt(tmp);
                }
            }
        }

        /// <summary>
        ///  コンストラクタ
        /// </summary>
        public RipplingTexture()
        {
            texture = YamalibConst.CreateTexture();
        }

        unsafe public void OnMove(IScreen screen)
        {

            if (surface == null) return;

            int py;
            int curIndex;

            MoveRipple();

            for (int y = 1; y < m_h - 1; y++)
            {
                // その行の先頭インデックス
                py = m_w * y;
                for (int x = 1; x < m_w - 1; x++)
                {
                    // 現在ピクセルのインデックス番号
                    curIndex = py + x;
                    int r, g, b;
                    if (alpha)
                    {
                        Sdl.SDL.SDL_Color* srcPixels = (Sdl.SDL.SDL_Color*)surfaceOrg.Pixels;
                        Sdl.SDL.SDL_Color* dstPixels = (Sdl.SDL.SDL_Color*)surface.Pixels;

                        // もともとの色素とそこの波情報                        
                        r = srcPixels[curIndex].r + (m_pixelsWaveInfo[curIndex] >> 8);
                        g = srcPixels[curIndex].g + (m_pixelsWaveInfo[curIndex] >> 8);
                        b = srcPixels[curIndex].b + (m_pixelsWaveInfo[curIndex] >> 8);

                        // これが実際に表示する配列
                        dstPixels[curIndex].r = (byte)(r > 255 ? 255 : (r < 0 ? 0 : r));
                        dstPixels[curIndex].g = (byte)(g > 255 ? 255 : (g < 0 ? 0 : g));
                        dstPixels[curIndex].b = (byte)(b > 255 ? 255 : (b < 0 ? 0 : b));
                        dstPixels[curIndex].unused = srcPixels[curIndex].unused;
                    }
                    else
                    {
                        Sdl.SDL.SDL_Color* srcPixels = (Sdl.SDL.SDL_Color*)surfaceOrg.Pixels;
                        Sdl.SDL.SDL_Color* dstPixels = (Sdl.SDL.SDL_Color*)surface.Pixels;

                        // もともとの色素とそこの波情報                        
                        r = srcPixels[curIndex].r + (m_pixelsWaveInfo[curIndex] >> 8);
                        g = srcPixels[curIndex].g + (m_pixelsWaveInfo[curIndex] >> 8);
                        b = srcPixels[curIndex].b + (m_pixelsWaveInfo[curIndex] >> 8);

                        // これが実際に表示する配列
                        dstPixels[curIndex].r = (byte)(r > 255 ? 255 : (r < 0 ? 0 : r));
                        dstPixels[curIndex].g = (byte)(g > 255 ? 255 : (g < 0 ? 0 : g));
                        dstPixels[curIndex].b = (byte)(b > 255 ? 255 : (b < 0 ? 0 : b));
                    }
                }
            }
        }

        public ITexture GetTexture()
        {
            if (surface == null) return null;

            //// テクスチャにセット
            // このメソッドはサーフェイスの所有権を放棄しない
            texture.SetSurface(surface);

            return texture;
        }

        /// 波紋を生成する
        public void CreateWave(int mx, int my, int sz)
        {
            if (surface == null)
            {
                return;
            }

            int r;
            for (int x = -HSIZE; x < HSIZE; x++)
            {
                for (int y = -HSIZE; y < HSIZE; y++)
                {

                    r = POW[x + HSIZE][y + HSIZE];
                    if (r <= HSIZE)
                    {
                        //距離に応じて波の強さを減少
                        m_pixelsWaveInfo[GetOff(x + mx, y + my)] = sz >> r;
                    }	// if

                }	// for
            }	// for
        }

        /// サーフェイスの設定
        public void SetSurface(Surface surface_)
        {
            if (surface_ == null)
            {
                //Log.printError("RipplingTexture#SetSurface : surface is null!!");
                return;
            }

            // オリジナルサーフェイス
            surfaceOrg = surface_;

            // サーフェイスの幅と高さを控えておく		
            m_w = surface_.Width;
            m_h = surface_.Height;
            alpha = surface_.Alpha;

            if (surface != null)
            {
                surface.Release();
            }
            else
            {
                surface = new Surface();
            }

            surface.CreateDIB(m_w, m_h, alpha);
            // サーフェイスのコピー
            surface.Blt(surfaceOrg, 0, 0);

            // 一発サーフェイスを生成しておく
            texture.SetSurface(surface);

            // 波紋作業用スペースの確保
            m_pixelsWaveInfo = new int[m_w * m_h];
            pbOldState = new int[m_w * m_h];
            pmOldState = new int[m_w * m_h];
        }

        #region 非公開メソッド

        /// X,Y座標系から配列インデックスを求める
        private int GetOff(int x, int y)
        {
            if (x < 0 || x >= m_w - 1)
                x = 0;
            if (y < 0 || y >= m_h - 1)
                y = 0;
            return y * m_w + x;
        }

        private void MoveRipple()
        {
            const int BORDER = 58 * ZM;
            int f;
            int py;
            int curIndex;
            for (int y = 1; y < m_h - 1; y++)
            {
                // その行の先頭インデックス
                py = m_w * y;
                for (int x = 1; x < m_w - 1; x++)
                {
                    // 現在ピクセルのインデックス番号
                    curIndex = py + x;

                    if (m_pixelsWaveInfo[curIndex] == 0
                    && pbOldState[curIndex - m_w] == 0
                    && m_pixelsWaveInfo[curIndex + m_w] == 0
                    && m_pixelsWaveInfo[curIndex + 1] == 0
                    && pbOldState[curIndex - 1] == 0)
                        continue;

                    f = pmOldState[curIndex] +
                            ((m_pixelsWaveInfo[curIndex] * WEIGHTO   //中
                              + pbOldState[curIndex - m_w]        //1dot上
                              + m_pixelsWaveInfo[curIndex + m_w]  //1dot下
                              + m_pixelsWaveInfo[curIndex + 1]    //1dot右
                              + pbOldState[curIndex - 1]          // 1dot左
                                )
                             >> 3); // 足した色素の平均

                    // ここで波紋の減衰率が決定される
                    f -= (f >> 7);

                    pmOldState[curIndex] = f - (pbOldState[curIndex] = m_pixelsWaveInfo[curIndex]);
                    //                m_pixelsWaveInfo[curIndex] = f;

                    if (f > BORDER)
                    {
                        m_pixelsWaveInfo[curIndex] = BORDER;
                    }
                    else
                    {
                        m_pixelsWaveInfo[curIndex] = f;
                    }

                }
            }
        }



        #endregion


    }
}
