module yamalib.draw.ripplingtexture;

private import SDL_image;

private import std.math;

private import y4d_draw.surface;
private import y4d_draw.texture;
private import y4d_draw.screen;

private import y4d_math.rand;
private import y4d_math.round;

private import yamalib.draw.texturekga;
private import yamalib.log.log;


/// 波紋を描画できるテクスチャクラス
/**
	テクスチャ内において波紋を描画する。
	ただし、テクスチャを越えて波紋を描画することはできません。
*/
class RipplingTexture {
	
	void onMove(Screen screen) {
		
		if (surface is null) return;

        int f;
		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;
                
/+                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;
+/
		        int r, g, b, z, k;
		 		if (alpha) {
					SDL_Color* srcPixels = cast(SDL_Color*) surfaceOrg.getPixels();
					SDL_Color* dstPixels = cast(SDL_Color*) surface.getPixels();
				
/+					// 盛り上がっている様に見せる処理
					z = curIndex + (pbOldState[curIndex + m_w] - pbOldState[curIndex]) / ZM * m_w +
						(pbOldState[curIndex + 1] - pbOldState[curIndex]) / ZM;
					// はみ出た時の処理
					if(z >= m_w*m_h || z < 0)
						z = curIndex;

					k = m_pixelsWaveInfo[z] * 2 / ZM;

			        r = srcPixels[curIndex].r + k;
			        g = srcPixels[curIndex].g + k;
			        b = srcPixels[curIndex].b + k;
+/

			        // もともとの色素とそこの波情報                        
			        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 = cast(ubyte) ( r > 255 ? 255 : (r < 0 ? 0 : r) );
			        dstPixels[curIndex].g = cast(ubyte) ( g > 255 ? 255 : (g < 0 ? 0 : g) );
			        dstPixels[curIndex].b = cast(ubyte) ( b > 255 ? 255 : (b < 0 ? 0 : b) );
			        dstPixels[curIndex].unused = srcPixels[curIndex].unused;
		 		} else {
					Color24* srcPixels = cast(Color24*) surfaceOrg.getPixels();
					Color24* dstPixels = cast(Color24*) surface.getPixels();
		
/+					// 盛り上がっている様に見せる処理
					z = curIndex + (pbOldState[curIndex + m_w] - pbOldState[curIndex]) / ZM * m_w +
						(pbOldState[curIndex + 1] - pbOldState[curIndex]) / ZM;
					// はみ出た時の処理
					if(z >= m_w*m_h || z < 0)
						z = curIndex;
					k = m_pixelsWaveInfo[z] * 2 / ZM;

			        r = srcPixels[curIndex].r + k;
			        g = srcPixels[curIndex].g + k;
			        b = srcPixels[curIndex].b + k;
+/
			        // もともとの色素とそこの波情報                        
			        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 = cast(ubyte) ( r > 255 ? 255 : (r < 0 ? 0 : r) );
			        dstPixels[curIndex].g = cast(ubyte) ( g > 255 ? 255 : (g < 0 ? 0 : g) );
			        dstPixels[curIndex].b = cast(ubyte) ( b > 255 ? 255 : (b < 0 ? 0 : b) );
		        }
            }
        }
	}

	Texture getTexture() {

		if (surface is null) return null;
		
/+		int py;
		int curIndex;
		
		if (alpha) {
			SDL_Color* srcPixels = cast(SDL_Color*) surfaceOrg.getPixels();
			SDL_Color* dstPixels = cast(SDL_Color*) surface.getPixels();
			
	        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;
			        // もともとの色素とそこの波情報                        
	
			        r = srcPixels[curIndex].r + m_pixelsWaveInfo[curIndex] / ZM;
			        g = srcPixels[curIndex].g + m_pixelsWaveInfo[curIndex] / ZM;
			        b = srcPixels[curIndex].b + m_pixelsWaveInfo[curIndex] / ZM;
			
			        // これが実際に表示する配列
			        dstPixels[curIndex].r = ( r > 255 ? 255 : (r < 0 ? 0 : r) );
			        dstPixels[curIndex].g = ( g > 255 ? 255 : (g < 0 ? 0 : g) );
			        dstPixels[curIndex].b = ( b > 255 ? 255 : (b < 0 ? 0 : b) );
			        dstPixels[curIndex].unused = srcPixels[curIndex].unused;
	            }
	        }
		} else {
			Color24* srcPixels = cast(Color24*) surfaceOrg.getPixels();
			Color24* dstPixels = cast(Color24*) surface.getPixels();
		
	        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;
			        // もともとの色素とそこの波情報                        
	
			        r = srcPixels[curIndex].r + m_pixelsWaveInfo[curIndex] / ZM;
			        g = srcPixels[curIndex].g + m_pixelsWaveInfo[curIndex] / ZM;
			        b = srcPixels[curIndex].b + m_pixelsWaveInfo[curIndex] / ZM;
			
			        // これが実際に表示する配列
			        dstPixels[curIndex].r = ( r > 255 ? 255 : (r < 0 ? 0 : r) );
			        dstPixels[curIndex].g = ( g > 255 ? 255 : (g < 0 ? 0 : g) );
			        dstPixels[curIndex].b = ( b > 255 ? 255 : (b < 0 ? 0 : b) );
	            }
	        }
		}
 +/      
 
       // テクスチャにセット
		tex.subSurfaceFast( surface );
  
  		return tex;      
	}
	
    /// 波紋を生成する
    void createWave(int mx, int my, int sz) {
    	
    	if (surface is 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
    }    
	
	/// サーフェイスの設定
	void setSurface(Surface surface_) {
		if (surface_ is null) {
			Log.printError("RipplingTexture#setSurface : surface is null!!");
			return;
		}
		
		// オリジナルサーフェイス
		surfaceOrg = surface_;
		
		// サーフェイスの幅と高さを控えておく		
		m_w = surface_.getWidth();
		m_h = surface_.getHeight();
		alpha = cast(bool) (surface_.getSurface().format.BitsPerPixel==32);
		
		if ( !(surface is null) ) {
			surface.release();
		} else {
			surface = new Surface();
		}

		surface.createDIB(m_w, m_h, alpha);
		// サーフェイスのコピー
		surface.blt( surfaceOrg, 0, 0 );
		
		// 一発サーフェイスを生成しておく
		tex.setSurface( TextureKGA.copySurface(surface) );
		
		// 波紋作業用スペースの確保
		m_pixelsWaveInfo = new int[m_w*m_h];
		pbOldState = new int[m_w*m_h];
		pmOldState = new int[m_w*m_h];

	}

/*	
	/// 開放処理
	void release() {
		if ( !(surfaceOrg is null) ) {
			surfaceOrg.release();
		}
		if ( !(surface is null) ) {
			surface.release();
		}
	}
*/
	
	/// コンストラクタ	
	this() {
		tex = new Texture();
	}
	
	/// 静的コンストラクタ
	static this() {
        //座標間の長さを求める
        for (int x = 0; x < POW.length; x++) {
            for (int y = 0; y < POW.length; y++) {
            	real temp = (x - HSIZE) * (x - HSIZE) + (y - HSIZE) * (y - HSIZE);
                POW[x][y] = cast(int) std.math.sqrt( temp );
            }
        }
	}

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

    // 座標間の距離を表します
    static final int[HSIZE * 2][HSIZE * 2] POW;
    
    int m_w;	//!< 
    int m_h;
	bool alpha;
    Surface surfaceOrg;	//!< 画像のオリジナル
    Surface surface;
    Texture tex;	// 画面に表示するためのテクスチャ
    
    // 波紋の情報
    int[] m_pixelsWaveInfo;
	// 波紋の前の状態
    int[] pbOldState;
    int[] pmOldState;
    
    
    /// X,Y座標系から配列インデックスを求める
    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;
    }
    
    void moveRipple() {
    	static 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;
				}
				
            }
        }
    }
    
    

	
}