/*
 * Copyright (c) 1997 Hong Hunsoo of (KAIST)
 * The Korea Advanced Institute of Science and Technology
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Neither the name of the KAIST nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * This program transforms an ascii file to the bitmap file format and pixmap
 * file format.  The height of the bitmap is the same to the line number
 * of an input file and the width of the outputed bitmap is the largest
 * line length;
 *
 * The xbm and xpm outputs will be save at files named as xbm.#### and
 * xpm.#### where #### is the input file name, respectively.
 *
 * OPTIONS
 * -v means vertical reflection
 * -h means horizontal reflection
 *
 * '.' characters of the input file act as a special role. It represents
 * background color.
 * This programe does not know '\t', the TAB character.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <memory.h>
#include <stdarg.h>

#define __F __FUNCTION__
#define LINE 1024

struct entry {
	int	 en_order;
	unsigned char	*en_chars;
};

struct collection {
	unsigned int col_cnt;
	unsigned int col_maxlen;
	struct entry **col_entries;
};

int	hflag, vflag;
char	inbuf[LINE];
char	*figname;
FILE	*xbm_fout;
FILE	*xpm_fout;

struct collection *malloc_collection(int row, int col);
void	err(const char *fmt, ...);
void	put_collection(struct collection *);
void	hreverse(struct collection *);
int	cmp_entries(struct entry **one, struct entry **two);
void	put_line(FILE *fp, char *line, const int len);
void	put_xbmfotmat(struct collection *);
void	put_xpmfotmat(struct collection *);
struct collection *rotate(struct collection *src);

int
main(int argc, char *argv[])
{
	FILE	*fp;
	int	i;
	unsigned int linecnt, maxlen, len;
	struct entry *entp, **entpp;
	unsigned char *p, *q;
	int	(*compare)();
	struct collection *set0;

	if (argc < 2)
		err("%s(): argc < 2", __F);
	while (argv[1][0] == '-') {
		if (argv[1][1] == 'h') {
			hflag = 1;
			if (argv[1][2] == 'v')
				vflag = 1;
		}

		if (argv[1][1] == 'v') {
			vflag = 1;
			if (argv[1][2] == 'h')
				hflag = 1;
		}
		argv++;
	}

	fp = fopen(argv[1], "r");
	if (fp == NULL)
		err("%s(): fp == NULL", __F);

	figname = strdup(argv[1]);
	p = (char *) malloc(strlen(figname) + 5);
	strcpy(p, "xbm.");
	strcat(p, figname);

	xbm_fout = fopen(p, "w");
	if (xbm_fout == NULL)
		err("%s(): xbm_fout == NULL", __F);
	fprintf(stderr, "xbm outfile=%s\n", p);

	p = (char *) malloc(strlen(figname) + 5);
	strcpy(p, "xpm.");
	strcat(p, figname);

	xpm_fout = fopen(p, "w");
	if (xpm_fout == NULL)
		err("%s(): xpm_fout == NULL", __F);
	fprintf(stderr, "xbm outfile=%s\n", p);

	linecnt = 0;
	maxlen = 0;
	while (fgets(inbuf, LINE, fp)) {
		len = strlen(inbuf)-1;
		if (len > maxlen)
			maxlen = len;
		linecnt++;
	}

	set0 = malloc_collection(linecnt, maxlen);
	entpp = set0->col_entries;

	rewind(fp);
	linecnt = 0;
	while (fgets(inbuf, LINE, fp)) {
		entp = entpp[linecnt];
		len = strlen(inbuf)-1;
		entp->en_order = linecnt;
		q = entp->en_chars;
		p = inbuf;
		for (i=0; i<len; i++, p++, q++)
			if (*p != '.')
				*q = '#';
			else
				*q = '.';
		for (; i < maxlen; i++)
			*q++ = '.';
		*q = 0;	/* TERMINATOR */

		linecnt++;
	}

	fclose(fp);
	if (vflag) {
		compare = cmp_entries;
		qsort(set0->col_entries, set0->col_cnt,
			sizeof(struct entry *), compare);
	}
	if (hflag)
		hreverse(set0);

	if (0) {
		put_collection(set0);
		set0 = rotate(set0);
	}

	put_xpmfotmat(set0);
	put_xbmfotmat(set0);

	return 0;
}

void
hreverse(struct collection *set)
{
	int	i, len;
	char	*p, *q, tmp;

	len = set->col_maxlen;
	for (i =0; i<set->col_cnt; i++) {
		p = set->col_entries[i]->en_chars;
		q = p + len -1;
		while (p < q) {
			tmp = *p;
			*p++ = *q;
			*q-- = tmp;
		}
	}

	return;
}


void
put_collection(struct collection *set)
{
	int	i;

	for (i =0; i<set->col_cnt; i++)
		printf("%s\n", set->col_entries[i]->en_chars);

	return;
}

int
cmp_entries(struct entry **one, struct entry **two)
{
	if ((*one)->en_order < (*two)->en_order)
		return 1;
	else
		return -1;
	return 0;
}

void
err(const char *fmt, ...)
{
	va_list cp;
	va_start(cp, fmt);

	vfprintf(stdout, fmt, cp);
	fprintf(stdout, "\n");
	va_end(cp);
	exit(1);
}

void
put_xbmfotmat(struct collection *set)
{
	struct entry *enp;
	int	len, i;

	fprintf(xbm_fout, "#define %s_width %d\n", figname, set->col_maxlen);
	fprintf(xbm_fout, "#define %s_height %d\n", figname, set->col_cnt);
	fprintf(xbm_fout, "static char %s_bits[] = {\n", figname);

	len = set->col_maxlen;
	for (i =0; i<set->col_cnt; i++) {
		enp = set->col_entries[i];
			put_line(xbm_fout, enp->en_chars, len);
	}
	fprintf(xbm_fout, "};\n");
	return;
}

int
get_hexa(char *line)
{
	char	*p;
	int	i, ret = 0, acc = 8;

	for (i=0, p = line; i<4; acc >>= 1, p++, i++)
		if (*p == '#')
			ret += acc;
	return ret;
}

char	hexas[]="0123456789abcdef";
char	tmpline[32];

void
put_line(FILE *fp, char *line, const int len)
{
	char	*p, *q, tmp, tmpline[32];
	char	*tq;
	int	i, j, howmany, extra, first, second;

	howmany = len / 8;
	extra = len % 8;
	if (extra)
		howmany++;
	p = inbuf;
	strcpy(p, "    ");
	p += 4;
	q = line;
	for (i=0; i < howmany; i++, q += 8) {
		for (j=0; j < 4; j++) {
			tmp = q[j];
			q[j] = q[7-j];
			q[7-j] = tmp;
		}
		first  = get_hexa(q);
		second = get_hexa(q+4);
		strcat(p, "0x");
		p[2] = hexas[first];
		p[3] = hexas[second];
		strcpy(p+4, ", ");
		p += 6;
		if (i == (howmany - 2) && extra) {
			memset(tmpline, ' ', 8);
			tq = &tmpline[8];
			for (q += 8; *q; q++)
				*tq++ = *q;
			q = tmpline;
		}
	}
	*p = 0;
	fprintf(xbm_fout, "%s\n", inbuf);
	return;
}

struct collection *
malloc_collection(int row, int col)
{
	int	i;
	struct collection *ret;
	struct entry **entpp, *entp;

	ret = (struct collection *) malloc(sizeof(struct collection));
	if (ret == NULL)
		err("%s(): ret == NULL", __F);
	ret->col_cnt = row;
	ret->col_maxlen = col;

	entpp = (struct entry **) malloc(sizeof(struct entry *) * row);
	if (entpp == NULL)
		err("%s(): entpp == NULL", __F);

	ret->col_entries = entpp;

	entp = (struct entry *) malloc(sizeof(struct entry) * row);
	if (entp == NULL)
		err("%s(): entp == NULL", __F);

	for (i=0; i < row; i++, entp++) {
		entpp[i] = entp;
		entp->en_chars = (unsigned char *) malloc((col+1)); /* XXX */
		memset(entp->en_chars, '.', (col+1));
		entp->en_chars[col] = 0;
	}
	return ret;
}

struct collection *
rotate(struct collection *src)
{
	struct collection *ret;
	char	*csrc, *cdst;
	int	i, j;

	ret = malloc_collection(src->col_maxlen, src->col_cnt);

	for (i=0; i<src->col_cnt; i++) {
		csrc = src->col_entries[i]->en_chars;
		for (j=0; j< src->col_maxlen; j++) {
			cdst = ret->col_entries[ret->col_cnt-j-1]->en_chars;

			cdst[i] = csrc[j];
		}
	}
	return ret;
}

void
put_xpmfotmat(struct collection *set)
{
	struct entry *enp;
	int	len, i;

	fprintf(xpm_fout, "/* XPM */\n");
	fprintf(xpm_fout, "static char *%s[] = {\n", figname);
	fprintf(xpm_fout, "\"%3d %3d %2d %2d\",\n",
		set->col_maxlen, set->col_cnt, 2, 1);
	fprintf(xpm_fout, "\". c #00ff00\",\n");
	fprintf(xpm_fout, "\"# c #0000ff\",\n");
	

	len = set->col_maxlen;
	for (i =0; i < (set->col_cnt-1) ; i++) {
		enp = set->col_entries[i];
		fprintf(xpm_fout, "\"%s\",\n", enp->en_chars);
	}
	enp = set->col_entries[i];
	fprintf(xpm_fout, "\"%s\"\n", enp->en_chars);
	fprintf(xpm_fout, "};\n");
	return;
}
