/*
 * Preferences for Euler
 *
 * Copyright (C) 2001 Eric Boucharé <bouchare.eric@wanadoo.fr>
 *
 * 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 <glib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>       /* for strchr */
#include "rc.h"

static short colors[3][MAX_COLORS] = {
	{255,0,100,0  ,0  ,0  ,100,150,100,50,220 ,80  ,80  ,80  ,140,190},
	{255,0,0  ,100,0  ,100,100,150,100,50,80  ,220 ,80  ,140 ,140,190},
	{255,0,0  ,0  ,100,100,  0,150,100,50,80  ,80  ,220 ,140 , 80,190}
};

/* define enumeration values to be returned for specific symbols */
typedef enum {
	E_TOKEN_FIRST = G_TOKEN_LAST,
	E_TOKEN_SAVEATEXIT,
	E_TOKEN_BROWSER,
	E_TOKEN_TFONT,
	E_TOKEN_PFONT,
	E_TOKEN_TWIDTH,
	E_TOKEN_THEIGHT,
	E_TOKEN_GWIDTH,
	E_TOKEN_GHEIGHT,
	E_TOKEN_ESTACK,
	E_TOKEN_GSTACK,
	E_TOKEN_GLINES,
	E_TOKEN_GCOLORS,
	E_TOKEN_LAST
} EulerRcTokenType;


/* symbol array */
static const struct {
	gchar *name;
	guint  token;
} symbols[] = {
	{ "saveatexit",	E_TOKEN_SAVEATEXIT },
	{ "browser",	E_TOKEN_BROWSER },
	{ "tfont",		E_TOKEN_TFONT },
	{ "pfont",		E_TOKEN_PFONT },
	{ "twidth",		E_TOKEN_TWIDTH },
	{ "theight",	E_TOKEN_THEIGHT },
	{ "gwidth",		E_TOKEN_GWIDTH },
	{ "gheight",	E_TOKEN_GHEIGHT },
	{ "estack",		E_TOKEN_ESTACK },
	{ "gstack",		E_TOKEN_GSTACK },
	{ "glines",		E_TOKEN_GLINES },
	{ "colors",		E_TOKEN_GCOLORS }
};

static const guint n_symbols = sizeof (symbols) / sizeof (symbols[0]);


static guint eulerrc_parse(GScanner *scanner, EulerPrefs *prefs)
{
	guint token;

	token = g_scanner_get_next_token(scanner);
	
	switch (token) {
		case E_TOKEN_SAVEATEXIT:
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_EQUAL_SIGN) return G_TOKEN_EQUAL_SIGN;
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_INT) return G_TOKEN_INT;
			prefs->saveatexit = scanner->value.v_int;
			break;
		case E_TOKEN_BROWSER:
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_EQUAL_SIGN) return G_TOKEN_EQUAL_SIGN;
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_STRING) return G_TOKEN_STRING;
			strcpy(prefs->browser, scanner->value.v_string);
			break;
		case E_TOKEN_TFONT:
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_EQUAL_SIGN) return G_TOKEN_EQUAL_SIGN;
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_STRING) return G_TOKEN_STRING;
			strcpy(prefs->tfont, scanner->value.v_string);
			break;
		case E_TOKEN_PFONT:
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_EQUAL_SIGN) return G_TOKEN_EQUAL_SIGN;
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_STRING) return G_TOKEN_STRING;
			strcpy(prefs->pfont, scanner->value.v_string);
			break;
		case E_TOKEN_TWIDTH:
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_EQUAL_SIGN) return G_TOKEN_EQUAL_SIGN;
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_INT) return G_TOKEN_INT;
			prefs->twidth = scanner->value.v_int;
			break;
 		case E_TOKEN_THEIGHT:
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_EQUAL_SIGN) return G_TOKEN_EQUAL_SIGN;
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_INT) return G_TOKEN_INT;
			prefs->theight = scanner->value.v_int;
			break;
		case E_TOKEN_GWIDTH:
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_EQUAL_SIGN) return G_TOKEN_EQUAL_SIGN;
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_INT) return G_TOKEN_INT;
			prefs->gwidth = scanner->value.v_int;
			break;
 		case E_TOKEN_GHEIGHT:
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_EQUAL_SIGN) return G_TOKEN_EQUAL_SIGN;
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_INT) return G_TOKEN_INT;
			prefs->gheight = scanner->value.v_int;
			break;
		case E_TOKEN_ESTACK:
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_EQUAL_SIGN) return G_TOKEN_EQUAL_SIGN;
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_INT) return G_TOKEN_INT;
			prefs->estack = scanner->value.v_int;
			break;
		case E_TOKEN_GSTACK:
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_EQUAL_SIGN) return G_TOKEN_EQUAL_SIGN;
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_INT) return G_TOKEN_INT;
			prefs->gstack = scanner->value.v_int;
			break;
		case E_TOKEN_GLINES:
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_EQUAL_SIGN) return G_TOKEN_EQUAL_SIGN;
			token = g_scanner_get_next_token(scanner);
			if (token != G_TOKEN_INT) return G_TOKEN_INT;
			prefs->glines = scanner->value.v_int;
			break;
		case E_TOKEN_GCOLORS:
			{
				int i, j;
				
				token = g_scanner_get_next_token(scanner);
				if (token != G_TOKEN_EQUAL_SIGN) return G_TOKEN_EQUAL_SIGN;
				token = g_scanner_get_next_token(scanner);
				if (token != G_TOKEN_LEFT_CURLY) return G_TOKEN_LEFT_CURLY;
				
				for (j=0 ; j<MAX_COLORS ;j++) {
					token = g_scanner_get_next_token(scanner);
					if (token != G_TOKEN_LEFT_CURLY) return G_TOKEN_LEFT_CURLY;
					for (i=0 ; i<3 ; i++) {
						token = g_scanner_get_next_token(scanner);
						if (token != G_TOKEN_INT) return G_TOKEN_INT;
						prefs->colors[i][j] = scanner->value.v_int;
						if (i!=2) {
							token = g_scanner_get_next_token(scanner);
							if (token != G_TOKEN_COMMA) return G_TOKEN_COMMA;
						}
					}
					token = g_scanner_get_next_token(scanner);
					if (token != G_TOKEN_RIGHT_CURLY) return G_TOKEN_RIGHT_CURLY;
				}
				token = g_scanner_get_next_token(scanner);
				if (token != G_TOKEN_RIGHT_CURLY) return G_TOKEN_RIGHT_CURLY;
			}
			break;
		default:
			return token;
	}
	
	return G_TOKEN_NONE;
}


static gint eulerrc_load(EulerPrefs *prefs)
{
	GScanner *scanner;
	gint fd;
	guint i, expected_token = G_TOKEN_NONE;
	gchar *filename;

	filename = g_strconcat(g_get_home_dir(),"/.euler/eulerrc",NULL);
	fd = open(filename, O_RDONLY);
	g_free(filename);
	if (fd < 0)	return 0;

	scanner = g_scanner_new(NULL);

	/* Don't return G_TOKEN_SYMBOL, but the symbol's value */
	scanner->config->symbol_2_token = TRUE;

	/* Don't return G_TOKEN_IDENTIFIER, but convert it to string */
	scanner->config->identifier_2_string = TRUE;

	g_scanner_freeze_symbol_table(scanner);
	for (i = 0; i < n_symbols; i++)
		g_scanner_add_symbol(scanner,symbols[i].name,GINT_TO_POINTER(symbols[i].token));
	g_scanner_thaw_symbol_table(scanner);

	g_scanner_input_file(scanner, fd);
	scanner->input_name = ".eulerrc";

	/*
	 * Scanning loop, we parse the input until it's end is reached,
	 * the scanner encountered a lexing error, or our sub routine came
	 * across invalid syntax
	 */
	while(1) {
		expected_token = eulerrc_parse(scanner,prefs);

		if (expected_token != G_TOKEN_NONE) {
			switch (expected_token) {
				case G_TOKEN_EQUAL_SIGN:
					g_scanner_unexp_token(scanner,expected_token,NULL,"=",NULL,NULL,TRUE);
					break;
				case G_TOKEN_EOF:
					break;
				default:
					g_scanner_unexp_token(scanner,expected_token,NULL,NULL,NULL,NULL,TRUE);
					break;
			}
			break;
		}
	}

	g_scanner_destroy(scanner);

	close(fd);

	return 1;
}


/*
 *	preference init
 */
EulerPrefs eulerrc_init()
{
	EulerPrefs prefs;
	int i,j;
	
	prefs.saveatexit = E_SAVEATEXIT_DEFAULT;
	prefs.twidth	 = E_TWIDTH_DEFAULT;
	prefs.theight	 = E_THEIGHT_DEFAULT;
	prefs.gwidth	 = E_GWIDTH_DEFAULT;
	prefs.gheight	 = E_GHEIGHT_DEFAULT;
	prefs.estack	 = E_ESTACK_DEFAULT;
	prefs.gstack	 = E_GSTACK_DEFAULT;
	prefs.glines	 = E_GLINES_DEFAULT;
	for (i=0 ; i<3 ; i++)
		for (j=0 ; j<MAX_COLORS ; j++)
			prefs.colors[i][j] = colors[i][j];
	strcpy(prefs.browser, E_BROWSER_DEFAULT);
	strcpy(prefs.tfont, E_TFONT_DEFAULT);
	strcpy(prefs.pfont, E_PFONT_DEFAULT);
	eulerrc_load(&prefs);
	return prefs;
}

static int is_dir(char *dirname)
{
	struct stat buf;
	
	if (stat(dirname,&buf)==0) {
		if (S_ISDIR(buf.st_mode))
			return 1;
	}
	return 0;
}


/*
 *  Preferences memory-deallocation
 *  (Call this one at exit time)
 */
void eulerrc_save(EulerPrefs *prefs)
{
	FILE *file;
	char *filename, *dirname;
	
	dirname = g_strconcat(g_get_home_dir(),"/.euler",NULL);
	if (!is_dir(dirname)) {
		mkdir(dirname,0755);
	}
	g_free(dirname);
	
	filename = g_strconcat(g_get_home_dir(),"/.euler/eulerrc",NULL);
	file = fopen(filename, "w");
	g_free(filename);

	if (file) {
		int i,j;
		
		fprintf(file,"#\n# Euler ressouce file\n#\n");
		fprintf(file,"#\n# flag to tell Euler if it must save the preferences when the program exits.\n#\n");
		fprintf(file,"saveatexit = %d\n",prefs->saveatexit);
		fprintf(file,"\n#\n# browser for documentation viewing\n#\n");
		fprintf(file,"browser = \"%s\"\n",prefs->browser);
		fprintf(file,"\n#\n# text window font\n#\n");
		fprintf(file,"tfont = \"%s\"\n",prefs->tfont);
		fprintf(file,"\n#\n# editor font\n#\n");
		fprintf(file,"pfont = \"%s\"\n",prefs->pfont);
		fprintf(file,"\n#\n# text window width and height (in chars)\n#\n");
		fprintf(file,"twidth = %d\ntheight = %d\n",prefs->twidth,prefs->theight);
		fprintf(file,"\n#\n# graphics window width and height (in pixels)\n#\n");
		fprintf(file,"gwidth = %d\ngheight = %d\n",prefs->gwidth,prefs->gheight);
		fprintf(file,"\n#\n# Euler stack size in kbyte\n#\n");
		fprintf(file,"estack = %d\n",prefs->estack);
		fprintf(file,"\n#\n# graphic stack in kbyte\n#\n");
		fprintf(file,"gstack = %d\n",prefs->gstack);
		fprintf(file,"\n#\n# glines fixes the font size for the graphics window\n# the font size is gheight / glines.\n#\n");
		fprintf(file,"glines = %d\n",prefs->glines);
		fprintf(file,"\n#\n# the 16 basic colors used for the graphics\n#\n");
		fprintf(file,"colors = {\n");
		for (j=0 ; j<MAX_COLORS ; j++) {
			fprintf(file,"    { ");
			for (i=0 ; i<3 ; i++) {
				fprintf(file,"%3d ",prefs->colors[i][j]);
				if (i!=2) fprintf(file,", ");
			}
			fprintf(file,"}\n");
		}
		fprintf(file,"}\n");
		fclose(file);
	}
}
