#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "aalib.h"
#include "aaint.h"

#define NORMALPRINTS { "%s", "%s", "%s", "%s", "%s", }
#define NONE { "", "", "", "", "", }

static __AA_CONST char * __AA_CONST html_escapes[] =
{"<", "&lt;", ">", "&gt;", "&", "&amp;", NULL};
static __AA_CONST char * __AA_CONST html_alt_escapes[] =
{"<", "&lt;", ">", "&gt;", "&", "&amp;", "\"", "&quot;", NULL};
#ifdef VYHEN_SUPPORT
static __AA_CONST char * __AA_CONST vyhen_escapes[] =
{"|", "||","~", "~~", NULL};
#endif

__AA_CONST static char * __AA_CONST irc_escapes[] =
{"@", "@@", NULL};

static char *generate_filename(__AA_CONST char *template, char *result, int x, int y, int pages, __AA_CONST char *extension);

__AA_CONST struct aa_format aa_nhtml_format =
{
    79, 36,
    79, 36,
    0,
    AA_NORMAL_MASK | AA_BOLD_MASK | AA_BOLDFONT_MASK | AA_DIM_MASK,
    &aa_fontcourier,
    "Nestcapeized html",
    ".html",
    "<HTML>\n<HEAD><TITLE>Ascii arted image done using aalib</TITLE>\n</HEAD>\n<BODY BGCOLOR=\"#000000\" TEXT=\"#b2b2b2\" LINK=\"#FFFFFF\">\n<FONT COLOR=#b2b2b2 SIZE=2><PRE>\n",
    "</PRE></FONT></BODY>\n</HTML>\n",
    "\n",
    NORMALPRINTS,
    {"",
     "<FONT COLOR=\"686868\">",
     "<FONT COLOR=\"ffffff\">",
     "",
     "<B>"
    },
    {"",
     "</FONT>",
     "</FONT>",
     "",
     "</B>"
    },
    html_escapes
};
__AA_CONST struct aa_format aa_html_format =
{
    79, 25,
    79, 25,
    0,
    AA_NORMAL_MASK | AA_BOLD_MASK | AA_BOLDFONT_MASK,
    NULL,
    "Pure html",
    ".html",
    "<HTML>\n <HEAD> <TITLE>Ascii arted image done using aalib</TITLE>\n</HEAD>\n<BODY><PRE>\n",
    "</PRE></BODY>\n</HTML>\n",
    "\n",
    NORMALPRINTS,
    {"",
     "",
     "<B>",
     "",
     "<B>"
    },
    {"",
     "",
     "</B>",
     "",
     "</B>"
    },
    html_escapes
};
__AA_CONST struct aa_format aa_ansi_format =
{
    80, 25,
    80, 25,
    0,
    AA_NORMAL_MASK | AA_BOLD_MASK | AA_BOLDFONT_MASK | AA_REVERSE_MASK | AA_DIM_MASK,
    NULL,
    "ANSI escape seqences",
    ".ansi",
    "",
    "",
    "\n",
    NORMALPRINTS,
    {"",
     "\33[8m",
     "\33[1m",
     "\33[1m",
     "\33[7m"
    },
    {"",
     "\33[0;10m",
     "\33[0;10m",
     "\33[0;10m",
     "\33[0;10m",
    },
    NULL
};
#ifdef VYHEN_SUPPORT
__AA_CONST struct aa_format aa_vyhen_format =
{
    80, 30,
    80, 30,
    0,
    AA_NORMAL_MASK | AA_BOLD_MASK | AA_REVERSE_MASK | AA_DIM_MASK | AA_EXTENDED,
    NULL,
    "vyhen",
    ".vyhen",
    "",
    "",
    "\n",
    NORMALPRINTS,
    {"|07",
     "|08",
     "|15",
     "||7|00",
     "||1|15"
    },
    {"",
     "",
     "",
     "||9",
     "||0",
    },
    vyhen_escapes
};
#endif
__AA_CONST struct aa_format aa_text_format =
{
    80, 25,
    80, 25,
    0,
    AA_NORMAL_MASK,
    NULL,
    "Text file",
    ".txt",
    "",
    "",
    "\n",
    NORMALPRINTS,
    NONE,
    NONE,
    NULL
};
__AA_CONST struct aa_format aa_more_format =
{
    80, 25,
    80, 25,
    AA_NORMAL_SPACES,
    AA_NORMAL_MASK | AA_BOLD_MASK | AA_BOLDFONT_MASK,
    NULL,
    "For more/less",
    ".cat",
    "",
    "",
    "\n",
		/* Can leave these %c iff there are no conversions to multi-character
		 * thingies. */
    {"%s", "%s", "%s\10%s", "%s\10%s", "%s"},
    NONE,
    NONE,
    NULL
};
__AA_CONST struct aa_format aa_hp_format =
{
    130 * 2, 64 * 2 - 1,
    130, 64 * 2 - 1,
    AA_USE_PAGES,
    AA_NORMAL_MASK,
    &aa_fontline,
    "HP laser jet - A4 small font",
    ".hp",
    "\33(10U\33(s0p16.67h8.5v0s0b0T\33%%0A\33&/0U\33&/0Z\33&/24D",
    "\14",
    "\r\33=",
    NORMALPRINTS,
    NONE,
    NONE,
    NULL
};
__AA_CONST struct aa_format aa_hp2_format =
{
    80 * 2, 64 - 1,
    80, 64 - 1,
    AA_USE_PAGES,
    AA_NORMAL_MASK,
    &aa_font16,
    "HP laser jet - A4 big font",
    ".hp",
    "\33(s7B\33*p0Y@",
    "\14",
    "\r\n",
    NORMALPRINTS,
    NONE,
    NONE,
    NULL
};
__AA_CONST struct aa_format aa_irc_format =
{
    70, 25,
    70, 25,
    0,
    AA_NORMAL_MASK | AA_BOLD_MASK | AA_REVERSE_MASK,
    &aa_font16,
    "For catting to an IRC channel",
    ".irc",
    "",
    "",
    "\n",
    NORMALPRINTS,
    {"",
     "",
     "",
     "",
     ""
    },
    {"",
     "",
     "",
     "",
     ""
    },
    NULL
};
__AA_CONST struct aa_format aa_zephyr_format =
{
    70, 25,
    70, 25,
    0,
    AA_NORMAL_MASK | AA_DIM_MASK | AA_BOLD_MASK,
    &aa_font16,
    "For catting to an IRC channel II",
    ".irc",
    "",
    "",
    "\n",
    NORMALPRINTS,
    {"",
     "@color(gray50)",
     "@b(",
     "",
     ""
    },
    {"",
     "@color(black)",
     ")",
     "",
     ""
    },
    irc_escapes
};
__AA_CONST struct aa_format aa_roff_format =
{
    70, 25,
    70, 25,
    0,
    AA_NORMAL_MASK | AA_BOLD_MASK,
    &aa_font16,
    "For including in a man page",
    ".man",
    "",
    "",
    "\n.br\n",
    NORMALPRINTS,
    {"",
     "",
     "\n.B ",
     "",
     ""
    },
    {"",
     "",
     "\n",
     "",
     ""
    },
    irc_escapes
};

__AA_CONST struct aa_format aa_html_alt_format =
{
    79, 25,
    79, 25,
    0,
    AA_NORMAL_MASK,
    NULL,
    "HTML <IMG ALT= tag",
    ".html",
		/* Need that newline, because lynx inserts a space, thus shifting
		 * the first line of our art. */
    "<PRE><IMG SRC=\"your image here\" ALT=\"\n",
    "\"></PRE>\n",
    "\n",
    NORMALPRINTS,
    {"",
     "",
     "",
     "",
     ""
    },
    {"",
     "",
     "",
     "",
     ""
    },
    html_alt_escapes
};

__AA_CONST struct aa_format * __AA_CONST aa_formats[] =
{
#ifdef VYHEN_SUPPORT
    &aa_vyhen_format,
#endif
    &aa_text_format,
    &aa_html_format,
    &aa_nhtml_format,
    &aa_html_alt_format,
    &aa_more_format,
    &aa_ansi_format,
    &aa_hp_format,
    &aa_hp2_format,
    &aa_irc_format,
    &aa_zephyr_format,
    &aa_roff_format,
    NULL
};

__AA_CONST struct aa_driver save_d;
#define FORMAT ((struct aa_savedata *)c->driverdata)->format
#define DATA ((struct aa_savedata *)c->driverdata)

__AA_CONST static char **build_conversions(__AA_CONST char * __AA_CONST *in, __AA_CONST char **conv)
{
    unsigned char c;

    memset(conv, 0, sizeof(char *) * 256);

    if (in != NULL)
	while (*in != NULL && *(in + 1) != NULL) {
	    c = **in;
	    in++;
	    conv[c] = *in;
	    in++;
	}
    return conv;
}

static int save_init(__AA_CONST struct aa_hardware_params *p, __AA_CONST void *none,
		     struct aa_hardware_params *dest, void **data)
{
    __AA_CONST struct aa_savedata *d = (__AA_CONST struct aa_savedata *) none;
    static struct aa_hardware_params def;
    *data = (void *) malloc(sizeof(struct aa_savedata));
    memcpy(*data, d, sizeof(struct aa_savedata));
    *dest = def;
    if (p->font == NULL)
	dest->font = d->format->font;
    dest->width = d->format->width;
    dest->height = d->format->height;
    dest->supported = d->format->supported;


    return 1;
}
static void save_uninit(aa_context * c)
{
}
static void save_getsize(aa_context * c, int *width, int *height)
{
}
static void save_gotoxy(aa_context * c, int x, int y)
{
}

static int lastattr;
static FILE *f;
static aa_context *c;
static void stop_tag(void)
{
    if (lastattr != -1)
	fputs(FORMAT->ends[lastattr], f);
    lastattr = -1;
}
static void start_tag(int attr)
{
    if (attr > AA_NATTRS)
	attr = AA_NATTRS;
    lastattr = attr;
    fputs(FORMAT->begin[lastattr], f);
}
static void encodechar(unsigned char attr, unsigned char ch, __AA_CONST char **conversions)
{
    char chr[2];
    if (FORMAT->flags & AA_NORMAL_SPACES && ch == ' ' && (attr != AA_REVERSE))
	attr = AA_NORMAL;
    if (attr != lastattr) {
	stop_tag();
	start_tag(attr);
    }
    if (conversions[ch] == NULL) {
	chr[0] = ch;
	chr[1] = 0;
	fprintf(f, FORMAT->prints[attr], chr, chr, chr, chr);
    } else {
	fprintf(f, FORMAT->prints[attr], conversions[ch], conversions[ch],
		conversions[ch], conversions[ch]);
    }
}

static void savearea(int x1, int y1, int x2, int y2, __AA_CONST char **conversions)
{
    int x, y;
    fputs(FORMAT->head, f);
    lastattr = -1;
    for (y = y1; y < y2; y++) {
	for (x = x1; x < x2; x++) {
	    if (x < 0 || x >= aa_scrwidth(c) ||
		y < 0 || y >= aa_scrheight(c))
		encodechar(AA_NORMAL, ' ', conversions);
	    else {
		int pos = x + y * aa_scrwidth(c);
		encodechar(c->attrbuffer[pos], c->textbuffer[pos], conversions);
	    }
	}
	stop_tag();
	fputs(FORMAT->newline, f);
    }
    fputs(FORMAT->end, f);
    fflush(f);
}

static void save_flush(aa_context * c1)
{
    char fname[4096];
    __AA_CONST char *conversions[256];
    c = c1;
    build_conversions(FORMAT->conversions, conversions);
    if (FORMAT->flags & AA_USE_PAGES) {
	int xpages = (aa_scrwidth(c1) + FORMAT->pagewidth - 1) /
	FORMAT->pagewidth;
	int ypages = (aa_scrheight(c1) + FORMAT->pageheight - 1) /
	FORMAT->pageheight;
	int x, y;
	for (x = 0; x < xpages; x++)
	    for (y = 0; y < ypages; y++) {
		if (DATA->name != NULL) {
		    generate_filename(DATA->name, fname, x, y, 1, FORMAT->extension);
		    f = fopen(fname, "w");
		} else
		    f = DATA->file;

		if (f == NULL)
		    return;
		savearea(x * FORMAT->pagewidth, y * FORMAT->pageheight,
			 (x + 1) * FORMAT->pagewidth,
			 (y + 1) * FORMAT->pageheight, conversions);
		if (DATA->name != NULL)
		    fclose(f);
	    }

    } else {
	if (DATA->name != NULL) {
	    generate_filename(DATA->name, fname, 0, 0, 0, FORMAT->extension);
	    f = fopen(fname, "w");
	} else
	    f = DATA->file;
	if (f == NULL)
	    return;
	savearea(0, 0, /*DATA->width, DATA->height */ aa_scrwidth(c1), aa_scrheight(c1), conversions);
	if (DATA->name != NULL)
	    fclose(f);
    }
}

/*
 * Takes a template of the form blah%xfu%y.extension and will return
 * either blah<x>fu<y>.extension or blahfu.extension depending on whether
 * pages is true or not. %x and %y may be flipped, but may appear only once.
 * If you want a % in the output, you'll have to use %%.
 * Using %[^%xy] is not defined. The results are also undefined if you don't
 * include both %x and %y.
 *
 * Rewrote by Jan Hubicka to accept unlimited (and zero) number of % stuff
 * and %e for extension and %c for coordinates in format %x_%y.
 * You might now add after filename simply %c%e to get filename
 * blahX_Y.txt or blah.txt
 * Also avoided memory owerflows. 
 */
static char *generate_filename(__AA_CONST char *template, char *result, int x, int y, int pages, __AA_CONST char *extension)
{
    char *a;
    __AA_CONST char *b;
    char *end = result + 4090;
    b = template - 1, a = result - 1;
    while ((*++a = *++b)) {
	if (a >= end)
	    break;		/*Too long filename - probably bug in caller, 
				   since filenames longer than 4KB are useless :) */
	if (*b == '%') {
	    switch (b[1]) {
	    case 'x':
		a--;
		if (pages) {
		    char text[8];
		    char *e = text - 1;
		    sprintf(text, "%i", x);
		    while ((*++a = *++e))
			if (a >= end)
			    break;
		    a--;
		}
		b++;
		break;
	    case 'y':
		a--;
		if (pages) {
		    char text[8];
		    char *e = text - 1;
		    sprintf(text, "%i", y);
		    while ((*++a = *++e))
			if (a >= end)
			    break;
		    a--;
		}
		b++;
		break;
	    case 'c':
		a--;
		if (pages) {
		    char text[8];
		    char *e = text - 1;
		    sprintf(text, "_%i_%i", x, y);
		    while ((*++a = *++e))
			if (a >= end)
			    break;
		    a--;
		}
		b++;
		break;
	    case 'e':
		a--;
		{
		    __AA_CONST char *e = extension - 1;
		    while ((*++a = *++e))
			if (a >= end)
			    break;
		    a--;
		}
		b++;
		break;
	    case '%':
		a--;
		b++;
		break;
	    }
	    if (!*b)
		break;
	}
    }
    *a = 0;
    return result;
}

__AA_CONST struct aa_driver save_d =
{
    "save", "Special driver for saving to files",
    save_init,
    save_uninit,
    save_getsize,
    NULL,
    NULL,
    save_gotoxy,
    save_flush,
};
