/*
 *  Draw the empty boxes (and beveled edges) for:
 *   the artwork box,
 *   the rules text box(es) [flip-cards have two boxes].
 *
 *  Also, draw the border, and ensure there are
 *  transparent bits at the edges of the image.
 */

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

#include "GenCard.h"

Image * form_normal_card_7th_ed(Image *bgimg, Image *tbimg, Colour border_colour, Colour box_border)
{
	const int border_line = 1;
	const int border = 30;
	const int card_line = 3;
	const int name_height = 62;
	//const int name_LR_inset = 18;
	const int artwork_LR_inset = 42;
	const int artwork_line = 2;
	const int artwork_bevel = 12;
	const int artwork_width = 570;
	const int artwork_height = 460;
	const int type_height = 62;
	//const int type_LR_inset = 37;
	const int textbox_LR_inset = 50;
	const int textbox_line = 2;
	const int textbox_bevel = 8;
	const int textbox_width = 578;
	const int textbox_height = 306;
	//const int artist_height = 55;
	//const int serif_font_height = 28;

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

	result = app_copy_image(bgimg);
	if (result == NULL)
		return NULL;

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

	/* Draw the black or white outer card border. */
	draw_outer_border(g, result, r, border, border_line, border_colour);

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

	box = app_inset_rect(r, 1);
	draw_bevel(g, box, card_line, result,
			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);
	draw_bevel(g, box, artwork_bevel - artwork_line, result,
			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);
	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));

	if (tbimg)
	{
		/* Draw the text box. */
		app_draw_image(g, box, tbimg, app_get_image_area(tbimg));

		/* Make bevel effect around text box. */
		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. */
	if (box_border.alpha != 0xFF)
	{
		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, box_border);
		app_draw_rect(g, app_inset_rect(box, -5));
	}

	/* Report text box inner area. */
	box = app_inset_rect(box, artwork_bevel);

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

	/* Return the constructed 300 DPI image to the caller. */
	return result;
}

Image * form_flip_card_7th_ed(Image *bgimg, Image *tbimg, Colour border_colour, Colour box_border)
{
	const int border_line = 1;
	const int border = 30;
	const int card_line = 3;
	const int name_height = 62;
	//const int name_LR_inset = 18;
	const int artwork_LR_inset = 42;
	const int artwork_line = 2;
	const int artwork_bevel = 12;
	const int artwork_width = 570;
	const int artwork_height = 460 * 7/10;
	const int type_height = 62;
	//const int type_LR_inset = 37;
	const int textbox_LR_inset = 50;
	const int textbox_line = 2;
	const int textbox_bevel = 8;
	const int textbox_width = 578;
	const int textbox_height = 306 * 11/20;
	//const int artist_height = 55;
	//const int serif_font_height = 28;

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

	result = app_copy_image(bgimg);
	if (result == NULL)
		return NULL;

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

	/* Draw the black or white outer card border. */
	draw_outer_border(g, result, r, border, border_line, border_colour);

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

	box = app_inset_rect(r, 1);
	draw_bevel(g, box, card_line, result,
			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 + textbox_height + type_height,
		artwork_width + artwork_bevel * 2,
		artwork_height + artwork_bevel * 2);
	draw_bevel(g, box, artwork_bevel - artwork_line, result,
			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 + textbox_height + type_height),
			pt(r.x + artwork_LR_inset + artwork_bevel,
				r.y + name_height + textbox_height + type_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 + textbox_height + type_height),
				pt(r.x + artwork_LR_inset
				 + artwork_width + artwork_bevel
				 - artwork_line + 1,
				r.y + name_height + textbox_height + type_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 + textbox_height + type_height + artwork_bevel
				 + artwork_height),
			 pt(r.x + artwork_LR_inset
				 + artwork_width + artwork_bevel * 2
				 - artwork_line,
				r.y + name_height + textbox_height + type_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 + textbox_height + type_height
				 + artwork_height + artwork_bevel * 2
				 - artwork_line + 1),
			 pt(r.x + artwork_LR_inset + artwork_bevel,
				r.y + name_height + textbox_height + type_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 + textbox_height + type_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 + textbox_height + type_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 + textbox_height + type_height + artwork_bevel, 
		artwork_width,
		artwork_height);
	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,
		textbox_width,
		textbox_height);

	app_draw_rect(g, app_inset_rect(box, -textbox_line));

	if (tbimg)
	{
		/* Draw the text box. */
		app_draw_image(g, box, tbimg, app_get_image_area(tbimg));

		/* Make bevel effect around text box. */
		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. */
	if (box_border.alpha != 0xFF)
	{
		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, box_border);
		app_draw_rect(g, app_inset_rect(box, -5));
	}

	/* Report text box inner area. */
	box = app_inset_rect(box, artwork_bevel);


	/* 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 + textbox_height + type_height + artwork_bevel * 2 + artwork_height + type_height,
		textbox_width,
		textbox_height);

	app_draw_rect(g, app_inset_rect(box, -textbox_line));

	if (tbimg)
	{
		/* Draw the text box again. */
		app_draw_image(g, box, tbimg, app_get_image_area(tbimg));

		/* Make bevel effect around text box. */
		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. */
	if (box_border.alpha != 0xFF)
	{
		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, box_border);
		app_draw_rect(g, app_inset_rect(box, -5));
	}

	/* Report text box inner area. */
	box = app_inset_rect(box, artwork_bevel);

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

	/* Return the constructed 300 DPI image to the caller. */
	return result;
}

/*
 * This is the new extensible way of generating card backgrounds.
 */
Image * form_card_template_1_7th_ed(Colour bg_border,  /* BLACK, WHITE, GOLD */
			char *bg_name,          /* e.g. Red or Land */
			char *tb_name,          /* e.g. Red or LandR */
			Colour tb_border,       /* e.g. CLEAR or RED */
			int is_flip)            /* is it a flip card? */
{
	/* This function draws normal or normal flip-cards, not hybrids. */
	Image *bg, *tb;
	char *bg_filename, *tb_filename;
	Image *template = NULL;

	/* Form a file name like GreenCard.png or LandCard.png */
	bg_filename = copy_string(bg_name);
	bg_filename = append_string(bg_filename, "Card.png");

	/* Form a file name like BlueTextBox.png or LandUTextBox.png */
	tb_filename = copy_string(tb_name);
	tb_filename = append_string(tb_filename, "TextBox.png");

	/* Try to load those files as images. */
	bg = find_cached_image(bg_filename);
	tb = find_cached_image(tb_filename);

	/* Create a card template. */
	if (bg != NULL)
	{
		if (is_flip)
			template = form_flip_card_7th_ed(bg, tb, bg_border, tb_border);
		else
			template = form_normal_card_7th_ed(bg, tb, bg_border, tb_border);
	}

	/* Tidy up. */
	if (tb)
		app_del_image(tb);
	if (bg)
		app_del_image(bg);
	if (tb_filename)
		app_free(tb_filename);
	if (bg_filename)
		app_free(bg_filename);

	return template;
}

/* Hybrids with two colours blended horizontally. */
Image * form_card_template_2_7th_ed(Colour bg_border,  /* BLACK, WHITE, GOLD */
			char *bg_name1,         /* e.g. Blue or Land */
			char *tb_name1,         /* e.g. Blue or LandU */
			Colour tb_border1,      /* e.g. CLEAR or RED */
			char *bg_name2,         /* e.g. Purple or Land */
			char *tb_name2,         /* e.g. Purple or LandP */
			Colour tb_border2,      /* e.g. CLEAR or PURPLE */
			int is_flip)            /* is it a flip card? */
{
	/* This function draws hybrids or flip-cards hybrids. */
	Image *bg1, *tb1;
	Image *bg2, *tb2;
	char *bg_filename1, *tb_filename1;
	char *bg_filename2, *tb_filename2;
	Image *template1 = NULL;
	Image *template2 = NULL;

	if ((bg_name2 == NULL) && (tb_name2 == NULL))
	{
		/* Not a hybrid. Handle it as a normal or flip card. */
		return form_card_template_1_7th_ed(bg_border,
				bg_name1, tb_name1, tb_border1, is_flip);
	}

	if (bg_name2 == NULL)
		bg_name2 = bg_name1;
	if (tb_name2 == NULL)
		tb_name2 = tb_name1;

	/*
	 * Left card.
	 */

	/* Form a file name like BlueCard.png or LandCard.png */
	bg_filename1 = copy_string(bg_name1);
	bg_filename1 = append_string(bg_filename1, "Card.png");

	/* Form a file name like BlueTextBox.png or LandUTextBox.png */
	tb_filename1 = copy_string(tb_name1);
	tb_filename1 = append_string(tb_filename1, "TextBox.png");

	/* Try to load those files as images. */
	bg1 = find_cached_image(bg_filename1);
	tb1 = find_cached_image(tb_filename1);
	if (! tb1) {
		tb_filename1 = assign_string(tb_filename1, bg_name1);
		tb_filename1 = append_string(tb_filename1, "TextBox.png");
		tb1 = find_cached_image(bg_filename1);
	}

	/* Tidy up file name strings. */
	if (tb_filename1)
		app_free(tb_filename1);
	if (bg_filename1)
		app_free(bg_filename1);

	/* Create left side card template. */
	if (bg1 != NULL)
	{
		if (is_flip)
			template1 = form_flip_card_7th_ed(bg1, tb1, bg_border, tb_border1);
		else
			template1 = form_normal_card_7th_ed(bg1, tb1, bg_border, tb_border1);
	}

	/* If error, signal it. */
	if (template1 == NULL)
	{
		if (tb1)
			app_del_image(tb1);
		if (bg1)
			app_del_image(bg1);
		return NULL;
	}

	/*
	 * Right card.
	 */

	/* Form a file name like PurpleCard.png or LandCard.png */
	bg_filename2 = copy_string(bg_name2);
	bg_filename2 = append_string(bg_filename2, "Card.png");

	/* Form a file name like PurpleTextBox.png or LandPTextBox.png */
	tb_filename2 = copy_string(tb_name2);
	tb_filename2 = append_string(tb_filename2, "TextBox.png");

	/* Try to load those files as images, avoiding duplicated effort. */
	if (!strcmp(bg_name1, bg_name2))
		bg2 = bg1;
	else {
		/* Tidy up here to reduce peak memory usage. */
		if (bg1)
			app_del_image(bg1);
		bg2 = find_cached_image(bg_filename2);
	}

	if (!strcmp(tb_name1, tb_name2))
		tb2 = tb1;
	else {
		tb2 = find_cached_image(tb_filename2);
		if (! tb2)
		{
			tb_filename2 = assign_string(tb_filename2, bg_name2);
			tb_filename2 = append_string(tb_filename2, "TextBox.png");
			tb2 = find_cached_image(bg_filename2);
		}

		/* Tidy up here to reduce peak memory usage. */
		if (tb2 && tb1)
			app_del_image(tb1);
		if ((! tb2) && tb1)
			tb2 = tb1;
	}

	/* Tidy up file name strings. */
	if (tb_filename2)
		app_free(tb_filename2);
	if (bg_filename2)
		app_free(bg_filename2);

	/* Create right side card template. */
	if (bg2 != NULL)
	{
		if (is_flip)
			template2 = form_flip_card_7th_ed(bg2, tb2, bg_border, tb_border2);
		else
			template2 = form_normal_card_7th_ed(bg2, tb2, bg_border, tb_border2);
	}

	/* Tidy up. */
	if (tb2)
		app_del_image(tb2);
	if (bg2)
		app_del_image(bg2);

	/* If error, signal it. */
	if (template2 == NULL)
	{
		app_del_image(template1);
		return NULL;
	}

	/* Blend the hybrid templates left-right. */
	blend_two_images_32bpp_horizontally(template1, template2, BLEND_DIST);

	app_del_image(template2);

	return template1;
}


/* Hybrids with three colours blended horizontally. */
Image * form_card_template_3_7th_ed(Colour bg_border,  /* BLACK, WHITE, GOLD */
			char *bg_name1,         /* e.g. Blue or Land */
			char *tb_name1,         /* e.g. Blue or LandU */
			Colour tb_border1,      /* e.g. CLEAR or RED */
			char *bg_name2,         /* e.g. Purple or Land */
			char *tb_name2,         /* e.g. Purple or LandP */
			Colour tb_border2,      /* e.g. CLEAR or PURPLE */
			char *bg_name3,         /* e.g. Red or Land */
			char *tb_name3,         /* e.g. Red or LandR */
			Colour tb_border3,      /* e.g. CLEAR or GREEN */
			int is_flip)            /* is it a flip card? */
{
	/* This function draws hybrids or flip-cards hybrids. */
	Image *bg1, *tb1;
	Image *bg2, *tb2;
	Image *bg3, *tb3;
	char *bg_filename1, *tb_filename1;
	char *bg_filename2, *tb_filename2;
	char *bg_filename3, *tb_filename3;
	Image *template1 = NULL;
	Image *template2 = NULL;
	Image *template3 = NULL;

	if ((bg_name2 == NULL) && (tb_name2 == NULL))
	{
		/* Not a hybrid. Handle it as a normal or flip card. */
		return form_card_template_1_7th_ed(bg_border,
				bg_name1, tb_name1, tb_border1, is_flip);
	}
	else if ((bg_name3 == NULL) && (tb_name3 == NULL))
	{
		/* Two-colour hybrid. Handle it as a normal or flip card. */
		return form_card_template_2_7th_ed(bg_border,
				bg_name1, tb_name1, tb_border1,
				bg_name2, tb_name2, tb_border2,
				is_flip);
	}

	if (bg_name2 == NULL)
		bg_name2 = bg_name1;
	if (tb_name2 == NULL)
		tb_name2 = tb_name1;

	if (bg_name3 == NULL)
		bg_name3 = bg_name2;
	if (tb_name3 == NULL)
		tb_name3 = tb_name2;

	/*
	 * Left card.
	 */

	/* Form a file name like BlueCard.png or LandCard.png */
	bg_filename1 = copy_string(bg_name1);
	bg_filename1 = append_string(bg_filename1, "Card.png");

	/* Form a file name like BlueTextBox.png or LandUTextBox.png */
	tb_filename1 = copy_string(tb_name1);
	tb_filename1 = append_string(tb_filename1, "TextBox.png");

	/* Try to load those files as images. */
	bg1 = find_cached_image(bg_filename1);
	tb1 = find_cached_image(tb_filename1);
	if (! tb1) {
		tb_filename1 = assign_string(tb_filename1, bg_name1);
		tb_filename1 = append_string(tb_filename1, "TextBox.png");
		tb1 = find_cached_image(bg_filename1);
	}

	/* Tidy up file name strings. */
	if (tb_filename1)
		app_free(tb_filename1);
	if (bg_filename1)
		app_free(bg_filename1);

	/* Create left side card template. */
	if (bg1 != NULL)
	{
		if (is_flip)
			template1 = form_flip_card_7th_ed(bg1, tb1, bg_border, tb_border1);
		else
			template1 = form_normal_card_7th_ed(bg1, tb1, bg_border, tb_border1);
	}

	/* If error, signal it. */
	if (template1 == NULL)
	{
		if (tb1)
			app_del_image(tb1);
		if (bg1)
			app_del_image(bg1);
		return NULL;
	}


	/*
	 * Middle card.
	 */

	/* Form a file name like PurpleCard.png or LandCard.png */
	bg_filename2 = copy_string(bg_name2);
	bg_filename2 = append_string(bg_filename2, "Card.png");

	/* Form a file name like PurpleTextBox.png or LandPTextBox.png */
	tb_filename2 = copy_string(tb_name2);
	tb_filename2 = append_string(tb_filename2, "TextBox.png");

	/* Try to load those files as images, avoiding duplicated effort. */
	if (!strcmp(bg_name1, bg_name2))
		bg2 = bg1;
	else {
		/* Tidy up here to reduce peak memory usage. */
		if (bg1)
			app_del_image(bg1);
		bg2 = find_cached_image(bg_filename2);
	}

	if (!strcmp(tb_name1, tb_name2))
		tb2 = tb1;
	else {
		tb2 = find_cached_image(tb_filename2);
		if (! tb2)
		{
			tb_filename2 = assign_string(tb_filename2, bg_name2);
			tb_filename2 = append_string(tb_filename2, "TextBox.png");
			tb2 = find_cached_image(bg_filename2);
		}

		/* Tidy up here to reduce peak memory usage. */
		if (tb2 && tb1)
			app_del_image(tb1);
		if ((! tb2) && tb1)
			tb2 = tb1;
	}

	/* Tidy up file name strings. */
	if (tb_filename2)
		app_free(tb_filename2);
	if (bg_filename2)
		app_free(bg_filename2);

	/* Create middle card template. */
	if (bg2 != NULL)
	{
		if (is_flip)
			template2 = form_flip_card_7th_ed(bg2, tb2, bg_border, tb_border2);
		else
			template2 = form_normal_card_7th_ed(bg2, tb2, bg_border, tb_border2);
	}
	
	/* If error, signal it. */
	if (template2 == NULL)
	{
		app_del_image(template1);
		if (tb2)
			app_del_image(tb2);
		if (bg2)
			app_del_image(bg2);
		return NULL;
	}

	/*
	 * Right card.
	 */

	/* Form a file name like RedCard.png or LandCard.png */
	bg_filename3 = copy_string(bg_name3);
	bg_filename3 = append_string(bg_filename3, "Card.png");

	/* Form a file name like RedTextBox.png or LandGTextBox.png */
	tb_filename3 = copy_string(tb_name3);
	tb_filename3 = append_string(tb_filename3, "TextBox.png");

	/* Try to load those files as images, avoiding duplicated effort. */
	if (!strcmp(bg_name2, bg_name3))
		bg3 = bg2;
	else {
		/* Tidy up here to reduce peak memory usage. */
		if (bg2)
			app_del_image(bg2);
		bg3 = find_cached_image(bg_filename3);
	}

	if (!strcmp(tb_name2, tb_name3))
		tb3 = tb2;
	else {
		tb3 = find_cached_image(tb_filename3);
		if (! tb3)
		{
			tb_filename3 = assign_string(tb_filename3, bg_name3);
			tb_filename3 = append_string(tb_filename3, "TextBox.png");
			tb3 = find_cached_image(bg_filename3);
		}

		/* Tidy up here to reduce peak memory usage. */
		if (tb3 && tb2)
			app_del_image(tb2);
		if ((! tb3) && tb2)
			tb3 = tb2;
	}

	/* Tidy up file name strings. */
	if (tb_filename3)
		app_free(tb_filename3);
	if (bg_filename3)
		app_free(bg_filename3);

	/* Create right side card template. */
	if (bg3 != NULL)
	{
		if (is_flip)
			template3 = form_flip_card_7th_ed(bg3, tb3, bg_border, tb_border3);
		else
			template3 = form_normal_card_7th_ed(bg3, tb3, bg_border, tb_border3);
	}

	/* Tidy up. */
	if (tb3)
		app_del_image(tb3);
	if (bg3)
		app_del_image(bg3);

	/* If error, signal it. */
	if (template3 == NULL)
	{
		app_del_image(template2);
		app_del_image(template1);
		return NULL;
	}

	/* Blend the hybrid templates left-right. */
	blend_three_images_32bpp_horizontally(template1, template2, template3, BLEND_DIST);

	/* Final tidy up. */
	app_del_image(template3);
	app_del_image(template2);

	return template1;
}

