/* vim: ts=4 sw=4 sts=4 :
 * =============================================================================
 *  SDL sample  for  D
 * =============================================================================
 */

import std.stdio;
import std.string;

import sdl.c.sdl;
import sdl.c.sdl_image;


/**
 * MAIN.
 *
 * Params:
 *  args = 起動引数
 */
int main(char[][] args)
{

	SDLImage sdlimg = new SDLImage();
	sdlimg.showImages();
	eventLoop();

	return 0;
}


/**
 * イベントループ.
 */
void eventLoop()
{
	int quit = 0;
	SDL_Event event;

	/* unicode 変換を有効にする. */
	SDL_EnableUNICODE(1);

	while (!quit)
	{
		while (SDL_PollEvent(&event))
		{
			switch (event.type)
			{
				/* SDL_QUIT イベント (ウィンドウが閉じられた) */
				case SDL_QUIT:
					quit = 1;
					break;

				default:
					break;
			}
		}
	}
}


/**
 * SDLImage.
 * あくまでサンプル, 構成変でスミマセン.
 */
class SDLImage
{
	/**
	 * 静的コンストラクタ.
	 *
	 * SDL_Init を実行し, SDL を初期化しています.
	 * SDL_Init 実行後, まだ初期化していないサブシステムを
	 * 初期化する場合は, SDL_InitSubSytem を使用する必要があります.
	 */
	static this()
	{
		int ret = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
		if (ret < 0)
		{	// SDL初期化失敗
			string errmsg = getErrorMessage();
			throw new SDLException("can't init sdl. " ~ errmsg);
		}
	}


	/**
	 * 静的デストラクタ.
	 * static this() が正常に完了している場合にのみ,
	 * プログラム終了時に呼び出されます.
	 * 本デストラクタにて, SDL終了処理を実行しています.
	 */
	static ~this()
	{
		SDL_Quit();
	}


	/**
	 * コンストラクタ.
	 * ビデオ表示初期化等を実施します.
	 */
	public
	this()
	{
		screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);
		if (screen is null)
		{	// Video Mode 設定失敗
			string errmsg = getErrorMessage();
			throw new SDLException("can't set video mode. " ~ errmsg);
		}
	}


	/**
	 * イメージ表示.
	 */
	public
	void showImages()
	{
		SDL_Rect pos;
		pos.x = 0;
		pos.y = 0;
		pos.h = 50;
		pos.w = 200;
		showImage("sample/resource/sample.bmp", pos);

		pos.y += 50;
		showImage("sample/resource/sample.png", pos);

		pos.y += 50;
		showImage("sample/resource/sample.jpg", pos);

		pos.y += 50;
		showImage("sample/resource/sample.gif", pos);

		pos.y += 50;
		showImage("sample/resource/sample.tif", pos);
	}


	/**
	 * 現在発生している SDL エラーメッセージを取得します.
	 *
	 * Returns:
	 *  エラーメッセージ
	 */
	public static
	string getErrorMessage()
	{
		const char* errmsg = SDL_GetError();
		return std.string.toString(errmsg);
	}


	/**
	 * イメージ表示.
	 * Params:
	 *  screen = スクリーン
	 *  file   = イメージファイル
	 *  pos    = 表示位置
	 */
	private
	void showImage(string file, SDL_Rect pos)
	{
		// イメージのロード.
		// SDL_LoadBMP : BMPをロード.
		// IMG_Load    : いろんな形式をロード. SDL_image が必要.
		SDL_Surface* image = IMG_Load(
			cast(char*) std.string.toStringz(file));
		if (image is null)
		{	// イメージロードエラー.
			string errmsg = getErrorMessage();
			throw new SDLException("can't load image. " ~ errmsg);
		}

		// 画像がパレット表現なら, そのパレットを使う.
		if ((image.format.palette !is null) && (this.screen.format.palette))
		{
			SDL_SetColors(this.screen,
				image.format.palette.colors,
				0,
				image.format.palette.ncolors);
		}

		// 高速 Blit 転送
		// 参照: http://www.tacoworks.jp/software/SDLdoc-jp/html/sdlblitsurface.html
		int ret = SDL_BlitSurface(image, null, this.screen, &pos);
		if (ret < 0)
		{	// 転送失敗
			string errmsg = getErrorMessage();
			throw new SDLException("can't blit. " ~ errmsg);
		}

		// 指定領域の更新
		SDL_UpdateRect(this.screen, pos.x, pos.y, image.w, image.h);

		// サーフェス開放
		SDL_FreeSurface(image);
	}


	private
	{
		SDL_Surface* screen;
	}

}



/**
 * SDL Exception.
 * エラー発生時に throw する.
 */
class SDLException : Exception
{
	/**
	 * 指定されたエラーメッセージを持つ, SDLException を構築します.
	 *
	 * Params:
	 *   msg  = エラーメッセージ
	 *   next = Exception
	 */
    this(string msg, Throwable next = null)
    {
        super(msg, next);
    }
}

