/*
 *	Example program for the JPGalleg library
 *
 *	Version 2.5, by Angelo Mottola, 2000-2004
 *
 *	This example demonstrates how to load/save JPGs in memory and how this
 *      could be used to make a preview of a JPG image for a given quality.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <allegro.h>
#include <jpgalleg.h>


#define ESTIMATED_MAX_JPG_SIZE(bmp)		(((bmp)->w * (bmp)->h * 3) + 1024)


static int sx = 0, sy = 0, old_sx = 0, old_sy = 0;
static char last_path[1024] = "";
static char quality_string[64] = "Quality: 75 (medium)";
static char size_string[64] = "Size: ?";
static int quality_cb(void *dp3, int d2);
static int quality_text_proc(int msg, DIALOG *d, int c);
static int preview_proc(int msg, DIALOG *d, int c);
static int load_proc(void);
static int save_proc(void);
static int mmx_proc(void);


#define QUALITY_TEXT		2
#define QUALITY_SLIDER		3
#define SIZE_TEXT		4
#define SS_444_RADIO		5
#define SS_422_RADIO		6
#define SS_411_RADIO		7
#define GREYSCALE_CHECK		8
#define OPTIMIZE_CHECK		9
#define PREVIEW			11


static DIALOG settings_dialog[] = {
/* proc               x    y    w    h    fg   bg   key  flags      d1   d2    dp    dp2   dp3 */
 { d_shadow_box_proc, 0,   0,   465, 190, 0,   0,   0,   0,         0,   0,    NULL, NULL, NULL },
 { d_text_proc,       98,  10,  96,  8,   0,   0,   0,   0,         0,   0,    "JPG settings", NULL, NULL },
 { quality_text_proc, 16,  30,  260, 8,   0,   0,   0,   0,         75,  0,    quality_string, NULL, NULL },
 { d_slider_proc,     16,  40,  260, 16,  0,   0,   0,   0,         99,  74,   NULL, quality_cb, NULL },
 { d_text_proc,       16,  58,  260, 8,   0,   0,   0,   0,         0,   0,    size_string, NULL, NULL },
 { d_radio_proc,      16,  80,  260, 11,  0,   0,   0,   D_SELECTED,0,   0,    "No subsampling (444)", NULL, NULL },
 { d_radio_proc,      16,  95,  260, 11,  0,   0,   0,   0,         0,   0,    "Horizontal subsampling (422)", NULL, NULL },
 { d_radio_proc,      16,  110, 260, 11,  0,   0,   0,   0,         0,   0,    "Hor./Ver. subsampling (411)", NULL, NULL },
 { d_check_proc,      16,  135, 100, 11,  0,   0,   0,   0,         1,   0,    " Greyscale", NULL, NULL },
 { d_check_proc,      188, 135, 100, 11,  0,   0,   0,   0,         1,   0,    " Optimize", NULL, NULL },
 { d_button_proc,     106, 160, 81,  17,  0,   0,   0,   D_EXIT,    0,   0,    "Save", NULL, NULL },
 { preview_proc,      290, 15,  160, 160, 0,   0,   0,   0,         0,   0,    NULL, NULL, NULL },
 { d_yield_proc,      0,   0,   0,   0,   0,   0,   0,   0,         0,   0,    NULL, NULL, NULL },
 { NULL,              0,   0,   0,   0,   0,   0,   0,   0,         0,   0,    NULL, NULL, NULL }
};

static MENU popup_menu[] = {
/* text           proc           child  flags        dp */
 { "Load image",  load_proc,     NULL,  0,           NULL },
 { "Save as JPG", save_proc,     NULL,  D_DISABLED,  NULL },
 { "Enable MMX",  mmx_proc,      NULL,  D_DISABLED,  NULL },
 { "Quit",        NULL,          NULL,  0,           NULL },
 { NULL,          NULL,          NULL,  0,           NULL }
};



static int
quality_text_proc(int msg, DIALOG *d, int c)
{
	if (msg == MSG_DRAW) {
		rectfill(screen, d->x, d->y, d->x + d->w - 1, d->y + d->h - 1, gui_bg_color);
		sprintf(quality_string, "Quality: %d (%s)", d->d1,
			d->d1 < 20 ? "very low" :
			(d->d1 < 50 ? "low" :
			(d->d1 < 80 ? "medium" :
			(d->d1 < 95 ? "high" : "very high"))));
	}
	return d_text_proc(msg, d, c);
}


static int
quality_cb(void *dp3, int d2)
{
	(void)dp3;

	settings_dialog[QUALITY_TEXT].d1 = d2 + 1;
	object_message(&settings_dialog[QUALITY_TEXT], MSG_DRAW, 0);
	return D_O_K;
}


static int
preview_proc(int msg, DIALOG *d, int c)
{
	BITMAP *bmp;
	static int quality = 75, flags = JPG_SAMPLING_444;
	int new_quality, new_flags = 0;
	int size;

	(void)c;

	new_quality = settings_dialog[QUALITY_SLIDER].d2 + 1;
	if (settings_dialog[SS_444_RADIO].flags & D_SELECTED)
		new_flags = JPG_SAMPLING_444;
	if (settings_dialog[SS_422_RADIO].flags & D_SELECTED)
		new_flags = JPG_SAMPLING_422;
	if (settings_dialog[SS_411_RADIO].flags & D_SELECTED)
		new_flags = JPG_SAMPLING_411;
	if (settings_dialog[GREYSCALE_CHECK].flags & D_SELECTED)
		new_flags |= JPG_GREYSCALE;
	if (settings_dialog[OPTIMIZE_CHECK].flags & D_SELECTED)
		new_flags |= JPG_OPTIMIZE;
	
	switch (msg) {
		case MSG_START:
			d->dp2 = (void *)create_bitmap(d->w, d->h);
			bmp = (BITMAP *)d->dp;
			d->dp3 = malloc(ESTIMATED_MAX_JPG_SIZE(bmp));
			clear_to_color((BITMAP *)d->dp2, gui_mg_color);
			d->d2 = 20;
			sprintf(size_string, "Size: ?");
			break;

		case MSG_END:
			destroy_bitmap((BITMAP *)d->dp2);
			free(d->dp3);
			break;

		case MSG_DRAW:
			clear_to_color((BITMAP *)d->dp2, gui_mg_color);
			bmp = (BITMAP *)d->dp;
			bmp = create_sub_bitmap(bmp, MAX(0, (bmp->w - d->w) / 2), MAX(0, (bmp->h - d->h) / 2), d->w, d->h);
			if (!bmp)
				goto preview_error;
			size = ESTIMATED_MAX_JPG_SIZE(bmp);
			if (save_memory_jpg_ex(d->dp3, &size, bmp, NULL, quality, flags, NULL))
				goto preview_error;
			size = ESTIMATED_MAX_JPG_SIZE(bmp);
			destroy_bitmap(bmp);
			bmp = load_memory_jpg(d->dp3, size, NULL);
			if (!bmp)
				goto preview_error;
			blit(bmp, (BITMAP *)d->dp2, 0, 0, 0, 0, bmp->w, bmp->h);
			rect(screen, d->x - 1, d->y - 1, d->x + d->w, d->y + d->h, gui_fg_color);
			blit((BITMAP *)d->dp2, screen, 0, 0, d->x, d->y, d->w, d->h);
			destroy_bitmap(bmp);
			object_message(&settings_dialog[QUALITY_TEXT], MSG_DRAW, 0);
			break;

		case MSG_IDLE:
			if (d->d2 > 0) {
				d->d2--;
				if (d->d2 == 0) {
					quality = new_quality;
					flags = new_flags;
					bmp = (BITMAP *)d->dp;
					size = ESTIMATED_MAX_JPG_SIZE(bmp);
					if (save_memory_jpg_ex(d->dp3, &size, bmp, NULL, quality, flags, NULL))
						goto preview_error;
					sprintf(size_string, "Size: %dx%d, %.1f Kbytes  ", bmp->w, bmp->h, (double)size / 1024.0);
					object_message(&settings_dialog[SIZE_TEXT], MSG_DRAW, 0);
					return D_REDRAWME;
				}
			}
			if ((new_quality != quality) || (new_flags != flags)) {
				quality = new_quality;
				flags = new_flags;
				d->d2 = 20;
				if (flags & JPG_GREYSCALE) {
					settings_dialog[SS_444_RADIO].flags |= D_DISABLED;
					settings_dialog[SS_422_RADIO].flags |= D_DISABLED;
					settings_dialog[SS_411_RADIO].flags |= D_DISABLED;
				}
				else {
					settings_dialog[SS_444_RADIO].flags &= ~D_DISABLED;
					settings_dialog[SS_422_RADIO].flags &= ~D_DISABLED;
					settings_dialog[SS_411_RADIO].flags &= ~D_DISABLED;
				}
				settings_dialog[SS_444_RADIO].fg = (flags & JPG_GREYSCALE ? gui_mg_color : gui_fg_color);
				settings_dialog[SS_422_RADIO].fg = (flags & JPG_GREYSCALE ? gui_mg_color : gui_fg_color);
				settings_dialog[SS_411_RADIO].fg = (flags & JPG_GREYSCALE ? gui_mg_color : gui_fg_color);
				object_message(&settings_dialog[SS_444_RADIO], MSG_DRAW, 0);
				object_message(&settings_dialog[SS_422_RADIO], MSG_DRAW, 0);
				object_message(&settings_dialog[SS_411_RADIO], MSG_DRAW, 0);
				return D_REDRAWME;
			}
			break;
	}
	return D_O_K;

preview_error:
	line((BITMAP *)d->dp2, 0, 0, d->w - 1, d->h - 1, 0);
	line((BITMAP *)d->dp2, d->w - 1, 0, 0, d->h - 1, 0);
	blit((BITMAP *)d->dp2, screen, 0, 0, d->x, d->y, d->w, d->h);
	return D_O_K;
}


static void
progress_cb(int percentage)
{
	rect(screen, 219, 235, 421, 244, 0);
	rectfill(screen, 220, 236, 220 + (percentage * 2), 243, makecol(255, 0, 0));
	rectfill(screen, 221 + (percentage * 2), 236, 421, 243, 0);
}


static int
load_proc(void)
{
	char buffer[256], *p;
	BITMAP *bmp;
	PALETTE palette;

	if (file_select_ex("Load image", last_path, "bmp;pcx;lbm;tga;jpg;jpeg", 1024, (SCREEN_W * 2) / 3, (SCREEN_H * 2) / 3)) {
		bmp = load_jpg_ex(last_path, palette, progress_cb);
		if (!bmp)
			bmp = load_bitmap(last_path, palette);
		settings_dialog[PREVIEW].dp = bmp;
		if (!bmp) {
			sprintf(buffer, "Error loading image (JPG error code: %d)", jpgalleg_error);
			alert("", buffer, "", "Ok", NULL, 0, 0);
			return 0;
		}
		for (p = last_path + strlen(last_path); (p >= last_path) && (*p != '/') && (*p != '\\'); p--)
			;
		*(p + 1) = '\0';
		sx = sy = old_sx = old_sy = 0;
		set_palette(palette);
		popup_menu[1].flags = 0;
	}
	return 0;
}


static int
save_proc(void)
{
	char filename[1024];
	char buffer[256], *p;
	BITMAP *bmp = NULL;
	int quality = 0, flags = 0;

	if (file_select_ex("Save as JPG", last_path, "jpg", 1024, (SCREEN_W * 2) / 3, (SCREEN_H * 2) / 3)) {
		strcpy(filename, last_path);
		for (p = last_path + strlen(last_path); (p >= last_path) && (*p != '/') && (*p != '\\'); p--)
			;
		*(p + 1) = '\0';
		set_dialog_color(settings_dialog, gui_fg_color, gui_bg_color);
		centre_dialog(settings_dialog);
		if (popup_dialog(settings_dialog, -1) < 0)
			return 0;
		quality = settings_dialog[QUALITY_SLIDER].d2 + 1;
		if (settings_dialog[SS_444_RADIO].flags & D_SELECTED)
			flags = JPG_SAMPLING_444;
		if (settings_dialog[SS_422_RADIO].flags & D_SELECTED)
			flags = JPG_SAMPLING_422;
		if (settings_dialog[SS_411_RADIO].flags & D_SELECTED)
			flags = JPG_SAMPLING_411;
		if (settings_dialog[GREYSCALE_CHECK].flags & D_SELECTED)
			flags |= JPG_GREYSCALE;
		if (settings_dialog[OPTIMIZE_CHECK].flags & D_SELECTED)
			flags |= JPG_OPTIMIZE;
		bmp = settings_dialog[PREVIEW].dp;

		if (save_jpg_ex(filename, bmp, NULL, quality, flags, progress_cb)) {
			sprintf(buffer, "Error saving JPG image (error code %d)", jpgalleg_error);
			alert("", buffer, "", "Ok", NULL, 0, 0);
		}
	}
	return 0;
}


static int
mmx_proc(void)
{
	if (popup_menu[2].flags & D_SELECTED) {
		popup_menu[2].flags &= ~D_SELECTED;
		cpu_capabilities &= ~CPU_MMX;
	}
	else {
		popup_menu[2].flags |= D_SELECTED;
		cpu_capabilities |= CPU_MMX;
	}
	return 0;
}


static void
draw_image(BITMAP *bmp, int x, int y)
{
	scare_mouse();
	if (bmp) {
		blit(bmp, screen, x, y, 0, 0, bmp->w, bmp->h);
		x = bmp->w - x;
		y = bmp->h - y;
		if (x < SCREEN_W)
			rectfill(screen, x, 0, SCREEN_W, SCREEN_H, gui_mg_color);
		if (y < SCREEN_H)
			rectfill(screen, 0, y, x, SCREEN_H, gui_mg_color);
	}
	else
		clear_to_color(screen, gui_mg_color);
	unscare_mouse();
}


int
main(int argc, char **argv)
{
	BITMAP *bmp = NULL;
	int x, y, mx, my;
	int result, mode = GFX_AUTODETECT;

	allegro_init();
	install_keyboard();

	if (install_mouse() < 0) {
		allegro_message("This example requires a mouse to run!");
		return -1;
	}

	jpgalleg_init();

	if (cpu_capabilities & CPU_MMX)
		popup_menu[2].flags = D_SELECTED;

	if ((argc > 1) && (!strcmp(argv[1], "-window")))
		mode = GFX_AUTODETECT_WINDOWED;

	set_color_depth(32);
	if (set_gfx_mode(mode, 640, 480, 0, 0)) {
		set_color_depth(16);
		if (set_gfx_mode(mode, 640, 480, 0, 0)) {
			set_color_depth(15);
			if (set_gfx_mode(mode, 640, 480, 0, 0)) {
				allegro_message("Unable to init truecolor 640x480 gfx mode: %s", allegro_error);
				return -1;
			}
		}
	}

	clear_to_color(screen, gui_mg_color);
	show_mouse(screen);
	
	alert("JPG image load/save example for " JPGALLEG_VERSION_STRING, NULL, "Press right mouse button for contextual menu, ESC to quit.", "Ok", NULL, 0, 0);

	while (!key[KEY_ESC]) {
		x = mouse_x;
		y = mouse_y;
		get_mouse_mickeys(&mx, &my);
		mx /= 2;
		my /= 2;

		bmp = settings_dialog[PREVIEW].dp;

		if (bmp) {
			old_sx = sx;
			old_sy = sy;
			if (bmp->w > SCREEN_W) {
				if ((x == 0) && (mx < 0) && (sx > 0))
					sx = MAX(0, sx + mx);
				if ((x == SCREEN_W - 1) && (mx > 0) && (sx < bmp->w - SCREEN_W - 1))
					sx = MIN(sx + mx, bmp->w - SCREEN_W - 1);
				if (key[KEY_LEFT] && (sx > 0))
					sx = MAX(0, sx - 8);
				if (key[KEY_RIGHT] && (sx < bmp->w - SCREEN_W - 1))
					sx = MIN(sx + 8, bmp->w - SCREEN_W - 1);
			}
			if (bmp->h > SCREEN_H) {
				if ((y == 0) && (my < 0) && (sy > 0))
					sy = MAX(0, sy + my);
				if ((y == SCREEN_H - 1) && (my > 0) && (sy < bmp->h - SCREEN_H - 1))
					sy = MIN(sy + my, bmp->h - SCREEN_H - 1);
				if (key[KEY_UP] && (sy > 0))
					sy = MAX(0, sy - 8);
				if (key[KEY_DOWN] && (sy < bmp->h - SCREEN_H - 1))
					sy = MIN(sy + 8, bmp->h - SCREEN_H - 1);
			}
			if ((sx != old_sx) || (sy != old_sy))
				draw_image(bmp, sx, sy);
		}
		if (mouse_b & 0x2) {
			result = do_menu(popup_menu, x, y);
			while(key[KEY_ESC])
				;
			if (result == 3)
				break;
			draw_image((BITMAP *)settings_dialog[PREVIEW].dp, sx, sy);
		}
	}

	if (bmp)
		destroy_bitmap(bmp);

	return 0;
}
END_OF_MAIN();
