/*
 *  Draw a magic card template given:
 *    its background image
 *    its text-box image
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "app.h"

Image *card;

App *app;
Window *w;
Control *c;

char *msg_file = "Dimensions.txt";

typedef struct SpecialBorder {
	char *  name;
	Colour  colour;
} SpecialBorder;

Colour border_colour = {0,   0,   0,   0}; /* BLACK */
/* Colour border_colour = {0, 255, 255, 255}; /* WHITE */

SpecialBorder land[] =
{
	{ "LandW",   {0x00, 0xd2, 0xc9, 0xc2} },
	{ "LandU",   {0x00, 0x43, 0x65, 0x87} },
	{ "LandB",   {0x00, 0x59, 0x4d, 0x4c} },
	{ "LandR",   {0x00, 0xe7, 0x50, 0x47} },
	{ "LandG",   {0x00, 0x96, 0xa5, 0x46} },
	{ "LandMult",{0x00, 0x71, 0x55, 0x7f} },
	{ "Land",    {0x00, 0xe1, 0x8c, 0x3a} },
	{ NULL,      {0xff, 0xff, 0xff, 0xff} },
};


void reset_msg_file()
{
	fclose(fopen(msg_file, "w"));
}

void print_rect(char *msg, Rect r, Rect outside)
{
	FILE * f = fopen(msg_file, "a");
	fprintf(f, "%s:\t(l=%d, t=%d, r=%d, b=%d, w=%d, h=%d)\n",
		msg,
		r.x - outside.x,
		r.y - outside.y,
		r.x + r.width - outside.x - outside.width,
		r.y + r.height - outside.y - outside.height,
		r.width,
		r.height);
	fclose(f);
}

void show_card_in_control(Control *c, Graphics *g)
{
	Rect r = app_get_control_area(c);

	if (card)
		app_draw_image(g, r, card, app_get_image_area(card));
}

/* Numerator and denominator of darkening scale factor. */
#define N 4
#define D 5

Colour darker(Colour c)
{
        int r, g, b;

        r = c.red;
        g = c.green;
        b = c.blue;

        return argb(c.alpha,(r+1)*N/D,(g+1)*N/D,(b+1)*N/D);
}

Colour brighter(Colour c)
{
        int r, g, b;

        r = c.red;
        g = c.green;
        b = c.blue;

        r = r * D / N; if (r > 255) r = 255;
        g = g * D / N; if (g > 255) g = 255;
        b = b * D / N; if (b > 255) b = 255;
        return rgb(r,g,b);
}

/*
 *  Copy the pixels from img to g, brightening the specified edges
 *  and darkening the opposite edges, in to a given width.
 *  Note: p is the top-left place in the image to start drawing from.
 *  We assume a 1:1 ratio between pixels in g and img.
 */

typedef long Edge;

void draw_bevel(Graphics *g, Rect r, int thick, Image *img, Point p, Edge lighten)
{
	Colour c;
	int x, y;

	/* Modify left edge. */
	for (x = 0; x < thick; x++)
	{
		for (y = x; y < r.height - x; y++)
		{
			c = img->data32[y+p.y][x+p.x];
			if (lighten & EDGE_LEFT)
				c = brighter(c);
			else
				c = darker(c);
			app_set_colour(g, c);
			app_fill_rect(g, rect(r.x+x, r.y+y, 1, 1));
		}
	}

	/* Modify bottom edge. */
	for (y = r.height - thick; y < r.height; y++)
	{
		for (x = r.height - y; x < r.width + y - r.height; x++)
		{
			c = img->data32[y+p.y][x+p.x];
			if (lighten & EDGE_BOTTOM)
				c = brighter(c);
			else
				c = darker(c);
			app_set_colour(g, c);
			app_fill_rect(g, rect(x+r.x, y+r.y, 1, 1));
		}
	}

	/* Modify right edge. */
	for (x = r.width - thick; x < r.width; x++)
	{
		for (y = r.width - x; y < r.height + x - r.width; y++)
		{
			c = img->data32[y+p.y][x+p.x];
			if (lighten & EDGE_RIGHT)
				c = brighter(c);
			else
				c = darker(c);
			app_set_colour(g, c);
			app_fill_rect(g, rect(x+r.x, y+r.y, 1, 1));
		}
	}

	/* Modify top edge. */
	for (y = 0; y < thick; y++)
	{
		for (x = y; x < r.width - y; x++)
		{
			c = img->data32[y+p.y][x+p.x];
			if (lighten & EDGE_TOP)
				c = brighter(c);
			else
				c = darker(c);
			app_set_colour(g, c);
			app_fill_rect(g, rect(x+r.x, y+r.y, 1, 1));
		}
	}
}

/*
 *  Force a rectangular area to become transparent.
 *  Assume the image has a 32-bit depth.
 */
void make_rect_transparent(Image *img, Rect r)
{
	int x, y;

	for (y = 0; y < r.height; y++)
	{
		for (x = 0; x < r.width; x++)
		{
			img->data32[y+r.y][x+r.x] = CLEAR;
		}
	}
}

/*
 *  Draw a diagonal line, 1 pixel thick and black at the centre.
 *  On either side, draw a 1 pixel thick half-transparent grey line,
 *  to try to antialias the line a little.
 */
void draw_antialiased_diagonal(Graphics *g, Point p1, Point p2)
{
	int w = p2.x - p1.x;
	int h = p2.y - p1.y;
	int old_width = g->line_width;
	Colour old_colour = g->colour;

	app_set_line_width(g, 1);
	app_set_colour(g, argb((old_colour.alpha + 0x0FF) / 2,
				old_colour.red,
				old_colour.green,
				old_colour.blue));

	if ((w > 0) && (h > 0)) /* down-to-right */
	{
		app_draw_line(g, pt(p1.x+1,p1.y), pt(p2.x+1,p2.y));
		app_draw_line(g, pt(p1.x,p1.y+1), pt(p2.x,p2.y+1));
	}
	else if ((w > 0) && (h < 0)) /* up-to-right */
	{
		app_draw_line(g, pt(p1.x,p1.y-1), pt(p2.x,p2.y-1));
		app_draw_line(g, pt(p1.x+1,p1.y), pt(p2.x+1,p2.y));
	}
	else if ((w < 0) && (h > 0)) /* down-to-left */
	{
		app_draw_line(g, pt(p1.x-1,p1.y), pt(p2.x-1,p2.y));
		app_draw_line(g, pt(p1.x,p1.y+1), pt(p2.x,p2.y+1));
	}
	else if ((w < 0) && (h < 0)) /* up-to-left */
	{
		app_draw_line(g, pt(p1.x,p1.y-1), pt(p2.x,p2.y-1));
		app_draw_line(g, pt(p1.x-1,p1.y), pt(p2.x-1,p2.y));
	}

	app_set_colour(g, old_colour);
	app_draw_line(g, p1, p2);

	app_set_line_width(g, old_width);
}

/*
 *  Composite together the pieces of a Magic card.
 */
void build_card(char *bg, char *tb, char *result)
{
	int border_line = 1;
	int border = 30;
	int card_line = 3;
	int name_height = 62;
	int name_LR_inset = 18;
	int artwork_LR_inset = 42;
	int artwork_line = 2;
	int artwork_bevel = 12;
	int artwork_width = 570;
	int artwork_height = 460;
	int type_height = 62;
	int type_LR_inset = 37;
	int textbox_LR_inset = 50;
	int textbox_line = 2;
	int textbox_bevel = 8;
	int textbox_width = 578;
	int textbox_height = 306;
	int artist_height = 55;
	int serif_font_height = 28;

	Image *bgimg = app_read_image(bg, 32);
	Image *tbimg = app_read_image(tb, 32);

	Rect outside = rect(0, 0, bgimg->width, bgimg->height);
	Rect r = outside;
	Rect box;
	int i;
	Graphics *g;

	reset_msg_file();

	card = app_copy_image(bgimg);

	/* Record the overall size of the image. */
	print_rect("Card area", r, outside);

	/* Draw into the image now. */
	g = app_get_image_graphics(card);

	/* Fill border of card with white. */
	app_set_colour(g, WHITE);
	app_fill_rect(g, rect(r.x, r.y, border, r.height));
	app_fill_rect(g, rect(r.x, r.y, r.width, border));
	app_fill_rect(g, rect(r.x + r.width - border, r.y,
				border, r.height));
	app_fill_rect(g, rect(r.x, r.y + r.height - border,
				r.width, border));

	/* Make the corners transparent. Later we fill a round rect. */
	make_rect_transparent(card, rect(r.x, r.y, border, border));
	make_rect_transparent(card, rect(r.x + r.width - border, r.y,
					border, border));
	make_rect_transparent(card, rect(r.x, r.y + r.height - border,
					border, border));
	make_rect_transparent(card, rect(r.x + r.width - border,
					r.y + r.height - border,
					border, border));

	/* Fill round rect card border black/white/gold. */
	app_set_colour(g, border_colour);
	app_fill_rect(g, rect(r.x, r.y + border,
				border, r.height - border * 2 + 1));
	app_fill_rect(g, rect(r.x + border, r.y,
				r.width - border * 2, border));
	app_fill_rect(g, rect(r.x + r.width - border, r.y + border,
				border, r.height - border * 2 + 1));
	app_fill_rect(g, rect(r.x + border, r.y + r.height - border,
				r.width - border * 2, border));

	/* Draw round rect around whole card. */
	app_set_colour(g, BLACK);
	app_set_line_width(g, border_line);
	app_fill_rect(g, rect(r.x, r.y + border,
				border_line, r.height - border * 2 + 1));
	app_fill_rect(g, rect(r.x + border, r.y,
				r.width - border * 2, border_line));
	app_fill_rect(g, rect(r.x + r.width - border_line, r.y + border,
				border_line, r.height - border * 2 + 1));
	app_fill_rect(g, rect(r.x + border, r.y + r.height - border_line,
				r.width - border * 2, border_line));

	/* Do a little anti-aliasing at the round-rect corners. */
	app_set_colour(g, argb(0x7F,0x0,0x0,0x0));
	app_set_line_width(g, 3);
	app_draw_arc(g, app_inset_rect(rect(r.x, r.y,
					border * 2, border * 2),
				-1),
			90, 180);
	app_draw_arc(g, app_inset_rect(rect(r.x + r.width - border * 2,
					r.y,
					border * 2, border * 2),
				-1),
			0, 90);
	app_draw_arc(g, app_inset_rect(rect(r.x,
					r.y + r.height - border * 2,
					border * 2, border * 2),
				-1),
			180, 270);
	app_draw_arc(g, app_inset_rect(rect(r.x + r.width - border * 2,
					r.y + r.height - border * 2,
					border * 2, border * 2),
				-1),
			270, 360);
	app_set_line_width(g, 1);

	/* Fill rounded corners with border black/white/gold. */
	app_set_colour(g, border_colour);
	app_fill_arc(g, rect(r.x, r.y, border * 2, border * 2), 90, 180);
	app_fill_arc(g, rect(r.x + r.width - border * 2, r.y,
				border * 2, border * 2), 0, 90);
	app_fill_arc(g, rect(r.x, r.y + r.height - border * 2,
				border * 2, border * 2), 180, 270);
	app_fill_arc(g, rect(r.x + r.width - border * 2,
				r.y + r.height - border * 2,
				border * 2, border * 2), 270, 360);

	/* Draw the solid rount-rect corners. */
	app_set_colour(g, BLACK);
	app_set_line_width(g, 1);
	app_draw_arc(g, rect(r.x, r.y, border * 2, border * 2), 90, 180);
	app_draw_arc(g, rect(r.x + r.width - border * 2, r.y,
				border * 2, border * 2), 0, 90);
	app_draw_arc(g, rect(r.x, r.y + r.height - border * 2,
				border * 2, border * 2), 180, 270);
	app_draw_arc(g, rect(r.x + r.width - border * 2,
				r.y + r.height - border * 2,
				border * 2, border * 2), 270, 360);

	/* Draw card border over outer edge of coloured bit. */
	r = app_inset_rect(r, border);
	print_rect("Card border", r, outside);

	box = app_inset_rect(r, 1);
	print_rect("Card bevel", box, outside);
	draw_bevel(g, box, card_line, card,
			pt(box.x, box.y), EDGE_TOP | EDGE_RIGHT);

	/* Draw card bevel's top-right diagonal. */
	app_set_line_width(g, 1);
	app_set_colour(g, argb(0xBB,0x0,0x0,0x0));
	draw_antialiased_diagonal(g, pt(box.x + box.width - 1, box.y),
		pt(box.x + box.width - card_line,
		   box.y + card_line - 1));

	/* Draw card bevel's bottom-left diagonal. */
	app_set_colour(g, argb(0xDD,0x0,0x0,0x0));
	draw_antialiased_diagonal(g, pt(box.x, box.y + box.height - 1),
		pt(box.x + card_line - 1,
		   box.y + box.height - card_line));

	/* Draw the thin card rectangle just inside the expansion edge. */
	app_set_colour(g, BLACK);
	app_set_line_width(g, 1);
	app_draw_rect(g, r);

	r = app_inset_rect(r, card_line);

	/* Make bevel effect around artwork box. */
	box = rect(r.x + artwork_LR_inset, r.y + name_height,
				artwork_width + artwork_bevel * 2,
				artwork_height + artwork_bevel * 2);
	print_rect("Artwork bevel", box, outside);
	draw_bevel(g, box, artwork_bevel - artwork_line, card,
			pt(box.x, box.y), EDGE_BOTTOM | EDGE_LEFT);

	app_set_colour(g, DARK_GREY);

	/* Draw artwork bevel's top-left diagonal. */
	draw_antialiased_diagonal(g, pt(r.x + artwork_LR_inset,
				r.y + name_height),
			pt(r.x + artwork_LR_inset + artwork_bevel,
				r.y + name_height + artwork_bevel));

	/* Draw artwork bevel's top-right diagonal. */
	draw_antialiased_diagonal(g, pt(r.x + artwork_LR_inset
				 + artwork_width + artwork_bevel * 2
				 - artwork_line + 1,
				r.y + name_height),
				pt(r.x + artwork_LR_inset
				 + artwork_width + artwork_bevel
				 - artwork_line + 1,
				r.y + name_height + artwork_bevel));

	/* Draw artwork bevel's bottom-right diagonal. */
	draw_antialiased_diagonal(g, pt(r.x + artwork_LR_inset
				 + artwork_width + artwork_bevel,
				r.y + name_height + artwork_bevel
				 + artwork_height),
			 pt(r.x + artwork_LR_inset
				 + artwork_width + artwork_bevel * 2
				 - artwork_line,
				r.y + name_height
				 + artwork_height + artwork_bevel * 2
				 - artwork_line));

	/* Draw artwork bevel's bottom-left diagonal. */
	draw_antialiased_diagonal(g, pt(r.x + artwork_LR_inset,
				r.y + name_height
				 + artwork_height + artwork_bevel * 2
				 - artwork_line + 1),
			 pt(r.x + artwork_LR_inset + artwork_bevel,
				r.y + name_height
				 + artwork_height + artwork_bevel
				 - artwork_line + 1));

	/* Draw artwork box outer border */
	app_set_colour(g, DARK_GREY);
	app_set_line_width(g, artwork_line);
	app_draw_rect(g, rect(r.x + artwork_LR_inset, r.y + name_height, 
				artwork_width + artwork_bevel * 2,
				artwork_height + artwork_bevel * 2));

	/* Draw artwork bevel inner border. */
	app_draw_rect(g, rect(r.x + artwork_LR_inset + artwork_bevel
				 - artwork_line,
				r.y + name_height + artwork_bevel
				 - artwork_line, 
				artwork_width + artwork_line * 2,
				artwork_height + artwork_line * 2));

	/* Blank artwork box. */
	app_set_colour(g, WHITE);
	box = rect(r.x + artwork_LR_inset + artwork_bevel,
				r.y + name_height + artwork_bevel, 
				artwork_width,
				artwork_height);
	print_rect("Artwork area", box, outside);
	app_fill_rect(g, box);

	/* Draw text box. */
	app_set_colour(g, DARK_GREY);
	app_set_line_width(g, textbox_line);

	box = rect(r.x + textbox_LR_inset,
		r.y + name_height
		    + artwork_height + artwork_bevel * 2
		    + type_height,
		textbox_width,
		textbox_height);

	app_draw_rect(g, app_inset_rect(box, -textbox_line));
	app_draw_image(g, box, tbimg, app_get_image_area(tbimg));

	/* Make bevel effect around text box. */
	print_rect("Text box bevel", box, outside);
	draw_bevel(g, box, textbox_bevel, tbimg, pt(0,0),
		EDGE_BOTTOM | EDGE_LEFT);

	/* Draw text box bevel's top-right diagonal. */
	app_set_line_width(g, 1);
	app_set_colour(g, argb(0xBB,0x0,0x0,0x0));
	draw_antialiased_diagonal(g, pt(box.x + box.width - 1, box.y),
		pt(box.x + box.width - textbox_bevel,
		   box.y + textbox_bevel - 1));

	/* Draw text box bevel's bottom-left diagonal. */
	app_set_colour(g, argb(0xDD,0x0,0x0,0x0));
	draw_antialiased_diagonal(g, pt(box.x, box.y + box.height - 1),
		pt(box.x + textbox_bevel - 1,
		   box.y + box.height - textbox_bevel));

	/* Draw a special "Land" colour border outside the textbox,
	 * if it is a land card. */
	for (i = 0; land[i].name != NULL; i++)
	{
		if (strstr(tb, land[i].name) != NULL)
		{
			app_set_colour(g, DARK_GREY);
			app_set_line_width(g, 1);
			app_draw_rect(g, app_inset_rect(box, -6));
			app_draw_rect(g, app_inset_rect(box, -1));

			app_set_line_width(g, 4);
			app_set_colour(g, land[i].colour);
			app_draw_rect(g, app_inset_rect(box, -5));

			break;
		}
	}

	/* Report text box inner area. */
	box = app_inset_rect(box, artwork_bevel);
	print_rect("Text box area", box, outside);

	/* Tidy up. */
	app_del_graphics(g);

	/* Save the image to a file. */
	if (result)
		app_write_image(card, result);

	/* Stop here. */
	return;

	/* Show it on screen. */
	app = app_new_app(0, 0);
	w = app_new_window(app, rect(20, 20, 300, 413),
				"Card Constructor",
				STANDARD_WINDOW);
	c = app_new_control(w, rect(10, 10, 280, 393));
	app_on_control_redraw(c, show_card_in_control);

	app_show_window(w);
	app_main_loop(app);
	app_del_app(app);
}

int main(int argc, char *argv[])
{
	if (argc < 3)
	{
		fprintf(stderr, "usage: layout_card background textbox outfile\n");
		return 1;
	}

	build_card(argv[1], argv[2], argv[3]);

	return 0;
}
