// memtest.cpp:
//  Tests blit to various surfaces. Blits a 100x100 bitmap in various
//  memory types to a same size bitmap in other memory types as many 
//  times as it can in one second (rounded up).
//  Compilation:
//    Compile using something like:
//       g++ memtest.cpp -lalleg -omemtest.exe
//    Optionally, you can pass -DGFX_MODE=GFX_AUTODETECT to change the
//    tested graphics mode (defaults to GFX_AUTODETECT).
//  (c) 2004 Ryan Patterson
//  Distributed under the zlib/libpng license

#include <allegro.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

#ifdef _WIN32
#	define vsnprintf _vsnprintf
	// For system specs
#	include <winalleg.h>
#endif

#define LOG_FILE "memtest.txt"
#define MAX_LINE 2048

#ifndef GFX_MODE
#	define GFX_MODE GFX_AUTODETECT
#endif

FILE* logfile = NULL;

void log(const char* fmt, ...)
{
	static char buffer[MAX_LINE];
	va_list va;

	// Prepare buffer
	va_start(va, fmt);
	vsnprintf(buffer, MAX_LINE, fmt, va);
	va_end(va);

	// Print message
	printf("%s", buffer);
	fprintf(logfile, "%s", buffer);

	// If allowed, broadcast message on screen
	if(SCREEN_W > 0 && SCREEN_H > 0 && screen)
	{
		textprintf_centre(screen, font,
			SCREEN_W / 2 - text_length(font, buffer) / 2,
			SCREEN_H / 2 - text_height(font) / 2,
			makecol(255, 255, 255), "%s", buffer);
	}
}

int do_test(BITMAP* src, BITMAP* dest)
{
	clock_t end;
	int times = 0;

	end = clock() + CLOCKS_PER_SEC;
	while(clock() < end)
	{
		blit(src, dest, 0, 0, 0, 0, src->w, src->h);
		++times;
	}

	return times;
}

void do_set()
{
	BITMAP* one;
	BITMAP* two;
	int res;

	// Test mem->mem
	log("mem->mem: ");
	one = create_bitmap(100, 100);
	two = create_bitmap(100, 100);
	if(!one || !two)
	{
		log("Failed creating bitmaps. one = %p, two = %p\n", one, two);
		return;
	}
	res = do_test(one, two);
	log("%d blits/sec\n", res);

	// Test mem->sys
	log("mem->sys: ");
	one = create_bitmap(100, 100);
	two = create_system_bitmap(100, 100);
	if(!one || !two)
	{
		log("Failed creating bitmaps. one = %p, two = %p\n", one, two);
		return;
	}
	res = do_test(one, two);
	log("%d blits/sec\n", res);

	// Test mem->vid
	log("mem->vid: ");
	one = create_bitmap(100, 100);
	two = create_video_bitmap(100, 100);
	if(!one || !two)
	{
		log("Failed creating bitmaps. one = %p, two = %p\n", one, two);
		return;
	}
	res = do_test(one, two);
	log("%d blits/sec\n", res);

	// Test sys->mem
	log("sys->mem: ");
	one = create_system_bitmap(100, 100);
	two = create_bitmap(100, 100);
	if(!one || !two)
	{
		log("Failed creating bitmaps. one = %p, two = %p\n", one, two);
		return;
	}
	res = do_test(one, two);
	log("%d blits/sec\n", res);

	// Test sys->sys
	log("sys->sys: ");
	one = create_system_bitmap(100, 100);
	two = create_system_bitmap(100, 100);
	if(!one || !two)
	{
		log("Failed creating bitmaps. one = %p, two = %p\n", one, two);
		return;
	}
	res = do_test(one, two);
	log("%d blits/sec\n", res);

	// Test sys->vid
	log("sys->vid: ");
	one = create_system_bitmap(100, 100);
	two = create_video_bitmap(100, 100);
	if(!one || !two)
	{
		log("Failed creating bitmaps. one = %p, two = %p\n", one, two);
		return;
	}
	res = do_test(one, two);
	log("%d blits/sec\n", res);

	// Test vid->mem
	log("vid->mem: ");
	one = create_video_bitmap(100, 100);
	two = create_bitmap(100, 100);
	if(!one || !two)
	{
		log("Failed creating bitmaps. one = %p, two = %p\n", one, two);
		return;
	}
	res = do_test(one, two);
	log("%d blits/sec\n", res);

	// Test vid->sys
	log("vid->sys: ");
	one = create_video_bitmap(100, 100);
	two = create_system_bitmap(100, 100);
	if(!one || !two)
	{
		log("Failed creating bitmaps. one = %p, two = %p\n", one, two);
		return;
	}
	res = do_test(one, two);
	log("%d blits/sec\n", res);

	// Test vid->vid
	log("vid->vid: ");
	one = create_video_bitmap(100, 100);
	two = create_video_bitmap(100, 100);
	if(!one || !two)
	{
		log("Failed creating bitmaps. one = %p, two = %p\n", one, two);
		return;
	}
	res = do_test(one, two);
	log("%d blits/sec\n", res);
}

void test_bpp(int bpp)
{
	log("Testing blits at %d bpp...\n", bpp);
	set_color_depth(bpp);
	if(set_gfx_mode(GFX_MODE, 640, 480, 0, 0) != 0)
	{
		log("Failed to set graphics mode: %s\n", allegro_error);
		return;
	}
	do_set();
	log("\n");
	set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
}

int main(int argc, char* argv[])
{
	// Init
	allegro_init();
	logfile = fopen(LOG_FILE, "w+");
	if(!logfile)
	{
		printf("Unable to open log %s", LOG_FILE);
		return 1;
	}

	// Do tests
	test_bpp(8);
	test_bpp(15);
	test_bpp(16);
	test_bpp(24);
	test_bpp(32);

	// Log system specs
#	if _WIN32
		log("Compiled for ");
		OSVERSIONINFO osv;
		GetVersionEx(&osv);
#		define OS_VERSION(mjr, mnr, ver) \
			if(osv.dwMajorVersion == mjr && osv.dwMinorVersion == mnr) \
			{ log("%s%s", ver, osv.szCSDVersion); } else
		OS_VERSION(4, 0, "Windows 95")
		OS_VERSION(4, 10, "Windows 98")
		OS_VERSION(4, 90, "Windows ME")
		OS_VERSION(4, 0, "Windows 2000")
		OS_VERSION(5, 1, "Windows XP")
		{ log("Windows"); }
#		undef OS_VERSION
		log("\n");

		SYSTEM_INFO sys;
		GetSystemInfo(&sys);
		log("%d processors, page size %d\n", sys.dwNumberOfProcessors, sys.dwPageSize);
#	else
#		error "Unsupported OS. Please implement me!"
#	endif

	log("gfx_capabilities: ");
	if(set_gfx_mode(GFX_MODE, 640, 480, 0, 0) != 0)
	{
		log("Failed to set graphics mode: %s\n", allegro_error);
		goto end_of_gfxcaps;
	}
	if(gfx_capabilities == 0)
		log("None");
	else
	{
		char gfxcaps[MAX_LINE] = "";
#		define GFX_CAP(flag) if(gfx_capabilities & flag) strcat(gfxcaps, " | "#flag);
		GFX_CAP(GFX_CAN_SCROLL)
		GFX_CAP(GFX_CAN_TRIPLE_BUFFER)
		GFX_CAP(GFX_HW_CURSOR)
		GFX_CAP(GFX_HW_HLINE)
		GFX_CAP(GFX_HW_HLINE_XOR)
		GFX_CAP(GFX_HW_HLINE_SOLID_PATTERN)
		GFX_CAP(GFX_HW_HLINE_COPY_PATTERN)
		GFX_CAP(GFX_HW_FILL)
		GFX_CAP(GFX_HW_FILL_XOR)
		GFX_CAP(GFX_HW_FILL_SOLID_PATTERN)
		GFX_CAP(GFX_HW_FILL_COPY_PATTERN)
		GFX_CAP(GFX_HW_LINE)
		GFX_CAP(GFX_HW_LINE_XOR)
		GFX_CAP(GFX_HW_TRIANGLE)
		GFX_CAP(GFX_HW_TRIANGLE_XOR)
		GFX_CAP(GFX_HW_GLYPH)
		GFX_CAP(GFX_HW_VRAM_BLIT)
		GFX_CAP(GFX_HW_VRAM_BLIT_MASKED)
		GFX_CAP(GFX_HW_MEM_BLIT)
		GFX_CAP(GFX_HW_MEM_BLIT_MASKED)
		GFX_CAP(GFX_HW_SYS_TO_VRAM_BLIT)
		GFX_CAP(GFX_HW_SYS_TO_VRAM_BLIT_MASKED)
#		undef GFX_CAP
		log("%s", &gfxcaps[3]);
	}
	log("\n");
end_of_gfxcaps:

	log("Using Allegro %s\n", ALLEGRO_VERSION_STR);

	// Shutdown
	fclose(logfile);

	return 0;
}
END_OF_MAIN()

// The end
