module yamalib.draw.texturekga;

private import SDL;
private import SDL_image;

private import std.stream;
private import std.path;


private import y4d_draw.texture;
private import y4d_draw.surface;
private import y4d_aux.filesys;

private import yamalib.log.log;
private import yamalib.log.performancelog;

struct Color24 {
	ubyte r,g,b;
}

/**
	RAWフォーマット画像読み込みに対応したTexture拡張クラス
*/
class TextureKGA {
	
	static const final uint KGAPREFIX = 0xF12DFB87;

	/// 既存の画像フォーマットから冗長のRGBA8888フォーマットを作る
	/// コンバート時、Littleとbigとで変換かけてRGBのならびに変更している
	static bool convertKGA(char[] filename) {

		Surface surface;
		scope MemoryStream mem;
		scope ubyte[] buffer;

		try {
			surface = new Surface();
			surface.load( filename );
			
			Log.print("KGA converting... %s", filename);
	
			if ( FileSys.isExist( addExt(filename, "kga") ) ) return true;
			
			assert ( !(surface is null) );
			
			mem = new MemoryStream();
			ubyte shiftr,shiftg,shiftb,shifta;
			uint rmask, gmask, bmask, amask;
			int width = cast(int) surface.getWidth();
			int height = cast(int) surface.getHeight();
			ubyte alpha = cast(ubyte) (surface.getSurface().format.BitsPerPixel==32 ? 1 : 0);
			bool error;
			bool fatal;
			
			rmask = surface.getSurface().format.Rmask;
			gmask = surface.getSurface().format.Gmask;
			bmask = surface.getSurface().format.Bmask;
			
			shiftr = getShiftSize( rmask );
			shiftg = getShiftSize( gmask );
			shiftb = getShiftSize( bmask );
	
			// アルファあるんやったらマスクを取得する		
			if (alpha) {
				amask = surface.getSurface().format.Amask;
				shifta = getShiftSize( amask );
			}
	
			// ファイルのヘッダにシグネチャを入れておく
	//		mem.write( KGAPREFIX );
			
			Log.print("width %s height %s alpah %s", width, height, alpha);
	
	retry:;
			try {		
				error = false;
							
				if (alpha) {
		
					// ピクセルデータはRGBAでならんでるからこれでＯＫでそ？
					SDL_Color* pixels = cast(SDL_Color*) surface.getPixels();
		
					for (int y; y < height; ++y) {
						for (int x; x < width; ++x) {
	
	version (LittleEndian) {
							uint* color = cast(uint*) &pixels[ (y * width) + x ];
							
							buffer ~= cast(ubyte) ((*color & rmask) >> shiftr);
							buffer ~= cast(ubyte) ((*color & gmask) >> shiftg);
							buffer ~= cast(ubyte) ((*color & bmask) >> shiftb);
							buffer ~= cast(ubyte) ((*color & amask) >> shifta);
							
	//						buffer ~= pixels[ (y * width) + x ].r;
	//						buffer ~= pixels[ (y * width) + x ].g;
	//						buffer ~= pixels[ (y * width) + x ].b;
	//						buffer ~= pixels[ (y * width) + x ].unused;
	
	} else version (BigEndian) {
							buffer ~= pixels[ (y * width) + x ].r;
							buffer ~= pixels[ (y * width) + x ].g;
							buffer ~= pixels[ (y * width) + x ].b;
							buffer ~= pixels[ (y * width) + x ].unused;
	}
	
						}
	
					}
				} else {
		
					Color24* pixels = cast(Color24*) surface.getPixels();
		
					for (int y; y < height; ++y) {
						for (int x; x < width; ++x) {
							uint color = *(cast(uint*) &pixels[ (y * width) + x ]);
	
	version (LittleEndian) {
		
							buffer ~= cast(ubyte) ((color & rmask) >> shiftr);
							buffer ~= cast(ubyte) ((color & gmask) >> shiftg);
							buffer ~= cast(ubyte) ((color & bmask) >> shiftb);
							
	//						buffer ~= pixels[ (y * width) + x ].r;
	//						buffer ~= pixels[ (y * width) + x ].g;
	//						buffer ~= pixels[ (y * width) + x ].b;
	
	} else version (BigEndian) {
							buffer ~= pixels[ (y * width) + x ].r;
							buffer ~= pixels[ (y * width) + x ].g;
							buffer ~= pixels[ (y * width) + x ].b;
	}
	
						}
					}
				}
				
			} catch (Exception e) {
				alpha = cast(ubyte) (alpha==1 ? 0 : 1);
				buffer = null;
				error = true;
				Log.printWarn("%s is unkown format. retry", filename);
			}
	
			if (error && !fatal) {
				fatal = true;
				goto retry;
			}
			
			if ( !error && !(buffer is null) ) {
	
				mem.write( width );
				mem.write( height );
				mem.write( alpha );
				
				// ファイル書き出し
				FileSys.write( addExt(filename, "kga"), mem.data() ~ buffer );
				
			} else {
				Log.printError("cannot convert KGA %s", filename);
			}
			
			return true;
	
		} catch {
			// なんらかのエラー
			return false;
		} finally {
			// 使用したオブジェクトの破棄
			if (mem) {
				mem.close();
			}
			mem = null;
			if (surface) {
				surface.release();
			}
			surface = null;
			buffer = null;
		}
		return true;
	}

	/// KGAフォーマットファイルからサーフェイスを生成して返却します
	/// ファイルは KGA 拡張子でなければならない
	static Surface load(char[] filename) {
		uint width;
		uint height;
		ubyte alpha;
		uint sig;
		
		scope ubyte[] data = cast(ubyte[]) FileSys.read(filename);


		if ( data is null ) {
			Log.printLook("TextureKGA load Error. Not Find %s", filename);
			return null;
		}

//		↓これはアーカイブ内は調べないからだめなのだ		
//		if ( !FileSys.isExist(filename) ) {
//			Log.printLook("TextureKGA load Error. Not Find %s", filename);
//			return null;
//		}

		// パフォーマンス測定開始
		PerformanceLog.abort(); // Texture クラスからの再帰呼び出し..がある
		PerformanceLog.logSoundLoadKGA(filename, null);
		
		scope MemoryStream mem = new MemoryStream( data );
		
		if (mem.size() < 8) return null;
		
//		mem.read(sig);
		// シグネチャチェック
//		if ( sig !=  KGAPREFIX) {
//			Log.printError("prefix is different! %d", sig);
//			return null;
//		}
		
		mem.read(width);
		mem.read(height);
		mem.read(alpha);
		
//--		test code
//		width=80 , height=150, alpha=1;

		Surface surface = new Surface();
		surface.createDIB(width , height, cast(bool) (alpha!=0));

		int py;
		int indexXY;
		if (alpha) {

			SDL_Color* pixels = cast(SDL_Color*) surface.getPixels();
			SDL_Color* srcPix = cast(SDL_Color*) &mem.data()[9];
			
			Log.printLook("32bit Surface %s", filename);

			for (int y; y < height; ++y) {
				py = width * y;
				for (int x; x < width; ++x) {
					indexXY = py + x;
					pixels[indexXY].r = srcPix[indexXY].r;
					pixels[indexXY].g = srcPix[indexXY].g;
					pixels[indexXY].b = srcPix[indexXY].b;
					pixels[indexXY].unused = srcPix[indexXY].unused;
				}
			}
		} else {
			
			Color24* srcPix = cast(Color24*) &mem.data()[9];
			Color24* pixels = cast(Color24*) surface.getPixels();
			Log.printLook("24bit Surface %s", filename);

			for (int y; y < height; ++y) {
				py = width * y;
				for (int x; x < width; ++x) {
					indexXY = py + x;
					pixels[indexXY].r = srcPix[indexXY].r;
					pixels[indexXY].g = srcPix[indexXY].g;
					pixels[indexXY].b = srcPix[indexXY].b;
				}
			}
		}
		
		mem.flush();
		mem.close();
		
		data = null;
		mem = null;
		
		// パフォーマンス測定終了
		PerformanceLog.endLog();

		return surface;
	}
	
	/// サーフェイスをコピーする
	static Surface copySurface(Surface surface_) {
		
		int width = cast(int) surface_.getWidth();
		int height = cast(int) surface_.getHeight();
		bool alpha = cast(bool) (surface_.getSurface().format.BitsPerPixel==32);

		// createDIBして転送したほうがはやいんでないかな...
		Surface surface = new Surface();
		surface.createDIB( width, height, alpha );

		surface.blt( surface_, 0, 0 );
		return surface;
/+		
		int py;
		int indexXY;
		if (alpha) {
			SDL_Color* pixels = cast(SDL_Color*) surface.getPixels();
			SDL_Color* srcPix = cast(SDL_Color*) surface_.getPixels();
			
			for (int y; y < height; ++y) {
				py = y * width;
				for (int x; x < width; ++x) {
					indexXY = py + x;
					pixels[indexXY].r 		= srcPix[indexXY].r;
					pixels[indexXY].g 		= srcPix[indexXY].g;
					pixels[indexXY].b 		= srcPix[indexXY].b;
					pixels[indexXY].unused 	= srcPix[indexXY].unused;
				}
			}
		} else {
			Color24* pixels = cast(Color24*) surface.getPixels();
			Color24* srcPix = cast(Color24*) surface_.getPixels();
			
			for (int y; y < height; ++y) {
				py = y * width;
				for (int x; x < width; ++x) {
					indexXY = py + x;
					pixels[indexXY].r = srcPix[indexXY].r;
					pixels[indexXY].g = srcPix[indexXY].g;
					pixels[indexXY].b = srcPix[indexXY].b;
				}
			}
		}
		
		return surface;
+/
	}

	/// KGAフォーマットからテクスチャを生成し返却します	
	static Texture createTexture(char[] filename) {
		
		Texture t = new Texture;
		t.setSurface( load(filename) );
		
		return t;
	}


private:

	/// マスクからシフトサイズを取得する
	static ubyte getShiftSize(uint mask) {
		switch (mask) {
			case 0xFF000000:
				return 24;
			case 0x00FF0000:
				return 16;
			case 0x0000FF00:
				return 8;
			case 0x000000FF:
				return 0;
			default:
				Log.printError("unkown mask");
				return 0;
		}
		assert(false);
	}
	
}