// for al_set_new_bitmap_depth
#define ALLEGRO_UNSTABLE

#include <allegro5/allegro.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_primitives.h>
#include <stdio.h>

#define RED al_map_rgb(255, 0, 0)
#define LIGHT_BLUE al_map_rgb(128, 128, 255)
#define BLUE al_map_rgb(0, 0, 255)
#define DARK_BLUE al_map_rgb(0, 0, 192)
#define GREEN al_map_rgb(0, 255, 0)
#define DARK_GREEN al_map_rgb(0, 192, 0)
#define YELLOW al_map_rgb(255, 255, 0)
#define BLACK al_map_rgb(0, 0, 0)
#define WHITE al_map_rgb(255, 255, 255)
#define LIGHT_GREY al_map_rgb(192, 192, 192)
#define GREY al_map_rgb(128, 128, 128)
#define DARK_GREY al_map_rgb(64, 64, 64)
#define CYAN al_map_rgb(0, 255, 255)
#define MAGENTA al_map_rgb(255, 0, 255)
#define TRANSPARENT al_map_rgba(0, 0, 0, 0)
#define ORANGE al_map_rgb(255, 192, 0)
#define BROWN al_map_rgb(192, 168, 128)

/*
static const char *default_glsl_vertex_source =
   "attribute vec4 " ALLEGRO_SHADER_VAR_POS ";\n"
   "attribute vec4 " ALLEGRO_SHADER_VAR_COLOR ";\n"
   "attribute vec2 " ALLEGRO_SHADER_VAR_TEXCOORD ";\n"
   "uniform mat4 " ALLEGRO_SHADER_VAR_PROJVIEW_MATRIX ";\n"
   "uniform bool " ALLEGRO_SHADER_VAR_USE_TEX_MATRIX ";\n"
   "uniform mat4 " ALLEGRO_SHADER_VAR_TEX_MATRIX ";\n"
   "varying vec4 varying_color;\n"
   "varying vec2 varying_texcoord;\n"
   "void main()\n"
   "{\n"
   "  varying_color = " ALLEGRO_SHADER_VAR_COLOR ";\n"
   "  if (" ALLEGRO_SHADER_VAR_USE_TEX_MATRIX ") {\n"
   "    vec4 uv = " ALLEGRO_SHADER_VAR_TEX_MATRIX " * vec4(" ALLEGRO_SHADER_VAR_TEXCOORD ", 0, 1);\n"
   "    varying_texcoord = vec2(uv.x, uv.y);\n"
   "  }\n"
   "  else\n"
   "    varying_texcoord = " ALLEGRO_SHADER_VAR_TEXCOORD";\n"
   "  gl_Position = " ALLEGRO_SHADER_VAR_PROJVIEW_MATRIX " * " ALLEGRO_SHADER_VAR_POS ";\n"
   "}\n";
*/

/*
static const char *default_glsl_pixel_source =
   "#ifdef GL_ES\n"
   "precision lowp float;\n"
   "#endif\n"
   "uniform sampler2D " ALLEGRO_SHADER_VAR_TEX ";\n"
   "uniform bool " ALLEGRO_SHADER_VAR_USE_TEX ";\n"
   "varying vec4 varying_color;\n"
   "varying vec2 varying_texcoord;\n"
   "void main()\n"
   "{\n"
   "  if (" ALLEGRO_SHADER_VAR_USE_TEX ")\n"
   "    gl_FragColor = varying_color * texture2D(" ALLEGRO_SHADER_VAR_TEX ", varying_texcoord);\n"
   "  else\n"
   "    gl_FragColor = varying_color;\n"
   "}\n";
*/

/*
static const char *testing_glsl_pixel_source =
   "#ifdef GL_ES\n"
   "precision lowp float;\n"
   "#endif\n"
   "uniform sampler2D " ALLEGRO_SHADER_VAR_TEX ";\n"
   "uniform bool " ALLEGRO_SHADER_VAR_USE_TEX ";\n"
   "varying vec4 varying_color;\n"
   "varying vec2 varying_texcoord;\n"
   "void main()\n"
   "{\n"
   "  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
   "}\n";
*/

static const char *discarding_glsl_pixel_source =
   "#ifdef GL_ES\n"
   "precision lowp float;\n"
   "#endif\n"
   "uniform sampler2D " ALLEGRO_SHADER_VAR_TEX ";\n"
   "uniform bool " ALLEGRO_SHADER_VAR_USE_TEX ";\n"
   "varying vec4 varying_color;\n"
   "varying vec2 varying_texcoord;\n"
   "void main()\n"
   "{\n"
   "  if (" ALLEGRO_SHADER_VAR_USE_TEX ") {\n"
   "    vec4 p = texture2D(" ALLEGRO_SHADER_VAR_TEX ", varying_texcoord);\n"
   "    if (p.a == 0.0) {\n"
   "        discard;\n"
   "    }\n"
   "    gl_FragColor = varying_color * p;\n"
   "  }\n"
   "  else\n"
   "    gl_FragColor = varying_color;\n"
   "}\n";

class Main
{
private:
	ALLEGRO_BITMAP *buffer;
	ALLEGRO_FONT *font;
	ALLEGRO_BITMAP *orange, *yellow, *blue;
public:
	int init();
	void run();

	void drawEyes() {
		al_draw_filled_circle(32, 64, 8, WHITE);
		al_draw_filled_circle(80, 64, 8, WHITE);
		al_draw_filled_circle(32, 64, 4, BLACK);
		al_draw_filled_circle(80, 64, 4, BLACK);
	}

	void initSprites() {
		al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP);
		orange = al_create_bitmap(128, 128);
		yellow = al_create_bitmap(128, 128);
		blue = al_create_bitmap(128, 128);

		al_set_target_bitmap(orange);
		al_clear_to_color(TRANSPARENT);
		al_draw_filled_ellipse(64, 64, 64, 48, LIGHT_GREY);
		al_draw_filled_ellipse(60, 60, 56, 40, ORANGE);
		drawEyes();

		al_set_target_bitmap(yellow);
		al_clear_to_color(TRANSPARENT);
		al_draw_filled_ellipse(64, 64, 48, 64, LIGHT_GREY);
		al_draw_filled_ellipse(60, 60, 40, 56, YELLOW);
		drawEyes();

		al_set_target_bitmap(blue);
		al_clear_to_color(TRANSPARENT);
		al_draw_filled_ellipse(64, 64, 64, 48, GREY);
		al_draw_filled_ellipse(60, 60, 56, 40, BLUE);
		drawEyes();
	}

	void drawSprite(ALLEGRO_BITMAP *sprite, int x, int y, float z) {
		ALLEGRO_TRANSFORM transform;
		al_identity_transform(&transform);
		al_translate_transform_3d(&transform, 0, 0, z);
		al_use_transform (&transform);
		al_draw_bitmap (sprite, x, y, 0);
	}

	void drawScene() {
		al_clear_depth_buffer(1); // 1 is infinite distance
		al_set_render_state(ALLEGRO_DEPTH_TEST, true);

		// draw floor
		// should be rendered at distance 0
		al_draw_line(  0, 479, 240, 239, CYAN, 1.0);
		al_draw_line(240, 239, 639, 239, CYAN, 1.0);
		al_draw_line(639, 239, 399, 479, CYAN, 1.0);
		al_draw_line(399, 479,   0, 479, CYAN, 1.0);

		// draw sprites in reverse order
		drawSprite (orange, 256     , 256     , -0.7);
		drawSprite (yellow, 256 + 32, 256 - 32, -0.6);
		drawSprite (blue, 256 + 64, 256 - 64, -0.5);
	}

	ALLEGRO_SHADER *shader;
	void createShader()
	{
		shader = al_create_shader(ALLEGRO_SHADER_AUTO);
		assert(shader);

		bool ok;

		ok = al_attach_shader_source(shader, ALLEGRO_PIXEL_SHADER, discarding_glsl_pixel_source);
		//TODO: assert with message format...
		if (!ok) printf ("al_attach_shader_source failed: %s\n", al_get_shader_log(shader));
		assert(ok);

		ok = al_attach_shader_source(shader, ALLEGRO_VERTEX_SHADER,
				al_get_default_shader_source(ALLEGRO_SHADER_GLSL, ALLEGRO_VERTEX_SHADER));

		//TODO: assert with message format...
		if (!ok) printf ("al_attach_shader_source failed: %s\n", al_get_shader_log(shader));
		assert(ok);

		ok = al_build_shader(shader);
		//TODO: assert with message...
		if (!ok) printf ("al_build_shader failed: %s\n", al_get_shader_log(shader));
		assert(ok);
	}

	void enableShader() {
		al_use_shader(shader);
	}

	void disableShader() {
		al_use_shader(NULL);
	}

};

int Main::init()
{
	ALLEGRO_DISPLAY *display;
	ALLEGRO_TIMER *timer;
	ALLEGRO_CONFIG *config;

	if (!al_init()) {
		return 0;
	}

	al_init_primitives_addon();
	al_install_keyboard();
	al_init_font_addon();

	al_set_new_display_flags(ALLEGRO_PROGRAMMABLE_PIPELINE|ALLEGRO_OPENGL);
	display = al_create_display(640, 480);
	if (!display) {
		return 0;
	}

	font = al_create_builtin_font();
	initSprites();
	createShader();

	return 1;
}

void Main::run()
{
	al_set_new_bitmap_depth(16); // needs ALLEGRO_UNSTABLE
	ALLEGRO_BITMAP *buffer = al_create_bitmap (640, 480);
	al_set_target_bitmap(buffer);

	enableShader();

	drawScene();

	al_set_target_bitmap (al_get_backbuffer(al_get_current_display()));
	al_draw_bitmap(buffer, 0, 0, 0);

	al_flip_display();

	ALLEGRO_EVENT_QUEUE *queue;
	queue = al_create_event_queue();
	al_register_event_source(queue, al_get_keyboard_event_source());

	ALLEGRO_EVENT event;

	al_wait_for_event(queue, &event);
	al_destroy_event_queue(queue);

	return;
}

int main(int argc, const char *const *argv)
{
	Main m;
	if (m.init())
	{
		m.run();
	}
	return 0;
}
