/* gridpack.c: create a gridpix from a ppm file */

/* usage: gridpack [ppmfile] */

#define BufSize 1024

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ppm.h>
#include <math.h>
#include <jpeglib.h>

#include "gridpix.h"
#include "utils.h"

/* Default values */

#define TileWidth 144
#define TileHeight 144
#define Layers 4
#define Ratio 2
#define Quality 75

/* #define DEBUG 1 */

void usage(char *progname) ;

int main(int argc, char **argv)
{
    char *progname ;
    pixval maxval ;
    pixel **ippm, **tppm ;
    int numfiles ;
    char *inputfile = NULL ;
    m_int_t offset ;
    FILE *ifp, *ofp ;
    struct gridpix gp ;
    struct jpeg_compress_struct cinfo ;
    struct jpeg_error_mgr jerr ;
    JSAMPROW scanline[1] ;
    int tmpwidth, tmpheight, layer, filenum ;
    int i, j, x, y, xmax, ymax, xsize, ysize ;
    int factor ;
    int width, height, tilewidth, tileheight ;
    int quality ;
    char *outputfile = NULL ;
    m_int_t *offsets = NULL ;
    f_int_t *offsets2 = NULL ;
    int layers = Layers ;

    tilewidth = TileWidth ;
    tileheight = TileHeight ;
    quality = Quality ;
    ifp = stdin ;

    progname = argv[0] ;

    while (argc > 1) {
	if (!strcmp(argv[1], "-t")) {
	    if (argc < 3)
		usage(progname) ;
	    tilewidth = tileheight = atoi(argv[2]) ;
	    argc-- ;
	    argv++ ;
	}
	else if (!strcmp(argv[1], "-q")) {
	    if (argc < 3)
		usage(progname) ;
	    quality = atoi(argv[2]) ;
	    argc-- ;
	    argv++ ;
	}
	else if (!strcmp(argv[1], "-o")) {
	    if (argc < 3)
		usage(progname) ;
	    outputfile = argv[2] ;
	    argc-- ;
	    argv++ ;
	}
	else if (!strcmp(argv[1], "-l")) {
	    if (argc < 3)
		usage(progname) ;
	    layers = atoi(argv[2]) ;
	    argc-- ;
	    argv++ ;
	}
	else if (!strcmp(argv[1], "-h")) {
	    usage(progname) ;
	    exit(0) ;
	}
	else
	    inputfile = argv[1] ;
	argc-- ;
	argv++ ;
    }

    if (inputfile == NULL && outputfile == NULL) {
	fprintf(stderr, "error: must specify at least one of input or output\n") ;
	exit(1) ;
    }

    memcpy(gp.header.magic, Magic2, 4) ;
    gp.header.headersize = sizeof(struct gridheader2) ;
    gp.header.tilewidth = tilewidth ;
    gp.header.tileheight = tileheight ;
    gp.header.layers = layers ;
    gp.header.ratio = Ratio ;

    if (inputfile) {
	if ((ifp = fopen(inputfile, "r")) == NULL) {
	    fprintf(stderr, "\"%s\"", inputfile) ;
	    perror("") ;
	    exit(1) ;
	}
	if (outputfile == NULL) {
	    if (strcmp(inputfile + strlen(inputfile) - strlen(".ppm"), ".ppm")) {
		fprintf(stderr, "error: inputfile doesn't end with \".ppm\"\n") ;
		exit(1) ;
	    }
	    outputfile = strdup(inputfile) ;
	    strcpy(outputfile + strlen(outputfile) - strlen(".ppm"), ".gpx") ;
	}
    }

    if ((ofp = fopen(outputfile, "w")) == NULL) {
	fprintf(stderr, "\"%s\"", outputfile) ;
	perror("") ;
	exit(1) ;
    }

    ippm = ppm_readppm(ifp, &width, &height, &maxval) ;
    gp.header.width = width ;
    gp.header.height = height ;

    scanline[0] = malloc(3 * gp.header.tilewidth) ;

    numfiles = 0 ;
    for (layer = 0 ; layer < gp.header.layers ; layer++) {
	factor = pow(gp.header.ratio, gp.header.layers-layer-1) ;
	tmpwidth = gp.header.width / factor ;
	tmpheight = gp.header.height / factor ;
	numfiles += ((tmpwidth + gp.header.tilewidth - 1) / gp.header.tilewidth)
	    * ((tmpheight + gp.header.tileheight - 1) / gp.header.tileheight) ;
	gp.header.layersize[layer] = numfiles ;
    }

    for (; layer < MaxLayers ; layer++)
	gp.header.layersize[layer] = gp.header.layersize[layer-1] ;

    gp.header.numfiles = numfiles ;

    gp.offsets[0] = offset = sizeof(gp) + numfiles * sizeof(offsets2[0]) ;
    offsets = xmalloc(numfiles * sizeof(offsets[0])) ;
    offsets2 = xmalloc(numfiles * sizeof(offsets2[0])) ;

    /* First write the jpeg files, then come back to write the header */
    fseek(ofp, offset, SEEK_SET) ;

    tppm = ppm_allocarray(gp.header.tilewidth, gp.header.tileheight) ;

    for (layer = 0, filenum = 0 ; layer < gp.header.layers ; layer++) {
	factor = pow(gp.header.ratio, gp.header.layers-layer-1) ;
	tmpwidth = gp.header.width / factor ;
	tmpheight = gp.header.height / factor ;
	xmax = (tmpwidth + gp.header.tilewidth - 1) / gp.header.tilewidth ;
	ymax = (tmpheight + gp.header.tileheight - 1) / gp.header.tileheight ;
	for (y = 0 ; y < ymax ; y++) {
	    if (y == ymax-1) {
		ysize = tmpheight % gp.header.tileheight ;
		if (ysize == 0) 
		    ysize = gp.header.tileheight ;
	    }
	    else
		ysize = gp.header.tileheight ;
	    for (x = 0 ; x < xmax ; x++) {
		if (x == xmax-1) {
		    xsize = tmpwidth % gp.header.tilewidth ;
		    if (xsize == 0) 
			xsize = gp.header.tilewidth ;
		}
		else
		    xsize = gp.header.tilewidth ;

		for (i = 0 ; i < ysize ; i++) {
		    for (j  = 0 ; j < xsize ; j++) {
			int tmpr, tmpg, tmpb ;
			pixel p ;
			int k, l ;
			tmpr = tmpg = tmpb = 0 ;
			for (k = 0 ; k < factor ; k++) {
			    for (l = 0 ; l < factor ; l++) {
				p = ippm[(y*gp.header.tileheight+i)*factor+k][(x*gp.header.tilewidth+j)*factor+l] ;
				tmpr += PPM_GETR(p) ;
				tmpg += PPM_GETG(p) ;
				tmpb += PPM_GETB(p) ;
			    }
			}
			tmpr /= factor*factor ;
			tmpg /= factor*factor ;
			tmpb /= factor*factor ;
			PPM_ASSIGN(tppm[i][j], tmpr, tmpg, tmpb) ;
		    }
		}

		cinfo.err = jpeg_std_error(&jerr) ;
		jpeg_create_compress(&cinfo) ;

		jpeg_stdio_dest(&cinfo, ofp) ;
		cinfo.image_width = xsize ;
		cinfo.image_height = ysize ;
		cinfo.input_components = 3 ;
		cinfo.in_color_space = JCS_RGB ;
		jpeg_set_defaults(&cinfo) ;

		/* create progressive JPEGS */
		/* doesn't work ?
		jpeg_simple_progression(&cinfo) ;
		*/

		jpeg_set_quality(&cinfo, quality, TRUE) ;

		jpeg_start_compress(&cinfo, TRUE) ;

		for (i = 0 ; i < ysize ; i++) {
		    for (j = 0 ; j < xsize ; j++) {
			pixel p = tppm[i][j] ;
			scanline[0][j*3] = PPM_GETR(p) ;
			scanline[0][j*3+1] = PPM_GETG(p) ;
			scanline[0][j*3+2] = PPM_GETB(p) ;
		    }
		    jpeg_write_scanlines(&cinfo, scanline, 1) ;
		}

		jpeg_finish_compress(&cinfo) ;
		jpeg_destroy_compress(&cinfo) ;

		offsets[filenum] = ftell(ofp) ;
		filenum++ ;
	    }
	}
    }

    ppm_freearray(tppm, gp.header.tileheight) ;

    writeheader(&gp, ofp) ;

    /* note offset to first file is already written in writeheader */

    for (i = 0 ; i < numfiles ; i++)
	tofint(&offsets2[i], offsets[i]) ;

    fwrite(offsets2, sizeof(offsets2[0]), numfiles, ofp) ;

    return 0 ;
}

void usage(char *progname)
{
    fprintf(stderr, "usage: %s [-t tilesize] [-q quality] [-l numlayers] [-o outputfile] [inputfile]\n", progname) ;
    exit(1) ;
}
