/* 
 * Copyright (C) 1995 Mark Boyns
 *
 * This file is part of gif
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <gd.h>
#include "gdfonts.h"
#include "gdfontl.h"

#undef is_true
#define is_true(string)                         \
    (strcmp (string, "true") == 0               \
     || strcmp (string, "t") == 0               \
     || strcmp (string, "1") == 0               \
     || strcmp (string, "yes") == 0             \
     || strcmp (string, "y") == 0               \
     || strcmp (string, "on") == 0)             \

typedef struct 
{
    char *name;
    char *usage;
    int min;
    int max;
    void (*func) (int, char **);
}
COMMAND;

void command_info (int, char **);
void command_help (int, char **);
void command_point (int, char **);
void command_line (int, char **);
void command_rectangle (int, char **);
void command_filled_rectangle (int, char **);
void command_transparent (int, char **);
void command_interlace (int, char **);
void command_circle (int, char **);
void command_filled_circle (int, char **);
void command_fill (int, char **);
void command_border_fill (int, char **);
void command_color (int, char **);
void command_polygon (int, char **);
void command_filled_polygon (int, char **);
void command_create (int, char **);
void command_destroy (int, char **);
void command_copy (int, char **);
void command_change_color (int, char **);
void command_font (int, char **);
void command_boxlabel (int, char **);
void command_label (int, char **);
void command_label_up (int, char **);

COMMAND commands[] =
{
    { "border-fill", "x,y [r g b]", 1, 4, command_border_fill },
    { "circle", "x,y radius", 1, 2, command_circle },
    { "color", "r g b", 3, 3, command_color },
    { "change-color", "r g b r g b", 6, 6, command_change_color },
    { "copy", "x,y image.gif [WxH]", 2, 3, command_copy },
    { "create", "WxH", 1, 1, command_create },
    { "destroy", "", -1, -1, command_destroy },
    { "fill", "x,y", 1, 1, command_fill },
    { "filled-circle", "x,y radius", 1, 2, command_filled_circle },
    { "filled-polygon", "x,y ...", 1, -1, command_filled_polygon },
    { "filled-rectangle", "x,y x,y", 2, 2, command_filled_rectangle },
    { "font", "name", 1, 1, command_font },
    { "help", "", -1, -1, command_help },
    { "info", "", -1, -1, command_info },
    { "interlace", "true|false", 1, 1, command_interlace },
    { "line", "x,y x,y", 2, 2, command_line },
    { "point", "x,y", 1, 1, command_point },
    { "polygon", "x,y ...", 1, -1, command_polygon },
    { "rectangle", "x,y x,y", 2, 2, command_rectangle },
    { "label", "x,y string", 2, -1, command_label },
    { "boxlabel", "x,y string", 2, -1, command_boxlabel },
    { "label-up", "x,y string", 2, -1, command_label_up },
    { "transparent", "[-1|r g b]", 0, 3, command_transparent },
};

#define NCOMMANDS (sizeof (commands) / sizeof (COMMAND))

gdImagePtr image = NULL;	/* current image */
gdFontPtr font = NULL;		/* current font */
int color = 0;			/* current color index */

main (int argc, char **argv)
{
    char *file = NULL;
    char buf[BUFSIZ];
    FILE *fp = NULL;

    if (argc == 1)
    {
	file = "-";
	fp = stdout;
    }
    else if (argc == 2)
    {
	file = argv[1];
	fp = fopen (file, "r+");
	if (!fp)
	{
	    fp = fopen (file, "w");
	}
	else
	{
	    image = gdImageCreateFromGif (fp);
	    fprintf (stderr, "%s: %dx%d, %d colors\n",
		     file, gdImageSX (image), gdImageSY (image), gdImageColorsTotal (image));
	}
    }
    else
    {
	usage ();
    }

    while (fgets (buf, sizeof (buf), stdin))
    {
	int ac, first, i;
	char *av[1024], *p;
	
	ac = 0;
	first = 1;
	while ((p = (char *) strtok (first ? buf : NULL, " \t\r\n")))
	{
	    av[ac++] = p;
	    first = 0;
	}
	av[ac] = NULL;

	for (i = 0; i < NCOMMANDS; i++)
	{
	    if (strcasecmp (commands[i].name, av[0]) == 0)
	    {
		if ((commands[i].min >= 0 && ac-1 < commands[i].min)
		    || (commands[i].max >= 0 && ac-1 > commands[i].max))
		{
		    fprintf (stderr, "usage: %s %s\n", commands[i].name, commands[i].usage);
		}
		else
		{
		    (*commands[i].func) (ac, av);
		}
		break;
	    }
	}
    }
    
    rewind (fp);
    gdImageGif (image, fp);
    fclose (fp);
    gdImageDestroy (image);

    exit (0);
}

void
get_size (char *str, int *x, int *y)
{
    char *p;

    *x = *y = 0;
    
    p = (char *) strchr (str, 'x');
    if (p)
    {
	*p++ = '\0';
	*x = atoi (str);
	*y = atoi (p);
    }
}

void
get_point (char *str, int *x, int *y)
{
    char *p;

    *x = *y = 0;
    
    p = (char *) strchr (str, ',');
    if (p)
    {
	*p++ = '\0';
	*x = atoi (str);
	*y = atoi (p);
    }
}

void
get_points (int argc, char **argv, gdPoint **points, int *npoints)
{
    int i;

    *npoints = argc - 1;
    *points = (gdPoint *) malloc (sizeof (gdPoint) * *npoints);
	
    for (i = 0; i < *npoints; i++)
    {
	get_point (argv[i + 1], &(*points+i)->x, &(*points+i)->y);
    }
}

void
command_color (int argc, char **argv)
{
    int r, g, b;

    r = atoi (argv[1]);
    g = atoi (argv[2]);
    b = atoi (argv[3]);
    
    color = gdImageColorExact (image, r, g, b);
    if (color == -1)
    {
	color = gdImageColorAllocate (image, r, g, b);
    }
}

void
command_create (int argc, char **argv)
{
    int x, y;

    if (argc != 2)
    {
	fprintf (stderr, "%s: invalid arguments\n", argv[0]);
	return;
    }
    
    command_destroy (argc, argv);
    
    get_size (argv[1], &x, &y);
    
    image = gdImageCreate (x, y);
}

void
command_destroy (int argc, char **argv)
{
    if (image)
    {
	gdImageDestroy (image);
	image = NULL;
    }
}

void
command_copy (int argc, char **argv)
{
    gdImagePtr im;
    FILE *fp;
    int x, y, w = -1, h = -1;
    
    get_point (argv[1], &x, &y);

    fp = fopen (argv[2], "r");
    if (fp == NULL)
    {
	perror (argv[2]);
	return;
    }

    if (argv[3])
    {
	get_size (argv[3], &w, &h);
    }
    
    im = gdImageCreateFromGif (fp);
    fclose (fp);

    if (w > 0 && h > 0)
    {
	gdImageCopyResized (image, im, x, y, 0, 0, w, h, gdImageSX (im), gdImageSY (im));
    }
    else
    {
	gdImageCopy (image, im, x, y, 0, 0, gdImageSX (im), gdImageSY (im));
    }

    gdImageDestroy (im);
}

void
command_change_color (int argc, char **argv)
{
    int r, g, b, index;
	
    r = atoi (argv[1]);
    g = atoi (argv[2]);
    b = atoi (argv[3]);

    index = gdImageColorExact (image, r, g, b);
    
    if (index == -1)
    {
	fprintf (stderr, "%s: %d %d %d not found\n", argv[0], r, g, b);
	return;
    }
    else
    {
	int new_index;
	
	gdImageColorDeallocate (image, index);

	r = atoi (argv[4]);
	g = atoi (argv[5]);
	b = atoi (argv[6]);

	new_index = gdImageColorAllocate (image, r, g, b);
	if (new_index != index)
	{
	    fprintf (stderr, "%s: incorrectly changed from %d to %d\n",
		     argv[0], index, new_index);
	    return;
	}
    }
}

void
command_point (int argc, char **argv)
{
    int x, y;

    get_point (argv[1], &x, &y);
    gdImageSetPixel (image, x, y, color);
}

void
command_line (int argc, char **argv)
{
    int x1, y1, x2, y2;

    get_point (argv[1], &x1, &y1);
    get_point (argv[2], &x2, &y2);
    gdImageLine (image, x1, y1, x2, y2, color);
}

void
command_rectangle (int argc, char **argv)
{
    int x1, y1, x2, y2;

    get_point (argv[1], &x1, &y1);
    get_point (argv[2], &x2, &y2);
    gdImageRectangle (image, x1, y1, x2, y2, color);
}

void
command_filled_rectangle (int argc, char **argv)
{
    int x1, y1, x2, y2;

    get_point (argv[1], &x1, &y1);
    get_point (argv[2], &x2, &y2);
    gdImageFilledRectangle (image, x1, y1, x2, y2, color);
}

void
command_circle (int argc, char **argv)
{
    int x, y, radius;

    get_point (argv[1], &x, &y);
    radius = atoi (argv[2]);

    gdImageArc (image, x, y, radius, radius, 0, 360, color);
}

void
command_filled_circle (int argc, char **argv)
{
    int x, y, radius;

    get_point (argv[1], &x, &y);
    radius = atoi (argv[2]);

    gdImageArc (image, x, y, radius, radius, 0, 360, color);
    gdImageFillToBorder (image, x, y, color, color);
}

void
command_fill (int argc, char **argv)
{
    int x, y;
    
    get_point (argv[1], &x, &y);

    gdImageFill (image, x, y, color);
}

void
command_border_fill (int argc, char **argv)
{
    int x, y;
    int border;

    get_point (argv[1], &x, &y);

    if (argc == 2)
    {
	border = color;
    }
    else
    {
	int r, g, b;
	
	r = atoi (argv[2]);
	g = atoi (argv[3]);
	b = atoi (argv[4]);
    
	border = gdImageColorExact (image, r, g, b);
	if (border == -1)
	{
	    border = gdImageColorAllocate (image, r, g, b);
	}
    }
    
    gdImageFillToBorder (image, x, y, border, color);
}

void
command_polygon (int argc, char **argv)
{
    int npoints;
    gdPoint *points;

    get_points (argc, argv, &points, &npoints);
    
    gdImagePolygon (image, points, npoints, color);

    free (points);
}

void
command_filled_polygon (int argc, char **argv)
{
    int npoints;
    gdPoint *points;

    get_points (argc, argv, &points, &npoints);
    
    gdImageFilledPolygon (image, points, npoints, color);

    free (points);
}

void
command_interlace (int argc, char **argv)
{
    if (is_true (argv[1]))
    {
	gdImageInterlace (image, 1);
    }
    else
    {
	gdImageInterlace (image, 0);
    }
}

void
command_transparent (int argc, char **argv)
{
    int index;

    if (argc == 1)
    {
	index = color;
    }
    else if (argc == 2)
    {
	index = atoi (argv[1]);
    }
    else
    {
	int r, g, b;
	
	r = atoi (argv[1]);
	g = atoi (argv[2]);
	b = atoi (argv[3]);
    
	index = gdImageColorExact (image, r, g, b);
	if (index == -1)
	{
	    fprintf (stderr, "%s: %d %d %d not found\n", argv[0], r, g, b);
	    return;
	}
    }
    
    gdImageColorTransparent (image, index);
}

char *
get_strings (int argc, char **argv)
{
    static char buf[1024];

    buf[0] = '\0';
    for (argc = 2; argv[argc]; argc++)
    {
	strcat (buf, argv[argc]);
	if (argv[argc+1])
	{
	    strcat (buf, " ");
	}
    }

    return buf;
}

void
command_label (int argc, char **argv)
{
    int x1, y1;
    char *strings;
    
    get_point (argv[1], &x1, &y1);
    strings = get_strings (argc, argv);
    
    gdImageString (image, font, x1, y1, strings, color);
}

void
command_boxlabel (int argc, char **argv)
{
    int x1, y1;
    char *strings;
    int white, black;
    int xborder = 2;
    int yborder = 0;
    
    get_point (argv[1], &x1, &y1);
    strings = get_strings (argc, argv);

    white = gdImageColorExact (image, 255, 255, 255);
    if (white == -1)
    {
	white = gdImageColorAllocate (image, 255, 255, 255);
    }

    black = gdImageColorExact (image, 0, 0, 0);
    if (black == -1)
    {
	black = gdImageColorAllocate (image, 0, 0, 0);
    }
    
    
    gdImageFilledRectangle (image, x1, y1, x1 + font->w * strlen (strings) + xborder*2, y1 + font->h + yborder*2, white);
    gdImageRectangle (image, x1, y1, x1 + font->w * strlen (strings) + xborder*2, y1 + font->h + yborder*2, black);
    gdImageString (image, font, x1 + xborder, y1 + yborder, strings, color);
}

void
command_label_up (int argc, char **argv)
{
    int x1, y1;
    char *strings;
    
    get_point (argv[1], &x1, &y1);
    strings = get_strings (argc, argv);

    gdImageStringUp (image, font, x1, y1, strings, color);
}

void
command_font (int argc, char **argv)
{
    if (font == NULL)
    {
	font = gdFontLarge;
    }
    
    if (strcmp (argv[1], "small") == 0)
    {
	font = gdFontSmall;
    }
    else if (strcmp (argv[1], "large") == 0)
    {
	font = gdFontLarge;
    }
}

void
command_help (int argc, char **argv)
{
    int i;
    
    for (i = 0; i < NCOMMANDS; i++)
    {
	printf ("%s %s\n", commands[i].name, commands[i].usage);
    }    
}

void
command_info (int argc, char **argv)
{
    printf ("%dx%d %d colors\n",
    	gdImageSX (image), gdImageSY (image), gdImageColorsTotal (image));
}

int
usage ()
{
    printf ("usage: gif [image.gif]\n");
    command_help (0, 0);
    exit (0);
}
