/*
 * seejpeg.c
 *
 * Copyright (C) 1993-1999 Evan Harris
 *
 * Permission is granted to freely redistribute and modify this code,
 * providing the author(s) get credit for having written it.
 */

/* 	$Id: seejpeg.c,v 1.9 1999/11/07 06:01:56 evan Exp $	 */

#ifndef lint
static char vcid[] = "$Id: seejpeg.c,v 1.9 1999/11/07 06:01:56 evan Exp $";
#endif /* lint */

#include "seejpeg.h"
#include <string.h>
#include <getopt.h>
#include <vga.h>
#include <sys/times.h>

#define VERSION "seejpeg version 1.10"

int opt_greyscale = 0;
int opt_quantize = 0;
int opt_verbose = 0;
int opt_widthonly = 0;
int opt_onepass = 0;
int opt_forcemode = TEXT;
int opt_doublex = 0;
int opt_doubley = 0;
int opt_slideshow = -1;
int opt_cycle = 0;
int opt_centre = 0;
int opt_randomise = 0;
int opt_maxwidth = 0;
int opt_minwidth = 0;
double opt_fuzz = 1.0;
double opt_gamma = 1.0;
int opt_enable_5x4 = 1;
int opt_enable_4x3 = 1;
int opt_enable_8x5 = 0;
int opt_enable_16x9 = 0;

int next_image = NEXT_IMAGE;


static void
print_usage(FILE* fd, const char* program_name)
{
    static const char* argument_description[] =
    {
	"-1    --one-pass           one pass quantization (-q implicit)",
	"-c    --centre             centre the image if it is smaller than video mode",
	"-C    --cycle              cycle through pictures",
	"-dx   --double=x           double the image in the x direction",
	"-f F  --fuzz=F             set fuzz factor to determine video mode",
	"-F M  --mode=M             force the use of a specified video mode",
	"-g    --grey               force greyscale mode (-1 implicit)",
	"-G G  --gamma=G            specify monitor gamma correction value",
	"-h    --help               display this usage message",
	"-m W  --min-mode-width=W   specify minimum mode width to use",
	"-M W  --max-mode-width=W   specify maximum mode width to use",
	"-q    --quantize           quantize picture (256 colour mode)",
	"-r    --randomize          randomize order of images",
	"-s T  --slideshow-delay=T  slideshow, wait T seconds between images",
	"-v    --verbose            display image and video mode information",
	"-w    --width-only         only use width to determine video mode",
	"      --enable-5x4         allow 5x4 video modes to be used (default)",
	"      --disable-5x4        do not allow 5x4 video modes to be used",
	"      --enable-4x3         allow 4x3 video modes to be used (default)",
	"      --disable-4x3        do not allow 4x3 video modes to be used",
	"      --enable-8x5         allow 8x5 video modes to be used",
	"      --disable-8x5        do not allow 4x3 video modes to be used (default)",
	"      --enable-16x9        allow 16x9 video modes to be used",
	"      --disable-16x9       do not allow 16x9 video modes to be used (default)",
	NULL
    };
    int i;

    fprintf(fd, "Usage: %s [-{option} ...] {file} ...\n", program_name);
    for (i = 0; argument_description[i] != NULL; i++)
    {
	fprintf(fd, "  %s\n", argument_description[i]);
    }
    fprintf(fd, "File types supported: JPEG, GIF, PPM, BMP, TARGA\n");
}


static const char*
parse_options(int argc, char * const argv[], const char** forcemodestr)
{
    static struct option long_options[] =
    {
	{ "one-pass", 0, NULL, '1' },
	{ "centre", 0, NULL, 'c' },
	{ "center", 0, NULL, 'c' },
	{ "double", 1, NULL, 'd' },
	{ "fuzz", 1, NULL, 'f' },
	{ "gray", 0, NULL, 'g' },
	{ "grey", 0, NULL, 'g' },
	{ "quantize", 0, NULL, 'q' },
	{ "quantise", 0, NULL, 'q' },
	{ "random-order", 0, NULL, 'r' },
	{ "verbose", 0, NULL, 'v' },
	{ "width-only", 0, NULL, 'w' },
	{ "cycle", 0, NULL, 'C' },
	{ "mode", 1, NULL, 'F' },
	{ "gamma", 1, NULL, 'G' },
	{ "slideshow-delay", 1, NULL, 's' },
	{ "min-mode-width", 1, NULL, 'm' },
	{ "max-mode-width", 1, NULL, 'M' },
	{ "enable-5x4", 0, &opt_enable_5x4, 1 },
	{ "disable-5x4", 0, &opt_enable_5x4, 0 },
	{ "enable-4x3", 0, &opt_enable_4x3, 1 },
	{ "disable-4x3", 0, &opt_enable_4x3, 0 },
	{ "enable-8x5", 0, &opt_enable_8x5, 1 },
	{ "disable-8x5", 0, &opt_enable_8x5, 0 },
	{ "enable-16x9", 0, &opt_enable_16x9, 1 },
	{ "disable-16x9", 0, &opt_enable_16x9, 0 },
	{ "help", 0, NULL, 'h' }
    };
    int option_index;
    const char* errstr = NULL;
    char* ch;
    int c;

    while ((c = getopt_long(argc, argv, "1cd:f:ghm:qrs:vwCF:G:M:",
			    long_options, &option_index)) != -1)
    {
	switch (c)
	{	
	    case 0:
	        break;
	    case '1':
		opt_onepass = 1;
		break;
	    case 'c':
		opt_centre = 1;
		break;
	    case 'd':
		for (ch = optarg; *ch != '\0'; ch++)
		{
		    switch (*ch)
		    {
			case 'x':
			    opt_doublex = 1;
			    break;
			case 'y':
			    opt_doubley = 1;
			    errstr = "doubling in Y direction not yet implemented";
			    break;
			default:
			    errstr = "can only double image in X or Y direction";
			    break;
		    }
		}
		break;
	    case 'f':
		opt_fuzz = atof(optarg);
		if (opt_fuzz < 0.5 || opt_fuzz > 1.0)
		{
		    errstr = "fuzz factor must be in range [0.5,1.0]";
		}
		break;
	    case 'g':
		opt_greyscale = 1;
		break;
	    case 'q':
		opt_quantize = 1;
		break;
	    case 'r':
		opt_randomise = 1;
		break;
	    case 'v':
		opt_verbose = 1;
		break;
	    case 'w':
		opt_widthonly = 1;
		break;
	    case 'C':
		opt_cycle = 1;
		break;
	    case 'F':
		*forcemodestr = optarg;
		break;
	    case 'G':
		opt_gamma = atof(optarg);
		if (opt_gamma < 0.1 || opt_gamma > 5.0)
		{
		    errstr = "gamma correction must be in range [0.1, 5.0]";
		}
		break;
	    case 's':
		opt_slideshow = atoi(optarg);
		if (opt_slideshow < -1)
		{
		    errstr = "slideshow waiting time must not be negative";
		}
		break;
	    case 'm':
		opt_minwidth = atoi(optarg);
		if (opt_minwidth < 0)
		{
		    errstr = "minimum mode width must be positive";
		}
		break;
	    case 'M':
		opt_maxwidth = atoi(optarg);
		if (opt_maxwidth < 0)
		{
		    errstr = "maximum mode width must be positive";
		}
		break;
	    case 'h':
	    case '?':
		/*
		 * Asking for help and passing an illegal paramter
                 * have the same result.
		 */
		errstr = "";
		break;
	}
    }

    if (opt_minwidth > 0
	&& opt_maxwidth > 0
	&& opt_minwidth > opt_maxwidth)
    {
	errstr = "maximum mode width must be greater than minimum mode width";
    }

    return errstr;
}

static void
handle_options(int argc, char * const argv[], const char** forcemodestr)
{
    const char* errstr = parse_options(argc, argv, forcemodestr);

    if (errstr != NULL && strcmp(errstr, "") != 0)
    {
	fprintf(stderr, "Error: %s\n", errstr);
	exit(EXIT_FAILURE);
    }
    
    if ((errstr != NULL && strcmp(errstr, "") == 0)
	|| argc - optind == 0)
    {
	fprintf(stderr, VERSION"\n");
	print_usage(stderr, argv[0]);
	exit(EXIT_FAILURE);
    }
}


int
main(int argc, char* const argv[])
{
    int arg, arg2, num_args, err = 0;
    short *args, t;
    const char* forcemodestr = NULL;

    handle_options(argc, argv, &forcemodestr);
  
    if (!opt_verbose)
    {
	vga_disabledriverreport();
    }

    vga_init();

    if (forcemodestr != NULL)
    {
	opt_forcemode = vga_getmodenumber((char*)forcemodestr);
	if (is_supported_256_mode(opt_forcemode))
	{
	    opt_quantize = 1;
	}
	else if (!is_supported_32k16m_mode(opt_forcemode))
	{
	    fprintf(stderr, "Error: mode name '%s' is unsupported by chipset/video card/svgalib\n",
		    forcemodestr);
	    return EXIT_FAILURE;
	}
    }

    num_args = argc - optind;
    args = malloc(num_args * sizeof(short));
    if (args != NULL)
    {
	for (arg = 0; arg < num_args; arg++)
	{
	    args[arg] = optind + arg;
	}
    }
    
    if (opt_randomise)
    {
	srand48(times(NULL));
    }

    chiptype_init();
    gamma_table_init(opt_gamma);
    if (opt_verbose)
    {
	printf("%-36s %-14s %-16s %s\n",
	       "File", "Image", "Video Mode", "Logical");
    }
    do
    {
	if (opt_randomise)
	{
	    for (arg = 0; arg < num_args; arg++)
	    {
		args[arg] = optind + arg;
	    }
	    for (arg = 0; arg < num_args; arg++)
	    {
		arg2 = lrand48() % num_args;
		t = args[arg];
		args[arg] = args[arg2];
		args[arg2] = t;
	    }
	}

	arg = 0;
	while (arg < num_args)
	{
	    if (opt_verbose)
	    {
		printf("%-36s ", argv[args[arg]]);
	    }
	    err = read_image_file(argv[args[arg]]);
	    if (opt_verbose)
	    {
		if (err != 1)
		{
		    printf("Error reading image\n");
		} 
	    }
	    switch (next_image)
	    {
		case NEXT_IMAGE:
		    arg++;
		    break;
		case PREVIOUS_IMAGE:
		    arg--;
		    break;
		case SAME_IMAGE:
		    break;
		case NO_IMAGE:
		    arg = num_args;
		    break;
	    }
	    next_image = NEXT_IMAGE;
	}
    }
    while (opt_cycle);
    display_shutdown();

    free(args);

    return EXIT_SUCCESS;
}
