/* $Id: generate_pot.c 67 2003-02-20 06:04:50Z gabucino $
 * (C)2003 Alban Hertroys
 *
 * This program rotates a pixmap in a larger pixmap
 * by translation. For every step a new pixmap is added
 * in vertical arrangement.
 * 
 * input files:
 * 	- rotation spec
 * 	- pixmap to rotate (ppm file)
 * 	- pixmap to rotate it in (ppm file)
 */

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <math.h>
#include <ppm.h>

#define SYNTAX_ERROR(f, l) \
do {\
    fprintf(stderr, "%s: Syntax error at line %d\n", (f), (l));\
    exit(EXIT_FAILURE);\
} while(0);


struct PPM
{
    int width, height;
    unsigned long pos;
    pixval colors;
    pixel **data;
};


int usage(char *progName);
char* readFile(char *filename, size_t *size, int *retVal);

int readSpec(char *filename,
	unsigned short *w, unsigned short *h, unsigned short *r,
	signed short *a1, signed short *a2, signed short *s);

struct PPM* readPPM(char *filename);
void ppmCopyArea(struct PPM *src, struct PPM *dst,
	int sx, int sy, int w, int h,
	int dx, int dy, int transparent);

int
main(int argc, char **argv)
{
    unsigned short w, h, r, n;
    signed short a1, a2, s;
    struct PPM *in_ppm1, *in_ppm2, *out_ppm;
    int retVal;
    int i, x, y;
    double dr, dx, dy;

    if (argc != 4)
	return usage(argv[0]);

    /* read the rotation spec */
    if ((retVal = readSpec(argv[1], &w, &h, &r, &a1, &a2, &s)) != 0)
	return retVal;

    ppm_init(&argc, argv);

    /* read source PPM images */
    in_ppm1 = readPPM(argv[2]);
    in_ppm2 = readPPM(argv[3]);

    /* determine #steps from angles and step size/direction */
    if (a1 > a2) {
	signed short tmp = a1;

	a1 = a2;
	a2 = tmp;
    }

    if (s > 0)	n = (a2 -a1)/s;
    else	n = (a2 -a1 -360)/s;

    fprintf(stderr, "Image has %d phases.\n", n);
    
    /* Allocate memory for output PPM */
    out_ppm = malloc(sizeof(struct PPM));
    out_ppm->width = in_ppm2->width;
    out_ppm->height = in_ppm2->height * n;
    out_ppm->colors = (in_ppm2->colors > in_ppm1->colors ?
	    in_ppm2->colors : in_ppm1->colors);
    out_ppm->pos = 0;
    out_ppm->data = ppm_allocarray(out_ppm->width, out_ppm->height);

    if (s < 0) a1 += 360;

    dx = in_ppm1->width / 2.0;
    dy = in_ppm1->height / 2.0;
    dr = sqrt(dx * dx + dy * dy);

fprintf(stderr, "dr = %1.1f\n", dr);

    for (i = a1; (s > 0 ? i < a2 : i > a2); i += s) {
	double xc = (w - 1.0) / 2.0;
	double yc = (in_ppm2->height - 1.0) / 2.0;
	double ang = (double)i * M_PI / 180.0;

	x = xc - dx - rint((double)(r - dr) * sin(ang));
	y = yc - dy + rint((double)(r - dr) * cos(ang));

	ppmCopyArea(in_ppm2, out_ppm,
		0, 0, in_ppm2->width, in_ppm2->height,
		0, out_ppm->pos,
		0);

	ppmCopyArea(in_ppm1, out_ppm,
		0, 0, in_ppm1->width, in_ppm1->height,
		x, y + out_ppm->pos,
		1);

	out_ppm->pos += in_ppm2->height * w;
    }

    ppm_writeppm(stdout, out_ppm->data,
	    out_ppm->width, out_ppm->height, out_ppm->colors, 0);

    return EXIT_SUCCESS;
}


int
usage(char *progName)
{
    fprintf(stderr,
	    "\n"
	    "%s\n"
	    "Usage:\n"
	    "------\n"
	    "%s spec rot_ppm base_ppm\n"
	    "\n",
	    progName, progName);

    return EXIT_FAILURE;
}


char*
readFile(char *filename, size_t *size, int *retVal)
{
    FILE *fd;
    char *data, *line;
    size_t nread, dsize, lsize, pos;

    if ((fd = fopen(filename, "r")) == NULL) {
	*retVal = errno;
	return NULL;
    }

    pos = 0;
    lsize = 10;
    dsize = 5;
    data = malloc(sizeof(char) * lsize * dsize);
    *size = 0;

    while(!feof(fd) && !ferror(fd)) {
	line = &data[pos];

	if (pos > dsize - lsize * sizeof(char)) {
	    dsize *= 2;
	    data = realloc(data, dsize * sizeof(char));
	    if (data == NULL) {
		*retVal = errno;
		return NULL;
	    }
	}

	nread = fread(line, sizeof(char), lsize, fd);
	pos += nread;
    }

    if ((*retVal = ferror(fd))) {
	fclose(fd);
	return NULL;
    }

    *size = pos;
    fclose(fd);
    *retVal = EXIT_SUCCESS;
    return data;
}


int
readSpec(char *filename,
	unsigned short *w, unsigned short *h, unsigned short *r,
	signed short *a1, signed short *a2, signed short *s)
{
    char *data;
    size_t size;
    int retVal;
    int i, line = 1, pos = 0;
    char c, comment;
    unsigned short *var;
    char name[4];

    if ((data = readFile(filename, &size, &retVal)) == NULL)
	return retVal;

    name[0] = '\0';

    comment = 0;
    for (i = 0; i < size; i++) {
	c = data[i];

	if (comment == 1 && c != '\n')
	    continue;

	switch(c) {
	    case '\r':
		break;
	    case '#':
		comment = 1;
		break;
	    case '\n':
		comment = 0;
		line++;
	    case ' ':
	    case '\t':
		if (comment == 1)
		    break;

		if (var != NULL && name[0] != '\0') {
		    *var = atoi(name);
		    var = NULL;
		    name[0] = '\0';
		    pos = 0;
		}
		break;
	    case '=':
		if (comment == 1)
		    break;

		switch(name[0]) {
		    case 'w':
			var = w;
			break;
		    case 'h':
			var = h;
			break;
		    case 'r':
			var = r;
			break;
		    case 'a':
			if (name[1] == '1')
			    var = (unsigned short*)a1;
			else if (name[1] == '2')
			    var = (unsigned short*)a2;
			else SYNTAX_ERROR(filename, line);
			break;
		    case 's':
			var = (unsigned short*)s;
			break;
		    default:
			SYNTAX_ERROR(filename, line);
			break;
		}
		name[0] = '\0';
		pos = 0;
		break;
	    default:
		if (comment == 1)
		    break;

		if (pos >= 4) SYNTAX_ERROR(filename, line);
		name[pos++] = c;
		name[pos] = '\0';
		break;
	}
    }

    return EXIT_SUCCESS;
}

    
struct PPM*
readPPM(char *filename)
{
    FILE *fp;
    struct PPM *ppm = malloc(sizeof(struct PPM));

    if ((fp = fopen(filename, "r")) == NULL)
	return NULL;

    ppm->data = ppm_readppm(fp,
	    &ppm->width,
	    &ppm->height,
	    &ppm->colors);

    fclose(fp);
    
    return ppm;
}


void
ppmCopyArea(struct PPM *src, struct PPM *dst,
	int sx, int sy, int w, int h,
	int dx, int dy, int transparent)
{
    int row;

#ifdef DEBUG
    fprintf(stderr, "src: (%3d, %3d) - (%3d, %3d) dst: (%3d, %3d)\n",
	    sx, sy, w, h, dx, dy);
#endif

    for (row = 0; row < h; row++) {
	int col;

	for (col = 0; col < w; col++) {
	    pixel *sp, *dp;
	    pixval r, g, b;
	    
	    sp = &src->data[col + sx][row + sy];
	    dp = &dst->data[col + dx][row + dy];

	    r = PPM_GETR(*sp);
	    g = PPM_GETG(*sp);
	    b = PPM_GETB(*sp);

	    if (r == 255 && g == 0 && b == 255 && transparent) {
#ifdef DEBUG
		fprintf(stderr, "\t...skipping (%3d, %3d)\n",
			col + sx, row + sy);
#endif
		continue;
	    }

	    PPM_DEPTH(*dp, *sp, src->colors, dst->colors);
	}
    }
}
