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

/*
 *  Composite together the pieces of an 8th Edition Magic card.
 */

/*
 *  Determine whether a card has a dark background.
 *  Used to decide whether to draw text at the bottom of a card
 *  in white.
 */
int has_dark_card_background_1(Card *card)
{
	if (card->bitfield & IS_ARTIFACT)
		return 0;
	if (card->num_colours >= 3)
		return 0;
	if ((card->bitfield & IS_MULTI) && (! (card->bitfield & IS_HYBRID)))
		return 0;
	if (card->bitfield & IS_LAND)
		return 1;
	if (card->bitfield & IS_HYBRID) {
		if (strcmp(card->bg1, "Black") != 0)
			return 0;
	}
	if (card->bitfield & IS_BLACK)
		return 1;
	return 0;
}

/*
 * Variant of the above function. The small text at the very
 * bottom of a card can sometimes be a different colour
 * to the artist's name.
 */
int has_dark_card_background_2(Card *card)
{
	if (card->bitfield & IS_ARTIFACT)
		return 0;
	if (card->num_colours >= 3)
		return 0;
	if ((card->bitfield & IS_MULTI) && (! (card->bitfield & IS_HYBRID)))
		return 0;
	if (card->bitfield & IS_LAND)
		return 1;
	if (card->bitfield & IS_BLACK)
		return 1;
	return 0;
}

/*
 *  Draw a normal card (as opposed to a flip card).
 *
 *  This function does all kinds of things like compositing the
 *  art onto the correct background colour, blending hybrid cards,
 *  drawing text for the card name, type, etc as well as scaling
 *  down font sizes to ensure rules text fits into the text box,
 *  and also draws the mana cost, expansion symbol, artist name.
 *
 *  In short: it does everything, except not flip cards or split cards.
 *  There are other functions below which handle those kinds of cards.
 */
Image * create_card_image_8th_ed_normal(Card *card, Colour border_colour)
{
	Graphics *g;
	Image *full;
	Image *art;
	Image *scaled_art;
	char *filename;
	Rect background	= rect(  0,   0,  744,     1042);
//	Rect borders	= rect( 35,  48,  704- 35,  952- 48);
	Rect powtgh 	= rect(546, 926,  693-546, 1000-926);
	Rect textbox	= rect( 58, 650,  684- 58-3,938-650);
//	Rect textbars	= rect( 46,  51,  697- 46,  650- 51);
	Rect namebar	= rect( 62,  58,  651- 32,  64);
	Rect costbar	= rect( 62,  51,  651- 32,  64);
	Rect typebar	= rect( 64, 587,  651- 36,  64);
	Rect artwork	= rect( 60, 122,  622,      456);
	Rect artsize	= rect(  0,   0,  622,      456);
	int has_powtgh	= 0;
	int costlen		= 0;
	char *costfnt	= NULL;
	int cost_width	= 0;

	if (card->powtgh && card->powtgh[0] && card->powtgh[0] != ' ')
		has_powtgh = 1;

	full = form_card_template_3_8th_ed(border_colour,
			card->bitfield, card->num_colours, card->num_genmana, card->num_hybrids,
			card->bg1, card->tb1,
			card->bg2, card->tb2,
			card->bg3, card->tb3,
			has_powtgh, 0);

	if (full == NULL) {
		if (logfile != NULL) {
			fprintf(logfile,
				"\tCouldn't read template image file %s\n", card->bg);
		}
		return NULL;
	}

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

	/* Measure the casting cost, using the symbol font. */
	if (card->cost[0])
	{
		costlen = (int) strlen(card->cost);
		costfnt = app_alloc(costlen + 1);
		memset(costfnt, MANA_FONT, costlen);
		cost_width = text_width(name8_fonts, full->width-60,
						card->cost, costlen, costfnt);
	}

	/* Draw the card name. */
	if (card->name[0])
	{
		int namelen = (int) strlen(card->name);
		int max_width = namebar.width - 10 - cost_width;
		Rect max_rect = namebar;
		
		max_rect.width = max_width;
		max_rect.height = name8_fonts[0]->height;

		narrow_text(g, name8_fonts,
					max_rect, (ALIGN_LEFT | VALIGN_TOP | LR_TB),
					card->name, namelen, card->namefnt);
	}

	/* Draw the card cost. */
	if (card->cost[0])
	{
		int nchars = app_utf8_length(card->cost);
		
		/* First draw a little shadow under where each mana symbol will go. */

		if (anti_aliased_circle != NULL)
		{
			int i;
			Rect mr = app_get_image_area(anti_aliased_circle);
			
			for (i = 0; i < nchars; i++)
			{
				Rect mana = rect(costbar.x + costbar.width - 33 - i * 34,
								costbar.y + costbar.height/2 - 13,
								mr.width,
								mr.height);
				app_draw_image(g, mana, anti_aliased_circle, mr);
			}
		}
		else {
			int i;
			
			for (i = 0; i < nchars; i++)
			{
				Rect mana = rect(costbar.x + costbar.width - 33 - i * 34,
								costbar.y + costbar.height/2 - 13,
								32,
								32);
				app_set_colour(g, argb(0xC0, 0xC0, 0xC0, 0xC0));
				app_fill_ellipse(g, app_inset_rect(mana, -2));
				app_set_colour(g, argb(0x80, 0x80, 0x80, 0x80));
				app_fill_ellipse(g, app_inset_rect(mana, -1));
				app_set_colour(g, BLACK);
				app_fill_ellipse(g, mana);
			}
			app_set_colour(g, BLACK);
		}

		/* Draw the mana symbols. */
		if (mana_font != NULL)
		{
			app_set_font(g, mana_font);
			app_draw_text(g, costbar,
					(ALIGN_RIGHT | VALIGN_CENTER | LR_TB),
					card->cost, costlen);
		}
		else
		{
			style_text(g, name8_fonts, costbar,
					(ALIGN_RIGHT | VALIGN_CENTER | LR_TB),
					card->cost, costlen, costfnt);
		}
	}
	if (costfnt)
		app_free(costfnt);

	/* Draw the card artwork. */
	if (card->artfile[0])
	{
		char *artfile = find_image_source("./", art_dir, card->artfile, "");
		if (artfile != NULL) {
			art = app_read_image(artfile, 32);
			app_free(artfile);
		}
		else
			art = NULL;

		if (art != NULL)
		{
			if ((art->width == artsize.width)
			 && (art->height == artsize.height))
			{
				app_draw_image(g, artwork, art, app_get_image_area(art));
			}
			else if ((art->width == full->width)
			      && (art->height == full->height)
			      && (art->data32[0][0].alpha == 0xFF))
			{
				/* Same size as full card, and top-left pixel is transparent */
				/* Might be Planeswalker art. Draw over entire card. */
				/* NB: This may overlap the card's name. That's desirable. */
				app_draw_image(g, background, art, background);
			}
			else
			{
				scaled_art = app_scale_image(art,
					artsize,
					largest_rect(app_get_image_area(art), artwork.width, artwork.height));
				app_draw_image(g, artwork,
					scaled_art, rect(0, 0, artwork.width, artwork.height));
				app_del_image(scaled_art);
		    }
			app_del_image(art);
		}
		else {
			if (logfile)
				fprintf(logfile, "Couldn't find or load artwork file %s\n", card->artfile);
		}
	}

	/* Draw the expansion symbol. */
	if (card->expsym[0])
		filename = card->expsym;
	else
		filename = default_expansion;
	filename = find_image_source("./", symbols, filename,
				rarity_to_trailer(filename, card->rarity));
	if (filename != NULL) {
		art = app_read_image(filename, 32);
		app_free(filename);
	}
	else
		art = NULL;

	if (art != NULL)
	{
		Rect sr = app_get_image_area(art);
		Rect dr;
		dr.y = typebar.y + 8;
		dr.height = 40;
		dr.width = sr.width * dr.height / sr.height;
		dr.x = full->width - 60 - dr.width;
		typebar.width = dr.x - typebar.x;
		app_draw_image(g, dr, art, sr);
		app_del_image(art);
	}

	/* Draw the type line. */
	if (card->type[0])
	{
		int typelen = (int) strlen(card->type);
		char *typefnt = app_alloc(typelen + 1);
		memset(typefnt, PLAIN_FONT, typelen);

		narrow_text(g, type8_fonts,
					typebar, (ALIGN_LEFT | VALIGN_CENTRE | LR_TB),
					card->type, typelen, typefnt);
		app_free(typefnt);
	}

	/* Draw the rules text and/or flavor text. */
	if ((card->text[0]) || (card->flavor[0]))
	{
		fit_text(g, fonts_8th_ed,
			app_inset_rect(textbox, 5),
			(ALIGN_LEFT | LR_TB),
			40, 16,
			card->text, (int) strlen(card->text), card->textfnt,
			card->flavor, (int) strlen(card->flavor), card->flavfnt,
			1);
	}

	/* Draw the power/toughness. */
	if (card->powtgh[0])
	{
		app_set_font(g, name8_fonts[PLAIN_FONT]);
		app_draw_text(g, app_inset_rect(powtgh, 3),
				(ALIGN_CENTRE | VALIGN_CENTRE | LR_TB),
				card->powtgh, (int) strlen(card->powtgh));
	}

	/* Draw the artist. */
	if (has_dark_card_background_1(card)) {
		app_set_colour(g, WHITE);
	}
	if (card->artist[0])
	{
		app_set_font(g, artist8_font);
		app_draw_text(g, rect(58, 940, 500, 60),
				(ALIGN_LEFT | VALIGN_CENTRE | LR_TB),
				card->artist, (int) strlen(card->artist));
	}
	app_set_colour(g, BLACK);

	/* Draw the card number. */
	if (has_dark_card_background_2(card)) {
		app_set_colour(g, WHITE);
	}
	if (card->number[0] || card->designer[0])
	{
		int designerlen;
		int numberlen;
		char *legend;

		designerlen = (int) strlen(card->designer);
		numberlen = (int) strlen(card->number);
		legend = app_alloc(10 + designerlen + numberlen);
		legend[0] = '\0';
		strcat(legend, card->designer);
		strcat(legend, " ");
		strcat(legend, card->number);

		app_set_font(g, number8_font);
		app_draw_text(g, rect(58, 980, 500, 40),
				(ALIGN_LEFT | VALIGN_CENTRE | LR_TB),
				legend, (int) strlen(legend));
		app_free(legend);
	}
	app_set_colour(g, BLACK);

	app_del_graphics(g);

	return full;
}

/*
 *  An alternative to the function above, which draws a flip-card.
 */
Image * create_card_image_8th_ed_flipped(Card *card, Colour border_colour)
{
	Graphics *g;
	Image *full;
	Image *art;
	Image *scaled_art;
	Image *rot;
	char *filename;
	const int gap   = 46;
//	Rect background	= rect(  0,   0,  744,     1042);
//	Rect borders	= rect( 35,  48,  704- 35,  952- 48);
	Rect powtgh 	= rect(546, 243,  693-546, 1000-926);
	Rect textbox	= rect( 58, 120,  684- 58-3,118);
//	Rect textbars	= rect( 46,  51,  697- 46,  650- 51);
	Rect namebar	= rect( 62,  58,  651- 32,  64);
	Rect costbar	= rect( 62,  51,  651- 32,  64);
	Rect typebar	= rect( 64, 246,  651- 36,  64);
	Rect artwork	= rect( 60, 316,  622,      362);
	Rect artsize	= rect(  0,   0,  622,      362);
	int has_powtgh	= 0;
	int costlen		= 0;
	char *costfnt	= NULL;
	int cost_width	= 0;
	int art_done    = 0;
	Card *flip      = card->flip;

	if (logfile && flip) {
		fprintf(logfile, "Viewing flip-card %s /// %s\n", card->name, flip->name);
		fflush(logfile);
	}

	if (card->powtgh && card->powtgh[0] && card->powtgh[0] != ' ')
		has_powtgh = 1;
	if (flip->powtgh && flip->powtgh[0] && flip->powtgh[0] != ' ')
		has_powtgh |= 2;

	full = form_card_template_3_8th_ed(border_colour,
			card->bitfield, card->num_colours, card->num_genmana, card->num_hybrids,
			card->bg1, card->tb1,
			card->bg2, card->tb2,
			card->bg3, card->tb3,
			has_powtgh, 1);

	if (flip->bg[0]) {
		rot = form_card_template_3_8th_ed(border_colour,
			flip->bitfield, flip->num_colours, flip->num_genmana, flip->num_hybrids,
			flip->bg1, flip->tb1,
			flip->bg2, flip->tb2,
			flip->bg3, flip->tb3,
			has_powtgh, 1);

		rotate_image_32bpp_180_degrees(rot);
	}
	else {
		rot = NULL;
	}

	if (full == NULL) {
		if (logfile != NULL) {
			fprintf(logfile,
				"\tCouldn't make template image file %s\n", card->bg);
		}
		return NULL;
	}

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

	/* Measure the casting cost, using the symbol font. */
	if (card->cost[0])
	{
		costlen = (int) strlen(card->cost);
		costfnt = app_alloc(costlen + 1);
		memset(costfnt, MANA_FONT, costlen);
		cost_width = text_width(name8_fonts, full->width-60,
						card->cost, costlen, costfnt);
	}

	/* Draw the card name. */
	if (card->name[0])
	{
		int namelen = (int) strlen(card->name);
		int max_width = namebar.width - 10 - cost_width;
		Rect max_rect = namebar;
		
		max_rect.width = max_width;
		max_rect.height = name8_fonts[0]->height;

		narrow_text(g, name8_fonts,
					max_rect, (ALIGN_LEFT | VALIGN_TOP | LR_TB),
					card->name, namelen, card->namefnt);
	}

	/* Draw the card cost. */
	if (card->cost[0])
	{
		int nchars = app_utf8_length(card->cost);
		
		/* First draw a little shadow under where each mana symbol will go. */

		if (anti_aliased_circle != NULL)
		{
			int i;
			Rect mr = app_get_image_area(anti_aliased_circle);
			
			for (i = 0; i < nchars; i++)
			{
				Rect mana = rect(costbar.x + costbar.width - 33 - i * 34,
								costbar.y + costbar.height/2 - 13,
								mr.width,
								mr.height);
				app_draw_image(g, mana, anti_aliased_circle, mr);
			}
		}
		else {
			int i;
			
			for (i = 0; i < nchars; i++)
			{
				Rect mana = rect(costbar.x + costbar.width - 33 - i * 34,
								costbar.y + costbar.height/2 - 13,
								32,
								32);
				app_set_colour(g, argb(0xC0, 0xC0, 0xC0, 0xC0));
				app_fill_ellipse(g, app_inset_rect(mana, -2));
				app_set_colour(g, argb(0x80, 0x80, 0x80, 0x80));
				app_fill_ellipse(g, app_inset_rect(mana, -1));
				app_set_colour(g, BLACK);
				app_fill_ellipse(g, mana);
			}
			app_set_colour(g, BLACK);
		}

		/* Draw the mana symbols. */
		if (mana_font != NULL)
		{
			app_set_font(g, mana_font);
			app_draw_text(g, costbar,
					(ALIGN_RIGHT | VALIGN_CENTER | LR_TB),
					card->cost, costlen);
		}
		else
		{
			style_text(g, name8_fonts, costbar,
					(ALIGN_RIGHT | VALIGN_CENTER | LR_TB),
					card->cost, costlen, costfnt);
		}
	}
	if (costfnt) {
		app_free(costfnt);
		costfnt = NULL;
	}

	/* Draw the type bar line. */
	if (has_powtgh & 1)
		typebar.width -= powtgh.width;
	if (card->type[0])
	{
		int typelen = (int) strlen(card->type);
		char *typefnt = app_alloc(typelen + 1);
		memset(typefnt, PLAIN_FONT, typelen);

		narrow_text(g, type8_fonts,
					typebar,
					(ALIGN_LEFT | VALIGN_CENTRE | LR_TB),
					card->type, typelen, typefnt);
		app_free(typefnt);
	}
	if (has_powtgh & 1)
		typebar.width += powtgh.width;

	/* Draw the rules text. */
	if (card->text[0])
	{
		fit_text(g, fonts_8th_ed,
			app_inset_rect(textbox, 5),
			(ALIGN_LEFT | LR_TB),
			40, 16,
			card->text,   (int) strlen(card->text),   card->textfnt,
			card->flavor, (int) strlen(card->flavor), card->flavfnt,
			1);
	}

	/* Draw the power/toughness. */
	if (card->powtgh[0])
	{
		app_set_font(g, name8_fonts[PLAIN_FONT]);
		app_draw_text(g, app_inset_rect(powtgh, 3),
				(ALIGN_CENTRE | VALIGN_CENTRE | LR_TB),
				card->powtgh, (int) strlen(card->powtgh));
	}

	/* Now draw the flipped attributes. */
	
	if (rot) {
		app_del_graphics(g);
		g = app_get_image_graphics(rot);
	}
	else {
		rotate_image_32bpp_180_degrees(full);
	}

	/* Measure the flipped casting cost, if any, using the symbol font. */
	cost_width = 0;
	if (flip->cost[0])
	{
		costlen = (int) strlen(flip->cost);
		costfnt = app_alloc(costlen + 1);
		memset(costfnt, MANA_FONT, costlen);
		cost_width = text_width(name8_fonts, full->width-60,
						flip->cost, costlen, costfnt);
	}

	/* Draw the flipped card name. */
	namebar.y += gap;
	if (flip->name[0])
	{
		int namelen = (int) strlen(flip->name);
		int max_width = namebar.width - 10 - cost_width;
		Rect max_rect = namebar;

		max_rect.width = max_width;
		max_rect.height = name8_fonts[0]->height;

		narrow_text(g, name8_fonts,
					max_rect, (ALIGN_LEFT | VALIGN_TOP | LR_TB),
					flip->name, namelen, flip->namefnt);
	}

	/* Draw the flipped card cost, if any. */
	costbar.y += gap;
	if (flip->cost[0])
	{
		int nchars = app_utf8_length(flip->cost);
		
		/* First draw a little shadow under where each mana symbol will go. */

		if (anti_aliased_circle != NULL)
		{
			int i;
			Rect mr = app_get_image_area(anti_aliased_circle);
			
			for (i = 0; i < nchars; i++)
			{
				Rect mana = rect(costbar.x + costbar.width - 33 - i * 34,
								costbar.y + costbar.height/2 - 13,
								mr.width,
								mr.height);
				app_draw_image(g, mana, anti_aliased_circle, mr);
			}
		}
		else {
			int i;
			
			for (i = 0; i < nchars; i++)
			{
				Rect mana = rect(costbar.x + costbar.width - 33 - i * 34,
								costbar.y + costbar.height/2 - 13,
								32,
								32);
				app_set_colour(g, argb(0xC0, 0xC0, 0xC0, 0xC0));
				app_fill_ellipse(g, app_inset_rect(mana, -2));
				app_set_colour(g, argb(0x80, 0x80, 0x80, 0x80));
				app_fill_ellipse(g, app_inset_rect(mana, -1));
				app_set_colour(g, BLACK);
				app_fill_ellipse(g, mana);
			}
			app_set_colour(g, BLACK);
		}

		/* Draw the mana symbols. */
		if (mana_font != NULL)
		{
			app_set_font(g, mana_font);
			app_draw_text(g, costbar,
					(ALIGN_RIGHT | VALIGN_CENTER | LR_TB),
					flip->cost, costlen);
		}
		else
		{
			style_text(g, name8_fonts, costbar,
					(ALIGN_RIGHT | VALIGN_CENTER | LR_TB),
					flip->cost, costlen, costfnt);
		}
	}
	if (costfnt)
		app_free(costfnt);

	/* Draw the flipped type bar. */
	typebar.y += gap;
	if (has_powtgh & 2)
		typebar.width -= powtgh.width;
	if (flip->type[0])
	{
		int typelen = (int) strlen(flip->type);
		char *typefnt = app_alloc(typelen + 1);
		memset(typefnt, PLAIN_FONT, typelen);

		narrow_text(g, type8_fonts,
					typebar,
					(ALIGN_LEFT | VALIGN_CENTRE | LR_TB),
					flip->type, typelen, typefnt);
		app_free(typefnt);
	}
	if (has_powtgh & 2)
		typebar.width += powtgh.width;

	/* Draw the flipped rules text. */
	textbox.y += gap;
	if (flip->text[0])
	{
		fit_text(g, fonts_8th_ed,
			app_inset_rect(textbox, 5),
			(ALIGN_LEFT | LR_TB),
			40, 16,
			flip->text,   (int) strlen(flip->text),   flip->textfnt,
			flip->flavor, (int) strlen(flip->flavor), flip->flavfnt,
			1);
	}

	/* Draw the flipped power/toughness, if any. */
	powtgh.y += gap;
	if (flip->powtgh[0])
	{
		app_set_font(g, name8_fonts[PLAIN_FONT]);
		app_draw_text(g, app_inset_rect(powtgh, 3),
				(ALIGN_CENTRE | VALIGN_CENTRE | LR_TB),
				flip->powtgh, (int) strlen(flip->powtgh));
	}

	/* Rotate the flipped back into the normal orientation. */
	if (rot) {
		rotate_image_32bpp_180_degrees(rot);
		blend_two_images_32bpp_vertically(full, rot, BLEND_DIST);
		app_del_image(rot);
	}
	else {
		rotate_image_32bpp_180_degrees(full);
	}

	app_del_graphics(g);

	/* Draw artwork and other components. */
	
	g = app_get_image_graphics(full);

	if ((card->artfile[0] != '\0') && (flip->artfile[0] != '\0'))
	{
		/* Blend the art, the flipped art is rotated first. */
		char *artfile = find_image_source("./", art_dir,
						card->artfile, "");
		if (artfile != NULL) {
			art = app_read_image(artfile, 32);
			app_free(artfile);
		}
		else {
			art = NULL;
			if (logfile) {
				fprintf(logfile, "Couldn't find artwork file %s\n", card->artfile);
				fflush(logfile);
			}
		}

		artfile = find_image_source("./", art_dir,
						flip->artfile, "");
		if (artfile != NULL) {
			rot = app_read_image(artfile, 32);
			app_free(artfile);
		}
		else {
			rot = NULL;
			if (logfile) {
				fprintf(logfile, "Couldn't find artwork file %s\n", flip->artfile);
				fflush(logfile);
			}
		}

		if ((art != NULL) && (rot != NULL))
		{
			Graphics *g2;
			const int overlap = 30;
			Rect half = rect(0, 0, (artsize.width + overlap) / 2, artsize.height);

			/* Scale the upright card art. */
			scaled_art = app_scale_image(art,
					half,
					largest_rect(app_get_image_area(art), half.width, artsize.height));
			app_del_image(art);

			/* Make a third-white image from scaled art. */
			art = app_new_image(artsize.width, artsize.height, 32);
			g2 = app_get_image_graphics(art);
			app_draw_image(g2, half, scaled_art, half);
			app_del_graphics(g2);
			app_del_image(scaled_art);

			/* Scale the rotated flip card art */
			rotate_image_32bpp_180_degrees(rot);

			scaled_art = app_scale_image(rot,
					half,
					largest_rect(app_get_image_area(rot), half.width, artsize.height));
			app_del_image(rot);

			/* Make a third-white image from rotated art. */
			rot = app_new_image(artsize.width, artsize.height, 32);
			g2 = app_get_image_graphics(rot);
			app_draw_image(g2, rect(half.width-overlap,half.y,half.width,half.height),
					scaled_art, half);
			app_del_graphics(g2);
			app_del_image(scaled_art);

			/* Blend then draw. */
			blend_two_images_32bpp_horizontally(art,
						rot, BLEND_FLIP_ART);

			app_draw_image(g, artwork, art, artsize);

			art_done = 1;
		}
		if (art)
			app_del_image(art);
		else if (logfile)
			fprintf(logfile, "Couldn't load artwork file %s\n", card->artfile);
		if (rot)
			app_del_image(rot);
		else if (logfile)
			fprintf(logfile, "Couldn't load artwork file %s\n", flip->artfile);
	}
	if ((! art_done) && (card->artfile[0] != '\0'))
	{
		char *artfile = find_image_source("./", art_dir,
						card->artfile, "");
		if (artfile != NULL) {
			art = app_read_image(artfile, 32);
			app_free(artfile);
		}
		else
			art = NULL;

		if (art != NULL)
		{
			scaled_art = app_scale_image(art,
					artsize,
					largest_rect(app_get_image_area(art), artsize.width, artsize.height));
			app_draw_image(g, artwork, scaled_art, artsize);
			app_del_image(scaled_art);
			app_del_image(art);
			art_done = 1;
		}
		else {
			if (logfile)
				fprintf(logfile, "Couldn't find or load artwork file %s\n", card->artfile);
		}
	}
	if ((! art_done) && (flip->artfile[0] != '\0'))
	{
		char *artfile = find_image_source("./", art_dir,
						flip->artfile, "");
		if (artfile != NULL) {
			art = app_read_image(artfile, 32);
			app_free(artfile);
		}
		else
			art = NULL;

		if (art != NULL)
		{
			scaled_art = app_scale_image(art,
					artsize,
					largest_rect(app_get_image_area(art), artsize.width, artsize.height));
			app_draw_image(g, artwork, scaled_art, artsize);
			app_del_image(scaled_art);
			app_del_image(art);
			art_done = 1;
		}
		else {
			if (logfile)
				fprintf(logfile, "Couldn't find or load artwork file %s\n", flip->artfile);
		}
	}

	/* Draw the expansion symbol at the bottom of the flipped card. */
	if (card->expsym[0])
		filename = card->expsym;
	else
		filename = default_expansion;
	filename = find_image_source("./", symbols, filename,
				rarity_to_trailer(filename, card->rarity));
	if (filename != NULL) {
		art = app_read_image(filename, 32);
		app_free(filename);
	}
	else
		art = NULL;

	if (art != NULL)
	{
		Rect sr = app_get_image_area(art);
		Rect dr;
		dr.y = 970;
		dr.height = 40;
		dr.width = sr.width * dr.height / sr.height;
		dr.x = full->width - 60 - dr.width;
		app_draw_image(g, dr, art, sr);
		app_del_image(art);
	}

	/* Draw the artist. */
	if (has_dark_card_background_1(card)) {
		app_set_colour(g, WHITE);
	}
	if (card->artist[0])
	{
		app_set_font(g, artist8_font);
		app_draw_text(g, rect(58, 940, 500, 60),
				(ALIGN_LEFT | VALIGN_CENTRE | LR_TB),
				card->artist, (int) strlen(card->artist));
	}
	app_set_colour(g, BLACK);

	/* Draw the card number. */
	if (has_dark_card_background_2(card)) {
		app_set_colour(g, WHITE);
	}
	if (card->number[0] || card->designer[0])
	{
		int designerlen;
		int numberlen;
		char *legend;

		designerlen = (int) strlen(card->designer);
		numberlen = (int) strlen(card->number);
		legend = app_alloc(10 + designerlen + numberlen);
		legend[0] = '\0';
		strcat(legend, card->designer);
		strcat(legend, " ");
		strcat(legend, card->number);

		app_set_font(g, number8_font);
		app_draw_text(g, rect(58, 980, 500, 40),
				(ALIGN_LEFT | VALIGN_CENTRE | LR_TB),
				legend, (int) strlen(legend));
		app_free(legend);
	}
	app_set_colour(g, BLACK);

	app_del_graphics(g);

	return full;
}

/*
 * Utility for finding Planeswalker abilities and their costs.
 */
const char *
find_planeswalker_ability_and_cost
	(const char *ability, int *abilitylenp, char cost[], int *costlenp)
{
	const char *s = ability;
	int costlen = 0;
	int c = 0;
	int found_cost = 0;

	if ((s[c] == '+') || (s[c] == '-') || (s[c] == '0')) {
		cost[costlen++] = s[c++];
		cost[costlen] = '\0';
	}
	if (((s[c] >= '0') && (s[c] <= '9')) || (s[c] == 'X')) {
		cost[costlen++] = s[c++];
		cost[costlen] = '\0';
	}
	if (((s[c] >= '0') && (s[c] <= '9')) || (s[c] == 'X')) {
		cost[costlen++] = s[c++];
		cost[costlen] = '\0';
	}
	if (s[c] == ':') {
		c++;
		found_cost++;
		while (s[c] == ' ') {
			c++;
		}
	}
	if (found_cost) {
		*costlenp = costlen;
		*abilitylenp = c;
	}
	else {
		*costlenp = 0;
		*abilitylenp = 0;
	}
	return s + c;
}

/*
 * Create a Planeswalker card.
 */
Image * create_card_image_8th_ed_planeswalker(Card *card, Colour border_colour)
{
	Graphics *g;
	Image *full;
	Image *art;
	Image *scaled_art;
	char *filename;
	Rect background	= rect(  0,   0,  744,     1042);
//	Rect borders	= rect( 35,  48,  704- 35,  952- 48);
//	Rect powtgh 	= rect(546, 926,  693-546, 1000-926);
	Rect textbox	= rect( 58, 650,  684- 58-3,938-650);
//	Rect textbars	= rect( 46,  51,  697- 46,  650- 51);
	Rect namebar	= rect( 62,  58,  651- 32,  64);
	Rect costbar	= rect( 62,  51,  651- 32,  64);
	Rect typebar	= rect( 64, 587,  651- 36,  64);
	Rect artwork	= rect( 60, 122,  622,      456);
	Rect artsize	= rect(  0,   0,  622,      456);
	int costlen		= 0;
	char *costfnt	= NULL;
	int cost_width	= 0;

	full = form_card_template_3_8th_ed(border_colour,
			card->bitfield, card->num_colours, card->num_genmana, card->num_hybrids,
			card->bg1, card->tb1,
			card->bg2, card->tb2,
			card->bg3, card->tb3,
			0, /* No pow/tgh box, since Planeswalker loyalty is drawn later. */
			0);

	if (full == NULL) {
		if (logfile != NULL) {
			fprintf(logfile,
				"\tCouldn't read template image file %s\n", card->bg);
		}
		return NULL;
	}

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

	/* Measure the casting cost, using the symbol font. */
	if (card->cost[0])
	{
		costlen = (int) strlen(card->cost);
		costfnt = app_alloc(costlen + 1);
		memset(costfnt, MANA_FONT, costlen);
		cost_width = text_width(name8_fonts, full->width-60,
						card->cost, costlen, costfnt);
	}

	/* Draw the card name. */
	if (card->name[0])
	{
		int namelen = (int) strlen(card->name);
		int max_width = namebar.width - 10 - cost_width;
		Rect max_rect = namebar;
		
		max_rect.width = max_width;
		max_rect.height = name8_fonts[0]->height;

		narrow_text(g, name8_fonts,
					max_rect, (ALIGN_LEFT | VALIGN_TOP | LR_TB),
					card->name, namelen, card->namefnt);
	}

	/* Draw the card cost. */
	if (card->cost[0])
	{
		int nchars = app_utf8_length(card->cost);
		
		/* First draw a little shadow under where each mana symbol will go. */

		if (anti_aliased_circle != NULL)
		{
			int i;
			Rect mr = app_get_image_area(anti_aliased_circle);
			
			for (i = 0; i < nchars; i++)
			{
				Rect mana = rect(costbar.x + costbar.width - 33 - i * 34,
								costbar.y + costbar.height/2 - 13,
								mr.width,
								mr.height);
				app_draw_image(g, mana, anti_aliased_circle, mr);
			}
		}
		else {
			int i;
			
			for (i = 0; i < nchars; i++)
			{
				Rect mana = rect(costbar.x + costbar.width - 33 - i * 34,
								costbar.y + costbar.height/2 - 13,
								32,
								32);
				app_set_colour(g, argb(0xC0, 0xC0, 0xC0, 0xC0));
				app_fill_ellipse(g, app_inset_rect(mana, -2));
				app_set_colour(g, argb(0x80, 0x80, 0x80, 0x80));
				app_fill_ellipse(g, app_inset_rect(mana, -1));
				app_set_colour(g, BLACK);
				app_fill_ellipse(g, mana);
			}
			app_set_colour(g, BLACK);
		}

		/* Draw the mana symbols. */
		if (mana_font != NULL)
		{
			app_set_font(g, mana_font);
			app_draw_text(g, costbar,
					(ALIGN_RIGHT | VALIGN_CENTER | LR_TB),
					card->cost, costlen);
		}
		else
		{
			style_text(g, name8_fonts, costbar,
					(ALIGN_RIGHT | VALIGN_CENTER | LR_TB),
					card->cost, costlen, costfnt);
		}
	}
	if (costfnt)
		app_free(costfnt);

	/* Draw the card artwork. */
	if (card->artfile[0])
	{
		char *artfile = find_image_source("./", art_dir, card->artfile, "");
		if (artfile != NULL) {
			art = app_read_image(artfile, 32);
			app_free(artfile);
		}
		else
			art = NULL;

		if (art != NULL)
		{
			if ((art->width == artsize.width)
			 && (art->height == artsize.height))
			{
				app_draw_image(g, artwork, art, app_get_image_area(art));
			}
			else if ((art->width == full->width)
			      && (art->height == full->height)
			      && (art->data32[0][0].alpha == 0xFF))
			{
				/* Same size as full card, and top-left pixel is transparent */
				/* Might be Planeswalker art. Draw over entire card. */
				/* NB: This may overlap the card's name. That's desirable. */
				app_draw_image(g, background, art, background);
			}
			else
			{
				scaled_art = app_scale_image(art,
					artsize,
					largest_rect(app_get_image_area(art), artwork.width, artwork.height));
				app_draw_image(g, artwork,
					scaled_art, rect(0, 0, artwork.width, artwork.height));
				app_del_image(scaled_art);
		    }
			app_del_image(art);
		}
		else {
			if (logfile)
				fprintf(logfile, "Couldn't find or load artwork file %s\n", card->artfile);
		}
	}

	/* Draw the expansion symbol. */
	if (card->expsym[0])
		filename = card->expsym;
	else
		filename = default_expansion;
	filename = find_image_source("./", symbols, filename,
				rarity_to_trailer(filename, card->rarity));
	if (filename != NULL) {
		art = app_read_image(filename, 32);
		app_free(filename);
	}
	else
		art = NULL;

	if (art != NULL)
	{
		Rect sr = app_get_image_area(art);
		Rect dr;
		dr.y = typebar.y + 8;
		dr.height = 40;
		dr.width = sr.width * dr.height / sr.height;
		dr.x = full->width - 60 - dr.width;
		typebar.width = dr.x - typebar.x;
		app_draw_image(g, dr, art, sr);
		app_del_image(art);
	}

	/* Draw the type line. */
	if (card->type[0])
	{
		int typelen = (int) strlen(card->type);
		char *typefnt = app_alloc(typelen + 1);
		memset(typefnt, PLAIN_FONT, typelen);

		narrow_text(g, type8_fonts,
					typebar, (ALIGN_LEFT | VALIGN_CENTRE | LR_TB),
					card->type, typelen, typefnt);
		app_free(typefnt);
	}

	/* Draw the rules text and/or flavor text. */
	if ((card->text[0]) || (card->flavor[0]))
	{
		int font_size;
		int min_font_size = 40;
		int intra_para_gap = 5;
		int loyalty_change_left = 30;
		int min_paragraph_height = 70 - intra_para_gap;
		int max_paragraph_height = textbox.height - 10;
		char **all_rules;
		int num_rules;
		int i;
		int fnt_offset;
		Font *fonts[4];
		Rect ruler;
		int left_gap = 60;
		Rect tr = app_inset_rect(textbox, 3);
		tr.x += left_gap;
		tr.width -= left_gap;
		
		all_rules = strarray_split(card->text, "\n\n");
		num_rules = strarray_get_length(all_rules);
		while ((num_rules > 0) && (all_rules[num_rules - 1][0] == '\0'))
		{
			--num_rules;
		}
		if (num_rules > 0) {
			max_paragraph_height = (tr.height - intra_para_gap * (num_rules - 1))
								/ num_rules;
		}
		if (max_paragraph_height < min_paragraph_height)
			max_paragraph_height = min_paragraph_height;

		/* Measure how small the font must be to fit it all on the card. */
		for (fnt_offset = i = 0; i < num_rules; i++) {
			char cost[5];
			int costlen;
			int c;
			char *s = all_rules[i];
			char *styles = card->textfnt + fnt_offset;
			int sbytes = (int) strlen(s);

			find_planeswalker_ability_and_cost(s, &c, cost, &costlen);

			font_size = fit_text(g, fonts_8th_ed,
				rect(tr.x, tr.y, tr.width, max_paragraph_height),
				(ALIGN_LEFT | LR_TB),
				min_font_size, 16,
				s + c, sbytes - c, styles + c,
				NULL, 0, NULL,
				0);
			if (font_size < min_font_size)
				min_font_size = font_size;
			/* Step to the next paragraph. */
			fnt_offset += sbytes + 2; // +2 to skip the \n\n
		}
		fonts[0] = fonts_8th_ed[0][min_font_size]; /* normal_fonts array */
		fonts[1] = fonts_8th_ed[1][min_font_size]; /* italic_fonts array */
		fonts[2] = fonts_8th_ed[2][min_font_size]; /* mana_fonts array */
		fonts[3] = NULL;
				
		ruler = tr;
		for (fnt_offset = i = 0; i < num_rules; i++) {
			char cost[5];
			int costlen;
			int c;
			int h;
			char *s = all_rules[i];
			char *styles = card->textfnt + fnt_offset;
			int sbytes = (int) strlen(s);
			
			find_planeswalker_ability_and_cost(s, &c, cost, &costlen);
			
			if (s[0] == '\0')
				break;
			/* Measure the height of the ability paragraph. */
			h = text_height(fonts, ruler.width, s + c, sbytes - c, styles + c);
			if (h < max_paragraph_height)
				h = max_paragraph_height;
			ruler.height = h;
			/* Darken the rules text box underneath every alternate ability. */
			if ((i % 2) == 1) {
				app_set_colour(g, argb(0xF0, 0, 0, 0));
				app_fill_rect(g, rect(textbox.x, ruler.y-5, textbox.width, h+10));
				app_set_colour(g, BLACK);
			}
			/* Draw the ability text. */
			app_set_colour(g, BLACK);
			style_text(g, fonts, ruler, ALIGN_LEFT | LR_TB | VALIGN_CENTER,
				s + c, sbytes - c, styles + c);
			/* If there was a cost associated with this ability, draw it. */
			if (costlen > 0) {
				int blur_shadow_inset_x = 8;
				int blur_shadow_inset_y = -2;
				Rect dr;
				Image *loy = NULL;
				if (cost[0] == '+') {
					loy = find_cached_image("Planeswalker_Up.png");
					blur_shadow_inset_y = +3;
				}
				else if (cost[0] == '0') {
					loy = find_cached_image("Planeswalker_Same.png");
					blur_shadow_inset_y = -5;
					blur_shadow_inset_x += 2;
				}
				else if (cost[0] == '-') {
					loy = find_cached_image("Planeswalker_Down.png");
					blur_shadow_inset_y = -11;
				}
				if (loy) {
					Rect lr = app_get_image_area(loy);
					dr = lr;
					dr.x = loyalty_change_left;
					dr.y = ruler.y + (ruler.height - lr.height) / 2;
					
					app_draw_image(g, dr, loy, lr);

					dr.x      += blur_shadow_inset_x;
					dr.width  -= blur_shadow_inset_x;
					dr.y      += blur_shadow_inset_y;
					dr.height -= blur_shadow_inset_y;
			
					app_set_colour(g, WHITE);
					app_set_font(g, type8_fonts[PLAIN_FONT]);
					app_draw_text(g, app_inset_rect(dr, 3),
							(ALIGN_CENTRE | VALIGN_CENTRE | LR_TB),
							cost, costlen);
				}
			}
			/* Step to the next paragraph. */
			fnt_offset += sbytes + 2; // +2 to skip the \n\n
			ruler.y += h + intra_para_gap;
		}
		strarray_del(all_rules);
	}

	/* Draw the Planeswalker Loyalty box and loyalty value (in white). */
	if (card->powtgh[0])
	{
		int blur_shadow_inset_x = 11;
		int blur_shadow_inset_y = -1;
		Rect dr = rect(586, 918, 119, 85);
		Image *loy = find_cached_image("Planeswalker_Loyalty.png");
		if (loy) {
			Rect lr = app_get_image_area(loy);
			dr = lr;
			dr.x = background.width - lr.width - 39;
			dr.y = background.height - lr.height - 39;
			
			app_draw_image(g, dr, loy, lr);
		}
		dr.x      += blur_shadow_inset_x;
		dr.width  -= blur_shadow_inset_x;
		dr.y      += blur_shadow_inset_y;
		dr.height -= blur_shadow_inset_y;

		app_set_colour(g, WHITE);
		app_set_font(g, name8_fonts[PLAIN_FONT]);
		app_draw_text(g, app_inset_rect(dr, 3),
				(ALIGN_CENTRE | VALIGN_CENTRE | LR_TB),
				card->powtgh, (int) strlen(card->powtgh));
	}

	/* Draw the artist. */
	if (has_dark_card_background_1(card)) {
		app_set_colour(g, WHITE);
	}
	if (card->artist[0])
	{
		app_set_font(g, artist8_font);
		app_draw_text(g, rect(58, 940, 500, 60),
				(ALIGN_LEFT | VALIGN_CENTRE | LR_TB),
				card->artist, (int) strlen(card->artist));
	}
	app_set_colour(g, BLACK);

	/* Draw the card number. */
	if (has_dark_card_background_2(card)) {
		app_set_colour(g, WHITE);
	}
	if (card->number[0] || card->designer[0])
	{
		int designerlen;
		int numberlen;
		char *legend;

		designerlen = (int) strlen(card->designer);
		numberlen = (int) strlen(card->number);
		legend = app_alloc(10 + designerlen + numberlen);
		legend[0] = '\0';
		strcat(legend, card->designer);
		strcat(legend, " ");
		strcat(legend, card->number);

		app_set_font(g, number8_font);
		app_draw_text(g, rect(58, 980, 500, 40),
				(ALIGN_LEFT | VALIGN_CENTRE | LR_TB),
				legend, (int) strlen(legend));
		app_free(legend);
	}
	app_set_colour(g, BLACK);

	app_del_graphics(g);

	return full;
}

/*
 * In-place image modification.
 * Simple reduction of non-primary colours. Stupid algorithm.
 */
void saturate_image32_rect(Image *dst, Rect dr, int delta)
{
	int x, y;
	Colour c;
	
	for (y = 0; y < dr.height; y++)
	{
		for (x = 0; x < dr.width; x++)
		{
			c = dst->data32[dr.y+y][dr.x+x];
			if ((c.red > c.green) && (c.red > c.blue))
			{
				if (c.green > delta)
					c.green -= delta;
				else
					c.green = 0;

				if (c.blue > delta)
					c.blue -= delta;
				else
					c.blue = 0;
			}
			else if ((c.green > c.red) && (c.green > c.blue))
			{
				if (c.red > delta)
					c.red -= delta;
				else
					c.red = 0;

				if (c.blue > delta)
					c.blue -= delta;
				else
					c.blue = 0;
			}
			else if ((c.blue > c.red) && (c.blue > c.green))
			{
				if (c.red > delta)
					c.red -= delta;
				else
					c.red = 0;

				if (c.green > delta)
					c.green -= delta;
				else
					c.green = 0;
			}
			else if ((c.red == c.green) && (c.red > c.blue))
			{
				if (c.blue > delta)
					c.blue -= delta;
				else
					c.blue = 0;
			}
			else if ((c.green == c.blue) && (c.green > c.red))
			{
				if (c.red > delta)
					c.red -= delta;
				else
					c.red = 0;
			}
			else if ((c.blue == c.red) && (c.blue > c.green))
			{
				if (c.green > delta)
					c.green -= delta;
				else
					c.green = 0;
			}
			else {
				if (c.red > delta)
					c.red -= delta;
				else
					c.red = 0;

				if (c.green > delta)
					c.green -= delta;
				else
					c.green = 0;

				if (c.blue > delta)
					c.blue -= delta;
				else
					c.blue = 0;
			}
			dst->data32[dr.y+y][dr.x+x] = c;
		}
	}
}

/*
 * Return a more-saturated copy of an image.
 * Simple reduction of non-primary colours. Stupid algorithm.
 */
Image *copy_image32_saturated(Image *src, int delta)
{
	Image *img = app_copy_image(src);
	saturate_image32_rect(img, app_get_image_area(img), delta);
	return img;
}

/*
 * Create a Level Up card.
 */
Image * create_card_image_8th_ed_levelup(Card *card, Colour border_colour)
{
	Graphics *g;
	Image *full;
	Image *art;
	Image *scaled_art;
	char *filename;
	Rect background	= rect(  0,   0,  744,     1042);
//	Rect borders	= rect( 35,  48,  704- 35,  952- 48);
//	Rect powtgh 	= rect(546, 926,  693-546, 1000-926);
	Rect textbox	= rect( 58, 650,  684- 58-3,938-650);
//	Rect textbars	= rect( 46,  51,  697- 46,  650- 51);
	Rect namebar	= rect( 62,  58,  651- 32,  64);
	Rect costbar	= rect( 62,  51,  651- 32,  64);
	Rect typebar	= rect( 64, 587,  651- 36,  64);
	Rect artwork	= rect( 60, 122,  622,      456);
	Rect artsize	= rect(  0,   0,  622,      456);
	int costlen		= 0;
	char *costfnt	= NULL;
	int cost_width	= 0;

	full = form_card_template_3_8th_ed(border_colour,
			card->bitfield, card->num_colours, card->num_genmana, card->num_hybrids,
			card->bg1, card->tb1,
			card->bg2, card->tb2,
			card->bg3, card->tb3,
			0, /* No pow/tgh box, since Level Up arrows and power/tgh drawn later. */
			0);

	if (full == NULL) {
		if (logfile != NULL) {
			fprintf(logfile,
				"\tCouldn't read template image file %s\n", card->bg);
			fflush(logfile);
		}
		return NULL;
	}

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

	/* Draw the card artwork. */
	if (card->artfile[0])
	{
		char *artfile = find_image_source("./", art_dir, card->artfile, "");
		if (artfile != NULL) {
			art = app_read_image(artfile, 32);
			app_free(artfile);
		}
		else
			art = NULL;

		if (art != NULL)
		{
			if ((art->width == artsize.width)
			 && (art->height == artsize.height))
			{
				app_draw_image(g, artwork, art, app_get_image_area(art));
			}
			else if ((art->width == full->width)
			      && (art->height == full->height)
			      && (art->data32[0][0].alpha == 0xFF))
			{
				/* Same size as full card, and top-left pixel is transparent */
				/* Full card art. Draw over entire card. */
				app_draw_image(g, background, art, background);
			}
			else
			{
				scaled_art = app_scale_image(art,
					artsize,
					largest_rect(app_get_image_area(art), artwork.width, artwork.height));
				app_draw_image(g, artwork,
					scaled_art, rect(0, 0, artwork.width, artwork.height));
				app_del_image(scaled_art);
		    }
			app_del_image(art);
		}
		else {
			if (logfile)
				fprintf(logfile, "Couldn't find or load artwork file %s\n", card->artfile);
		}
	}

	/* Measure the casting cost, using the symbol font. */
	if (card->cost[0])
	{
		costlen = (int) strlen(card->cost);
		costfnt = app_alloc(costlen + 1);
		memset(costfnt, MANA_FONT, costlen);
		cost_width = text_width(name8_fonts, full->width-60,
						card->cost, costlen, costfnt);
	}

	/* Draw the card name. */
	if (card->name[0])
	{
		int namelen = (int) strlen(card->name);
		int max_width = namebar.width - 10 - cost_width;
		Rect max_rect = namebar;
		
		max_rect.width = max_width;
		max_rect.height = name8_fonts[0]->height;

		narrow_text(g, name8_fonts,
					max_rect, (ALIGN_LEFT | VALIGN_TOP | LR_TB),
					card->name, namelen, card->namefnt);
	}

	/* Draw the card cost. */
	if (card->cost[0])
	{
		int nchars = app_utf8_length(card->cost);
		
		/* First draw a little shadow under where each mana symbol will go. */

		if (anti_aliased_circle != NULL)
		{
			int i;
			Rect mr = app_get_image_area(anti_aliased_circle);
			
			for (i = 0; i < nchars; i++)
			{
				Rect mana = rect(costbar.x + costbar.width - 33 - i * 34,
								costbar.y + costbar.height/2 - 13,
								mr.width,
								mr.height);
				app_draw_image(g, mana, anti_aliased_circle, mr);
			}
		}
		else {
			int i;
			
			for (i = 0; i < nchars; i++)
			{
				Rect mana = rect(costbar.x + costbar.width - 33 - i * 34,
								costbar.y + costbar.height/2 - 13,
								32,
								32);
				app_set_colour(g, argb(0xC0, 0xC0, 0xC0, 0xC0));
				app_fill_ellipse(g, app_inset_rect(mana, -2));
				app_set_colour(g, argb(0x80, 0x80, 0x80, 0x80));
				app_fill_ellipse(g, app_inset_rect(mana, -1));
				app_set_colour(g, BLACK);
				app_fill_ellipse(g, mana);
			}
			app_set_colour(g, BLACK);
		}

		/* Draw the mana symbols. */
		if (mana_font != NULL)
		{
			app_set_font(g, mana_font);
			app_draw_text(g, costbar,
					(ALIGN_RIGHT | VALIGN_CENTER | LR_TB),
					card->cost, costlen);
		}
		else
		{
			style_text(g, name8_fonts, costbar,
					(ALIGN_RIGHT | VALIGN_CENTER | LR_TB),
					card->cost, costlen, costfnt);
		}
	}
	if (costfnt)
		app_free(costfnt);

	/* Draw the expansion symbol. */
	if (card->expsym[0])
		filename = card->expsym;
	else
		filename = default_expansion;
	filename = find_image_source("./", symbols, filename,
				rarity_to_trailer(filename, card->rarity));
	if (filename != NULL) {
		art = app_read_image(filename, 32);
		app_free(filename);
	}
	else
		art = NULL;

	if (art != NULL)
	{
		Rect sr = app_get_image_area(art);
		Rect dr;
		dr.y = typebar.y + 8;
		dr.height = 40;
		dr.width = sr.width * dr.height / sr.height;
		dr.x = full->width - 60 - dr.width;
		typebar.width = dr.x - typebar.x;
		app_draw_image(g, dr, art, sr);
		app_del_image(art);
	}

	/* Draw the type line. */
	if (card->type[0])
	{
		int typelen = (int) strlen(card->type);
		char *typefnt = app_alloc(typelen + 1);
		memset(typefnt, PLAIN_FONT, typelen);

		narrow_text(g, type8_fonts,
					typebar, (ALIGN_LEFT | VALIGN_CENTRE | LR_TB),
					card->type, typelen, typefnt);
		app_free(typefnt);
	}
	
	/* Draw the rules text for each 'level'. Ignore flavor text! */
	if (card->text[0])
	{
		int font_size;
		int min_font_size = 40;
		int intra_para_gap = 0;
		int min_paragraph_height = 70 - intra_para_gap;
		int max_paragraph_height = textbox.height - 10;
		char **all_rules;
		int num_rules;
		int i;
		int fnt_offset;
		Font *fonts[4];
		Rect ruler;
		Rect tr = app_inset_rect(textbox, 3);
		int jut_powtgh_right = 20;
		int level_width = 125;
		int powtgh_width = 150;
		
		if (1 && logfile) {
			fprintf(logfile, "Creating level up creature from text: '''%s'''\n", card->text);
			fflush(logfile);
		}
		
		all_rules = strarray_split(card->text, "LEVEL ");
		num_rules = strarray_get_length(all_rules);
		/*
		while ((num_rules > 0) && (all_rules[num_rules - 1][0] == '\0'))
		{
			--num_rules;
		}
		*/
		if (num_rules > 0) {
			max_paragraph_height = (tr.height - intra_para_gap * (num_rules - 1))
								/ num_rules;
		}
		if (max_paragraph_height < min_paragraph_height)
			max_paragraph_height = min_paragraph_height;

		/* Measure how small the font must be to fit it all on the card. */
		for (fnt_offset = i = 0; i < num_rules; i++) {
			char *level = NULL;
			int levellen = 0;
			char *pwtg = NULL;
			int pwtglen = 0;
			char *s = all_rules[i];
			int slen = 0;
			char *styles = card->textfnt + fnt_offset;
			int sbytes = (int) strlen(s);
			int x = tr.x;
			int width = tr.width;
			
			if (i == 0) {
				pwtg = card->powtgh;
				pwtglen = strlen(pwtg);
			}
			else if (i > 0) {
				level = s;
				while ((level[levellen] != '\n') && (level[levellen] != '\0'))
					levellen++;
				pwtg = &level[levellen];
				if (pwtg[0] == '\n')
					pwtg++;
				if (pwtg[0] == '\n')
					pwtg++;
				while ((pwtg[pwtglen] != '\n') && (pwtg[pwtglen] != '\0'))
					pwtglen++;
				styles += (&pwtg[pwtglen] - s);
				s = &pwtg[pwtglen];
				if (s[0] == '\n') {
					s++;
					styles++;
				}
				if (s[0] == '\n') {
					s++;
					styles++;
				}
			}
			if (levellen > 0) {
				x += level_width;
				width -= level_width;
			}
			if (pwtglen > 0) {
				width -= (powtgh_width - jut_powtgh_right);
			}
			slen = (int) strlen(s);
			while (slen > 0) {
				if ((s[slen-1] == '\n') || (s[slen-1] == '\t') || (s[slen-1] == ' '))
					slen--;
				else
					break;
			}
			
			if (0 && logfile) {
				int c;
				fprintf(logfile, "\t");
				if (levellen > 0) {
					fprintf(logfile, "LEVEL ");
					for (c = 0; c < levellen; c++) {
						fprintf(logfile, "%c", level[c]);
					}
					fprintf(logfile, " ");
				}
				if (pwtglen > 0) {
					for (c = 0; c < pwtglen; c++) {
						fprintf(logfile, "%c", pwtg[c]);
					}
					fprintf(logfile, " ");
				}
				if (slen > 0) {
					for (c = 0; c < slen; c++) {
						if ((s[c] >= ' ') && (s[c] < 0x7F))
							fprintf(logfile, "%c", s[c]);
						else
							fprintf(logfile, "\\0x%02X", s[c]);
					}
					fprintf(logfile, "[slen=%d]", slen);
				}
				fprintf(logfile, "[sbytes=%d]", sbytes);
				fprintf(logfile, "[styles=%d]", (int)(styles - card->textfnt));
				fprintf(logfile, "[fnt_offset=%d]", fnt_offset);
				fprintf(logfile, "\n");
				fflush(logfile);
			}

			font_size = fit_text(g, fonts_8th_ed,
				rect(x, tr.y, width, max_paragraph_height),
				(ALIGN_LEFT | LR_TB),
				min_font_size, 16,
				s, slen, styles,
				NULL, 0, NULL,
				0);
			if (font_size < min_font_size)
				min_font_size = font_size;
			
			fnt_offset += sbytes + 6; /* +6 to skip "LEVEL " */
		}
		fonts[0] = fonts_8th_ed[0][min_font_size]; /* normal_fonts array */
		fonts[1] = fonts_8th_ed[1][min_font_size]; /* italic_fonts array */
		fonts[2] = fonts_8th_ed[2][min_font_size]; /* mana_fonts array */
		fonts[3] = NULL;
				
		ruler = tr;
		for (fnt_offset = i = 0; i < num_rules; i++) {
			char *level = NULL;
			int levellen = 0;
			char *pwtg = NULL;
			int pwtglen = 0;
			char *s = all_rules[i];
			int slen = 0;
			char *styles = card->textfnt + fnt_offset;
			int sbytes = (int) strlen(s);
			int x = tr.x;
			int width = tr.width;
			int saturation_delta = 12;
			
			if (i == 0) {
				pwtg = card->powtgh;
				pwtglen = strlen(pwtg);
			}
			else if (i > 0) {
				level = s;
				while ((level[levellen] != '\n') && (level[levellen] != '\0'))
					levellen++;
				pwtg = &level[levellen];
				if (pwtg[0] == '\n')
					pwtg++;
				if (pwtg[0] == '\n')
					pwtg++;
				while ((pwtg[pwtglen] != '\n') && (pwtg[pwtglen] != '\0'))
					pwtglen++;
				styles += (&pwtg[pwtglen] - s);
				s = &pwtg[pwtglen];
				if (s[0] == '\n') {
					s++;
					styles++;
				}
				if (s[0] == '\n') {
					s++;
					styles++;
				}
			}
			if (levellen > 0) {
				x += level_width;
				width -= level_width;
			}
			if (pwtglen > 0) {
				width -= (powtgh_width - jut_powtgh_right);
			}
			slen = (int) strlen(s);
			while (slen > 0) {
				if ((s[slen-1] == '\n') || (s[slen-1] == '\t') || (s[slen-1] == ' '))
					slen--;
				else
					break;
			}

			/* Measure the height of the ability paragraph. */
			ruler.x = x;
			ruler.width = width;
			ruler.height = max_paragraph_height;
			/* Saturate the rules text box underneath each successive ability. */
			if (i > 0) {
				Rect r = rect(textbox.x, ruler.y, textbox.width, ruler.height);
				saturate_image32_rect(full, r, i * saturation_delta);
			}
			/* Draw the ability text. */
			app_set_colour(g, BLACK);
			style_text(g, fonts, ruler, ALIGN_LEFT | LR_TB | VALIGN_CENTER,
				s, slen, styles);
			/* If there was a level associated with this ability, draw it. */
			if (levellen > 0) {
				Image *up = find_cached_image("LevelUp.png");
				if (up) {
					Rect ur = app_get_image_area(up);
					Rect dr = ur;
					dr.x = tr.x + 2;
					dr.y = ruler.y + (ruler.height - ur.height) / 2;
					
					app_draw_image(g, dr, up, ur);

					dr.y += 8;
					dr.width  *= 3;
					dr.width  /= 4;
			
					app_set_colour(g, BLACK);
					app_set_font(g, normal8_fonts[22]);
					app_draw_text(g, app_inset_rect(dr, 3),
							(ALIGN_CENTRE | VALIGN_TOP | LR_TB),
							"LEVEL", 5);
					dr.y += 22;

					app_set_font(g, normal8_fonts[40]);
					app_draw_text(g, app_inset_rect(dr, 3),
							(ALIGN_CENTRE | VALIGN_TOP | LR_TB),
							level, levellen);
				}
			}
			/* If there was a pow/tgh associated with this ability, draw it. */
			if (pwtglen > 0) {
				int jut_powtgh_down = 6;
				char *pow_filename = form_powtgh_filename
					(card->bitfield, card->num_colours, card->num_genmana, card->tb1);
				Image *pow = find_cached_image(pow_filename);
				if (pow) {
					Image *ptimg;
					Rect pr = app_get_image_area(pow);
					Rect dr = pr;
					dr.x = tr.x + tr.width - pr.width + jut_powtgh_right;
					dr.y = ruler.y + (ruler.height - pr.height) / 2 + jut_powtgh_down;
					
					ptimg = copy_image32_saturated(pow, i * saturation_delta);
					if (!ptimg)
						ptimg = pow;
					app_draw_image(g, dr, ptimg, pr);
															
					/* Write the power/toughness. */
					if (pwtg)
					{
						app_set_font(g, name8_fonts[PLAIN_FONT]);
						app_draw_text(g, app_inset_rect(dr, 3),
								(ALIGN_CENTRE | VALIGN_CENTRE | LR_TB),
								pwtg, pwtglen);
					}
					if (ptimg != pow)
						app_del_image(ptimg);
				}
				app_del_string(pow_filename);
			}
			/* Step to the next paragraph. */
			ruler.y += ruler.height + intra_para_gap;
			fnt_offset += sbytes + 6; /* +6 to skip "LEVEL " */
		}
		strarray_del(all_rules);
	}

	/* Draw the artist. */
	if (has_dark_card_background_1(card)) {
		app_set_colour(g, WHITE);
	}
	if (card->artist[0])
	{
		app_set_font(g, artist8_font);
		app_draw_text(g, rect(58, 940, 500, 60),
				(ALIGN_LEFT | VALIGN_CENTRE | LR_TB),
				card->artist, (int) strlen(card->artist));
	}
	app_set_colour(g, BLACK);

	/* Draw the card number. */
	if (has_dark_card_background_2(card)) {
		app_set_colour(g, WHITE);
	}
	if (card->number[0] || card->designer[0])
	{
		int designerlen;
		int numberlen;
		char *legend;

		designerlen = (int) strlen(card->designer);
		numberlen = (int) strlen(card->number);
		legend = app_alloc(10 + designerlen + numberlen);
		legend[0] = '\0';
		strcat(legend, card->designer);
		strcat(legend, " ");
		strcat(legend, card->number);

		app_set_font(g, number8_font);
		app_draw_text(g, rect(58, 980, 500, 40),
				(ALIGN_LEFT | VALIGN_CENTRE | LR_TB),
				legend, (int) strlen(legend));
		app_free(legend);
	}
	app_set_colour(g, BLACK);

	app_del_graphics(g);

	return full;
}

/*
 *  Generate an image of either a normal, flipped, planeswalker or levelup card.
 */
Image * create_card_image_8th_ed_upright(Card *card, Colour border_colour)
{
	if (card->flip)
		return create_card_image_8th_ed_flipped(card, border_colour);
	else if (card->bitfield & IS_PLANESWALKER)
		return create_card_image_8th_ed_planeswalker(card, border_colour);
	else if (card->bitfield & IS_LEVELUP)
		return create_card_image_8th_ed_levelup(card, border_colour);
	else
		return create_card_image_8th_ed_normal(card, border_colour);
}

/*
 *  To handle split-cards (such as Fire//Ice), the following
 *  function generates two card images, and rotates them,
 *  scales them down, and blits them onto a new card image.
 *  The intermediate results are deleted and the final card
 *  image is returned.
 */
Image * create_card_image_8th_ed_split(Card *card1, Card *card2, Colour border_colour)
{
	Image *img1;
	Image *rot1;
	Image *img2;
	Image *rot2;
	Image *scaled;
	Rect ir, rr, sr;
	Graphics *g;
	const int border_size = 30;
	const int border_line = 1;
	const int pull_towards_centre = 10;

	if (logfile)
		fprintf(logfile, "Viewing split-card %s // %s\n", card1->name, card2->name);

	/* Create the first card. */
	img1 = create_card_image_8th_ed_upright(card1, border_colour);
	if (img1 == NULL) /* ran out of memory */
		return NULL;
	ir = app_get_image_area(img1);
	//ir = app_inset_rect(ir, border_size); // 8th Ed: Commented out to include border.

	/* Create the rotated version of the first card. */
	rot1 = image_32bpp_rotated_90_degrees(img1);
	app_del_image(img1); /* throw this large image away now! */
	if (rot1 == NULL) /* ran out of memory */
		return NULL;
	rr = app_get_image_area(rot1);
	//rr = app_inset_rect(rr, border_size); // 8th Ed: Commented out to include border.

	/* Scale the rotated first card to fit into half a full card. */
	scaled = app_scale_image(rot1,
			rect(0, 0, ir.width, ir.height/2), rr);
	app_del_image(rot1); /* throw this large image away now! */
	if (scaled == NULL) /* ran out of memory */
		return NULL;
	sr = app_get_image_area(scaled);

	/* Create the second card. */
	img2 = create_card_image_8th_ed_upright(card2, border_colour);
	if (img2 == NULL) { /* ran out of memory */
		app_del_image(scaled);
		return NULL;
	}

	/* Create the rotated version of the second card. */
	rot2 = image_32bpp_rotated_90_degrees(img2);
	/* Don't throw away the second card image yet, re-use it! */
	if (rot2 == NULL) { /* ran out of memory */
		app_del_image(scaled);
		app_del_image(img2);
		return NULL;
	}

	/* Now, copy the scaled first card over the bottom half. */
	g = app_get_image_graphics(img2);
	if (g == NULL) { /* ran out of memory */
		app_del_image(scaled);
		app_del_image(img2);
		app_del_image(rot2);
		return NULL;
	}
	app_draw_image(g, rect(ir.x,
				ir.y + ir.height - sr.height - pull_towards_centre,
				ir.width,
				sr.height),
			scaled, sr);
	app_del_image(scaled); /* Discard last vestige of first card. */

	/* Scale the rotated second card to fit into half a full card. */
	scaled = app_scale_image(rot2,
			rect(0, 0, ir.width/2, (ir.height+1)/2), rr);
	app_del_image(rot2); /* throw this large image away now! */
	if (scaled == NULL) { /* ran out of memory */
		app_del_image(img2);
		return NULL;
	}
	sr = app_get_image_area(scaled); /* Could be slightly taller. */

	/* Now, copy the scaled second card over the top half. */
	app_draw_image(g, rect(ir.x,
				ir.y + pull_towards_centre,
				ir.width,
				sr.height),
			scaled, sr);
	app_del_image(scaled); /* Discard last vestige of second card. */

	/* Ensure card borders are correct. */
	ir = app_get_image_area(img2);
	draw_outer_border(g, img2, ir, border_size, border_line, border_colour);

	app_del_graphics(g); /* Finished drawing now. */

	return img2;
}

/*
 *  The master function which creates an 8th Edition card image
 *  at 300 DPI from a card already parsed.
 *
 *  Note: for this function to correctly operate,
 *  the current working directory must be such
 *  that the artwork file can be found in a
 *  relative path location (unless the spoiler
 *  designated the path using an absolute path).
 *
 *  This function can handle normal cards, land etc,
 *  as well as flip cards, and split cards.
 */
Image * create_card_image_8th_ed(Card *card, Colour border_colour)
{
	if (card->split)
		return create_card_image_8th_ed_split(card, card->split, border_colour);
	else
		return create_card_image_8th_ed_upright(card, border_colour);
}
