/* +-------------------------------------------------------------------+ */
/* | Copyright 1993, David Koblas (koblas@netcom.com)                  | */
/* |                                                                   | */
/* | Permission to use, copy, modify, and to distribute this software  | */
/* | and its documentation for any purpose is hereby granted without   | */
/* | fee, provided that the above copyright notice appear in all       | */
/* | copies and that both that copyright notice and this permission    | */
/* | notice appear in supporting documentation.  There is no           | */
/* | representations about the suitability of this software for        | */
/* | any purpose.  this software is provided "as is" without express   | */
/* | or implied warranty.                                              | */
/* |                                                                   | */
/* +-------------------------------------------------------------------+ */

#include <stdio.h>
#include <pwd.h>
#include <ctype.h>
#include <X11/Intrinsic.h>
#include <X11/Xos.h>
#include <sys/stat.h>
#include "image.h"
#include "rc.h"

#ifndef NOSTDHDRS
#include <stdlib.h>
#include <unistd.h>
#endif

extern char	*mktemp(char*);
extern void	StrToArgv(char*, int*, char**);
extern char	*GetDefaultRC(void);

#define RC_FILENAME	".XPaintrc"

static String	defaultRC[] = {
#include "DefaultRC.txt.h"
};

/*
**   RC File Syntax
**
**     brush   [ BeginData\n ... \nEndData | filename ]
**
**     pattern [ BeginData\n ... \nEndData | filename ]
**
**     solid   [ name | x-rgb-def | r g b ]
**     
**     \n# -- comment
*/

#define EQ(a,b)		(strcasecmp(a,b) == 0)

static int	tempIndex = -1;
static char	*tempName[10];
static RCInfo	*baseInfo = NULL;

static FILE	*openTemp(char **np)
{
	char		*n;
	char		xx[256];

	if ((n = getenv("TMPDIR")) == NULL)
		n = "/tmp";

	strcpy(xx, n);
	strcat(xx, "/XPaintXXXXXXX");
	n = mktemp(xx);
	tempName[++tempIndex] = XtNewString(n);
	if (np != NULL)
		*np = tempName[tempIndex];
	return fopen(tempName[tempIndex], "w");
}
static void	removeTemp()
{
	if (tempIndex < 0)
		return;
	if (tempName[tempIndex] != NULL) {
		unlink(tempName[tempIndex]);
		XtFree((XtPointer)tempName[tempIndex]);
	}
	tempName[tempIndex--] = NULL;
}

static void addImage(RCInfo *info, Image *image, Boolean isBrush)
{
	if (isBrush) {
		info->brushes = (Image**)XtRealloc((XtPointer)info->brushes, (info->nbrushes + 1) * sizeof(Image*));
		info->brushes[info->nbrushes++] = image;
	} else {
		info->images = (Image**)XtRealloc((XtPointer)info->images, (info->nimages + 1) * sizeof(Image*));
		info->images[info->nimages++] = image;
	}
}
static void addSolid(RCInfo *info, char *color)
{
	info->colors = (char**)XtRealloc((XtPointer)info->colors, (info->ncolors + 1) * sizeof(char*));
	info->colors[info->ncolors++] = XtNewString(color);
}

static RCInfo *makeInfo()
{
	RCInfo	*info;

	info = XtNew(RCInfo);
	info->freed = False;
	info->nimages = 0;
	info->nbrushes = 0;
	info->colorFlags = NULL;
	info->colorPixels = NULL;
	info->images  = XtNew(Image*);
	info->brushes = XtNew(Image*);
	info->ncolors = 0;
	info->colors = XtNew(char*);

	baseInfo = info;

	return info;
}

void FreeRC(RCInfo *info)
{
	int	i;

	if (info->colors != NULL) {
		for (i = 0; i < info->ncolors; i++)
			XtFree((XtPointer)info->colors[i]);
		XtFree((XtPointer)info->colors);
	}
	if (info->colorFlags != NULL)
		XtFree((XtPointer)info->colorFlags);
	if (info->colorPixels != NULL)
		XtFree((XtPointer)info->colorPixels);

	for (i = 0; i < info->nimages; i++)
		ImageDelete(info->images[i]);
	if (info->images != NULL)
		XtFree((XtPointer)info->images);

	for (i = 0; i < info->nbrushes; i++)
		ImageDelete(info->brushes[i]);
	if (info->brushes != NULL)
		XtFree((XtPointer)info->brushes);

	XtFree((XtPointer)info);

	if (info == baseInfo)
		baseInfo = NULL;
}


static char	*expand(char *path)
{
	static char	out[512];
	char		name[80];
	char		*pp = path, *cp;
	struct passwd	*pw;

	if (*path != '~')
		return path;
	path++;
	cp = name;
	while (*pp != '/' && *pp != '\0')
		*cp++ = *pp++;
	*cp = '\0';
	if (name[0] == '\0') {
		pw = getpwuid(getuid());
	} else {
		pw = getpwnam(name);
	}
	if (pw == NULL)
		return path;
	strcpy(out, pw->pw_dir);
	strcat(out, "/");
	strcat(out, pp);

	return out;
}

static Boolean readRC(RCInfo **info, char *file)
{
	FILE	*fd = fopen(file, "r");
	char	buf[512];
	int	lineno = 0;
	int	argc;
	char	*argv[128 + 2];

	if (fd == NULL)
		return False;

	while (fgets(buf, sizeof(buf), fd) != NULL) {
		lineno++;
		if (buf[0] == '#' || buf[0] == '!')
			continue;
		StrToArgv(buf, &argc, argv);
		if (argc == 0)
			continue;
		if (EQ(argv[0], "reset")) {
			FreeRC(*info);
			*info = makeInfo();
		} else if (EQ(argv[0], "solid")) {
			addSolid(*info, argv[1]);
		} else if (EQ(argv[0], "pattern") || EQ(argv[0], "brush")) {
			extern Image	*ReadMagic(char*);
			char		*nm;
			Image		*image;

			if (EQ(argv[1], "BeginData")) {
				FILE	*ofd;
				
				ofd = openTemp(&nm);
				while (fgets(buf, sizeof(buf), fd) != NULL) {
					if (strncmp(buf, "EndData", 7) == 0)
						break;
					if (ofd != NULL)
						fputs(buf, ofd);
				}
				if (ofd != NULL) {
					fclose(ofd);
				} else {
					removeTemp();
					continue;
				}
			} else {
				nm = expand(argv[1]);
			}

			if ((image = ReadMagic(nm)) != NULL)
				addImage(*info, image, EQ(argv[0], "brush"));

			removeTemp();
		}
	}

	return True;
}

/*
**  Simple RC reading strategy:
**    load default
**    append users ~/.XPaintrc
**    append users ./.XPaintrc
**
*/
RCInfo	*ReadDefaultRC()
{
	static Boolean	inited = False;
	static Boolean	have[2] = { False, False };
	static time_t	lastMtime;
	static RCInfo	*info = NULL;
	static char	homeRC[256];
	FILE		*fd;
	int		i;
	char		*tn;
	struct passwd	*pw = getpwuid(getuid());
	struct stat	statbufA, statbufB;
	char		*rcf;

	if (!inited) {
		inited = True;
		if (pw != NULL && pw->pw_dir != NULL) {
			strcpy(homeRC, pw->pw_dir);
			strcat(homeRC, "/");
			strcat(homeRC, RC_FILENAME);
		} else {
			homeRC[0] = '\0';
		}
	}

	if ((rcf = GetDefaultRC()) != NULL) {
		if (stat(rcf, &statbufA) < 0)	/* missing file? */
			goto readit;

		if (info == NULL || statbufA.st_mtime > lastMtime) {
			info = makeInfo();
			readRC(&info, rcf);
			lastMtime = statbufA.st_mtime;
		}
	} else {
		if (info != NULL) {
			Boolean	read = False;
			Boolean	hA, hB;

			hA = (stat(homeRC, &statbufA) >= 0);
			hB = (stat(RC_FILENAME, &statbufB) >= 0);

			if (hA != have[0] || hB != have[1])
				goto readit;
			
			if (hA && statbufA.st_mtime > lastMtime)
				goto readit;
			if (hB && statbufB.st_mtime > lastMtime)
				goto readit;

			/*
			**  No change
			*/
			return info;
		}
readit:
		if (info != NULL)
			FreeRC(info);
		info = makeInfo();

		/*
		**  Set time information
		*/
		have[0] = (stat(homeRC, &statbufA) >= 0);
		have[1] = (stat(RC_FILENAME, &statbufB) >= 0);

		if (have[0] && have[1]) {
			if (statbufA.st_mtime > statbufB.st_mtime)
				lastMtime = statbufA.st_mtime;
			else
				lastMtime = statbufB.st_mtime;
		} else if (have[0]) {
			lastMtime = statbufA.st_mtime;
		} else if (have[1]) {
			lastMtime = statbufB.st_mtime;
		}

		/*
		**  Load the default RC
		*/
		if ((fd = openTemp(&tn)) != NULL) {
			for (i = 0; i < XtNumber(defaultRC); i++) {
				fputs(defaultRC[i], fd);
				putc('\n', fd);
			}
			fclose(fd);

			readRC(&info, tn);
			removeTemp();
		}

		/*
		**  Load ~/.XPaintrc
		*/
		if (homeRC[0] != '\0')
			readRC(&info, homeRC);

		/*
		**  Load ".XPaintrc"
		*/
		readRC(&info, RC_FILENAME);
	}

	if (info->ncolors == 0 && info->nimages == 0) {
		addSolid(info, "black");
		addSolid(info, "white");
		addSolid(info, "red");
		addSolid(info, "green");
		addSolid(info, "blue");
		addSolid(info, "cyan");
		addSolid(info, "magenta");
		addSolid(info, "yellow");
	}

	return info;
}

RCInfo *ReadRC(char *file)
{
	RCInfo	*info = makeInfo();

	if (!readRC(&info, file)) {
		/*
		**  Error occured
		*/
		FreeRC(info);
		return NULL;
	}

	return info;
}
