/*ScianPreferences.c
  Eric Pepke
  June 15, 1991
  Preferences in SciAn
*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianMethods.h"
#include "ScianLists.h"
#include "ScianIDs.h"
#include "ScianStyle.h"
#include "ScianWindows.h"
#include "ScianObjWindows.h"
#include "ScianDialogs.h"
#include "ScianControls.h"
#include "ScianButtons.h"
#include "ScianTextBoxes.h"
#include "ScianErrors.h"
#include "ScianPreferences.h"
#include "ScianScripts.h"

typedef union pv
	{
	    char *string;		/*String value*/
	    Bool truth;			/*Expression true or false*/
	    long integer;		/*Numeric value*/
	} PrefValue;

typedef struct
    {
	char *shortName;		/*Short name of the preference*/
	char *longName;			/*Long name of the preference*/
	int type;			/*Type of the preference*/
	char *helpString;		/*String of the help*/
    } PrefInfo;

typedef struct
    {
	Bool exists;			/*True iff the preference exists*/
	Bool changed;			/*True iff the preference has been changed*/
	PrefValue value;		/*Value of the preference*/
    } PrefState;

PrefInfo prefInfo[NPREFERENCES] =
    {
	{"DEFDIR", "Data File Directory", PT_STRING,
"This controls the default directory that will be used when you choose NewFileWindow from the File menu.  \
Edit the string in the text box to change it."},
	{"ROTINERTIA", "Rotation Inertia", PT_YNBOOL,
"This controls the inertia when you rotate objects within a space.  \
If Yes is selected, the space will continue to rotate it when you give it a spin and release the mouse button while moving the mouse.  \
You can stop the rotation by clicking in the space again."},
	{"NEWWINPLACE", "New Window Placement", PT_AMBOOL,
"This controls how new windows will be placed on the screen.  If Automatic is \
selected, windows will be created with a fixed size and automatically staggered.  \
If Manual is selected, you will need to drag out the outline of new windows \
before they appear on the screen."},
	{"DRAWMOVING", "Interactive Drawing", PT_AFFINT,
"This controls how visualization objects are drawn when you are interactively \
changing them.  If Full Quality is selected, objects will always be drawn in full \
quality.  If Faster is selected, objects will be drawn with only a skeleton while you are \
interactively moving them, which is much faster.  This can provide better feedback \
on slower computers.  If Automatic is selected, SciAn will automatically choose whether \
objects are to be drawn fully or faster depending on how much time they take to draw."},
	{"STAGGERICONS", "Stagger Icons", PT_YNBOOL,
"This controls whether icons will be staggered in icon corrals.  If Yes is selected, \
the vertical position of the icons will be staggered so that long icon names have \
less chance of overlapping.  If No is selected, icons will not be staggered."}
#ifndef RELEASE
	,
	{"SCREENSAVE", "Screen Save", PT_RPINT,
"This controls whether screen snapshots are saved as IRIS rgb files or as PostScript files.  \
If PostScript is selected they will be saved as embedded PostScript files with a file extension \
of .eps.  If IRIS RGB is selected, they will be saved as IRIS RGB files with a file \
extension of .rgb."},
	{"GARBAGECOLLECT", "Garbage Collection", PT_SFBOOL,
"This controls whether garbage collection is absolutely safe or a little bit \
faster."}
#endif
    };

PrefState prefState[NPREFERENCES];

#define PREFCELLHEIGHT	40		/*Height of a cell in preferences*/
#define PREFNAMEWIDTH	200		/*Width of the name of a preference*/
#define PREFFIELDWIDTH	400		/*Width of a field of a preference*/

Bool PrefExists(whichPref)
int whichPref;
/*Returns true iff whichPref exists*/
{
    return prefState[whichPref] . exists;
}

char *GetPrefString(whichPref)
int whichPref;
/*Gets the string for preference whichPref*/
{
    if (prefInfo[whichPref] . type != PT_STRING) return 0;
    return prefState[whichPref] . value . string;
}

Bool GetPrefTruth(whichPref)
int whichPref;
/*Gets the truth for preference whichPref*/
{
    if (prefInfo[whichPref] . type != PT_YNBOOL &&
	prefInfo[whichPref] . type != PT_AMBOOL &&
	prefInfo[whichPref] . type != PT_SFBOOL)
    {
	return false;
    }
    return prefState[whichPref] . value . truth;
}

long GetPrefInteger(whichPref)
int whichPref;
/*Gets the integer for preference whichPref*/
{
    if (prefInfo[whichPref] . type != PT_AFFINT &&
	prefInfo[whichPref] . type != PT_RPINT)
    {
	return false;
    }
    return prefState[whichPref] . value . integer;
}

static void MakePrefFromString(whichPref, string)
int whichPref;
char *string;
/*Makes a preference whichPref from string*/
{
    prefState[whichPref] . exists = true;
    switch(prefInfo[whichPref] . type)
    {
	case PT_STRING:
	    prefState[whichPref] . value . string = malloc(strlen(string) + 1);
	    strcpy(prefState[whichPref] . value . string, string);
	    break;
	case PT_YNBOOL:
	    if (strcmp2(string, "yes") == 0 || strcmp2(string, "true") == 0)
	    {
		prefState[whichPref] . value . truth = true;
	    }
	    else
	    {
		prefState[whichPref] . value . truth = false;
	    }
	    break;
	case PT_AMBOOL:
	    if (strcmp2(string, "auto") == 0 || strcmp2(string, "automatic") == 0)
	    {
		prefState[whichPref] . value . truth = true;
	    }
	    else
	    {
		prefState[whichPref] . value . truth = false;
	    }
	    break;
	case PT_SFBOOL:
	    if (strcmp2(string, "fast") == 0 || strcmp2(string, "reckless") == 0)
	    {
		prefState[whichPref] . value . truth = true;
	    }
	    else
	    {
		prefState[whichPref] . value . truth = false;
	    }
	    break;
	case PT_AFFINT:
	    if (strcmp2(string, "auto") == 0 || strcmp2(string, "automatic") == 0)
	    {
		prefState[whichPref] . value . integer = 2;
	    }
	    else if (strcmp2(string, "fast") == 0 || strcmp2(string, "faster") == 0)
	    {
		prefState[whichPref] . value . integer = 1;
	    }
	    else
	    {
		prefState[whichPref] . value . integer = 0;
	    }
	    break;
	case PT_RPINT:
	    if (strcmp2(string, "post") == 0 || strcmp2(string, "postscript") == 0)
	    {
		prefState[whichPref] . value . integer = 1;
	    }
	    else
	    {
		prefState[whichPref] . value . integer = 0;
	    }
	    break;
    }
}

static ObjPtr ChangePrefString(object)
ObjPtr object;
/*Changed value for a text box object that controls a string preference*/
{
    ObjPtr var, value;
    int whichPref;

    var = GetIntVar("ChangePrefString", object, WHICHPREF);
    if (!var)
    {
	return ObjFalse;
    }
    whichPref = GetInt(var);

    value = GetValue(object);
    if (!value || !IsString(value))
    {
	return ObjFalse;
    }

    if (prefState[whichPref] . value . string)
    {
	free(prefState[whichPref] . value . string);
    }
    prefState[whichPref] . value . string = malloc(strlen(GetString(value)) + 1);
    strcpy(prefState[whichPref] . value . string, GetString(value));
    prefState[whichPref] . changed = true;

    return ObjTrue;
}

static ObjPtr ChangePrefBoolean(object)
ObjPtr object;
/*Changed value for a text box object that controls a boolean preference*/
{
    ObjPtr var, value;
    int whichPref;

    var = GetIntVar("ChangePrefBoolean", object, WHICHPREF);
    if (!var)
    {
	return ObjFalse;
    }
    whichPref = GetInt(var);

    value = GetValue(object);
    if (!value || !IsInt(value))
    {
	return ObjFalse;
    }

    prefState[whichPref] . value . truth = GetInt(value);
    prefState[whichPref] . changed = true;

    return ObjTrue;
}

static ObjPtr ChangePrefInteger(object)
ObjPtr object;
/*Changed value for a text box object that controls an integer preference*/
{
    ObjPtr var, value;
    int whichPref;

    var = GetIntVar("ChangePrefInteger", object, WHICHPREF);
    if (!var)
    {
	return ObjFalse;
    }
    whichPref = GetInt(var);

    value = GetValue(object);
    if (!value || !IsInt(value))
    {
	return ObjFalse;
    }

    prefState[whichPref] . value . integer = GetInt(value);
    prefState[whichPref] . changed = true;
    return ObjTrue;
}

void DoShowPreferences()
/*Shows the preferences dialog*/
{
    WinInfoPtr dialogExists, newDialog;
    ObjPtr whichDialog;

    if (logging)
    {
	Log("show preferences\n");
	InhibitLogging(true);
    }
    
    whichDialog = NewString("Preferences");

    dialogExists = DialogExists(0, whichDialog);
    newDialog = GetDialog(0, whichDialog, "Preferences",
	PREFNAMEWIDTH + PREFFIELDWIDTH + 3 * MAJORBORDER,
	PREFCELLHEIGHT * NPREFERENCES + MAJORBORDER * 2,
	PREFNAMEWIDTH + PREFFIELDWIDTH + 3 * MAJORBORDER,
	PREFCELLHEIGHT * NPREFERENCES + MAJORBORDER * 2,
	WINDBUF + WINFIXEDSIZE);

    if (!dialogExists)
    {
	/*Fill the dialog with stuff*/
	ObjPtr contents, panel, radio, button, textBox;
	int left, right, bottom, top;
	int k;

	/*Set the help string*/
	SetVar((ObjPtr) newDialog, HELPSTRING,
	    NewString("This window shows your personal preferences for using \
SciAn.  The preferences are saved in a file named \".scianPrefs\" in your home \
directory when you exit SciAn normally.  \
Use Help In Context and click on the various controls to find out what they do."));

	contents = GetListVar("DoShowPreferences", (ObjPtr) newDialog, CONTENTS);
	if (!contents) return;
	panel = NewPanel(greyPanelClass, 
	    0, PREFNAMEWIDTH + PREFFIELDWIDTH + 3 * MAJORBORDER,
	    0, PREFCELLHEIGHT * NPREFERENCES + MAJORBORDER * 2
	    );
	if (!panel)
	{
	    return;
	}
	PrefixList(contents, panel);
	contents = GetListVar("DoShowPreferences", panel, CONTENTS);
	if (!contents) return;
	SetVar(panel, PARENT, (ObjPtr) newDialog);
	ContentsExpectWindowSize(newDialog, 
	    PREFNAMEWIDTH + PREFFIELDWIDTH + 3 * MAJORBORDER,
	    PREFCELLHEIGHT * NPREFERENCES + MAJORBORDER * 2);

	for (k = 0; k < NPREFERENCES; ++k)
	{
	    char objName[200], longName[300];
	    int median;

	    /*Calculate a median line*/
	    median = MAJORBORDER + PREFCELLHEIGHT / 2 + PREFCELLHEIGHT * (NPREFERENCES - 1 - k);

	    /*Add a text box for this preference*/
	    sprintf(objName, "%s name", prefInfo[k] . shortName);
	    if (prefInfo[k] . type == PT_YNBOOL)
	    {
		sprintf(longName, "%s?", prefInfo[k] . longName);
	    }
	    else
	    {
		sprintf(longName, "%s:", prefInfo[k] . longName);
	    }
	    textBox = NewTextBox(MAJORBORDER, MAJORBORDER + PREFNAMEWIDTH,
				 median - TEXTBOXHEIGHT / 2, median + TEXTBOXHEIGHT / 2,
				 0, objName, longName);
	    PrefixList(contents, textBox);
	    SetVar(textBox, PARENT, panel);
	    SetVar(textBox, HELPSTRING, NewString(prefInfo[k] . helpString));
	    SetTextFont(textBox, "Helvetica-Bold");

	    /*Add the field based on the type of preference*/
	    switch(prefInfo[k] . type)
	    {
		case PT_STRING:
		    sprintf(objName, "%s field", prefInfo[k] . shortName);
		    textBox = NewTextBox(
			MAJORBORDER * 2 + PREFNAMEWIDTH,
			MAJORBORDER * 2 + PREFNAMEWIDTH + PREFFIELDWIDTH,
			median - PITBOXHEIGHT / 2, median + PITBOXHEIGHT / 2,
			EDITABLE + WITH_PIT + ONE_LINE, objName,
			prefState[k] . value . string);
		    PrefixList(contents, textBox);
		    SetVar(textBox, HELPSTRING, NewString(prefInfo[k] . helpString));
	 	    SetVar(textBox, PARENT, panel);
		    SetVar(textBox, WHICHPREF, NewInt(k));
		    SetMethod(textBox, CHANGEDVALUE, ChangePrefString);
		    break;
		case PT_YNBOOL:
		    sprintf(objName, "%s radio", prefInfo[k] . shortName);
		    radio = NewRadioButtonGroup(objName);
		    PrefixList(contents, radio);
		    SetVar(radio, PARENT, panel);
		    SetVar(radio, HELPSTRING, NewString(prefInfo[k] . helpString));

		    button = NewRadioButton(
			MAJORBORDER * 2 + PREFNAMEWIDTH,
			MAJORBORDER * 2 + PREFNAMEWIDTH + PREFFIELDWIDTH / 3,
			median - CHECKBOXHEIGHT / 2, median + CHECKBOXHEIGHT / 2,
			"No");
		    AddRadioButton(radio, button);

		    button = NewRadioButton(
			MAJORBORDER * 2 + PREFNAMEWIDTH + PREFFIELDWIDTH / 3,
			MAJORBORDER * 2 + PREFNAMEWIDTH + 2 * PREFFIELDWIDTH / 3,
			median - CHECKBOXHEIGHT / 2, median + CHECKBOXHEIGHT / 2,
			"Yes");
		    AddRadioButton(radio, button);
		    SetValue(radio, NewInt(prefState[k] . value . truth));
		    SetVar(radio, WHICHPREF, NewInt(k));
		    SetMethod(radio, CHANGEDVALUE, ChangePrefBoolean);
		    break;
		case PT_AMBOOL:
		    sprintf(objName, "%s radio", prefInfo[k] . shortName);
		    radio = NewRadioButtonGroup(objName);
		    PrefixList(contents, radio);
		    SetVar(radio, PARENT, panel);
		    SetVar(radio, HELPSTRING, NewString(prefInfo[k] . helpString));

		    button = NewRadioButton(
			MAJORBORDER * 2 + PREFNAMEWIDTH,
			MAJORBORDER * 2 + PREFNAMEWIDTH + PREFFIELDWIDTH / 3,
			median - CHECKBOXHEIGHT / 2, median + CHECKBOXHEIGHT / 2,
			"Manual");
		    AddRadioButton(radio, button);

		    button = NewRadioButton(
			MAJORBORDER * 2 + PREFNAMEWIDTH + PREFFIELDWIDTH / 3,
			MAJORBORDER * 2 + PREFNAMEWIDTH + 2 * PREFFIELDWIDTH / 3,
			median - CHECKBOXHEIGHT / 2, median + CHECKBOXHEIGHT / 2,
			"Automatic");
		    AddRadioButton(radio, button);
		    SetValue(radio, NewInt(prefState[k] . value . truth));
		    SetVar(radio, WHICHPREF, NewInt(k));
		    SetMethod(radio, CHANGEDVALUE, ChangePrefBoolean);
		    break;
		case PT_SFBOOL:
		    sprintf(objName, "%s radio", prefInfo[k] . shortName);
		    radio = NewRadioButtonGroup(objName);
		    PrefixList(contents, radio);
		    SetVar(radio, PARENT, panel);
		    SetVar(radio, HELPSTRING, NewString(prefInfo[k] . helpString));

		    button = NewRadioButton(
			MAJORBORDER * 2 + PREFNAMEWIDTH,
			MAJORBORDER * 2 + PREFNAMEWIDTH + PREFFIELDWIDTH / 3,
			median - CHECKBOXHEIGHT / 2, median + CHECKBOXHEIGHT / 2,
			"Safer");
		    AddRadioButton(radio, button);

		    button = NewRadioButton(
			MAJORBORDER * 2 + PREFNAMEWIDTH + PREFFIELDWIDTH / 3,
			MAJORBORDER * 2 + PREFNAMEWIDTH + 2 * PREFFIELDWIDTH / 3,
			median - CHECKBOXHEIGHT / 2, median + CHECKBOXHEIGHT / 2,
			"Faster");
		    AddRadioButton(radio, button);
		    SetValue(radio, NewInt(prefState[k] . value . truth));
		    SetVar(radio, WHICHPREF, NewInt(k));
		    SetMethod(radio, CHANGEDVALUE, ChangePrefBoolean);
		    break;
		case PT_AFFINT:
		    sprintf(objName, "%s radio", prefInfo[k] . shortName);
		    radio = NewRadioButtonGroup(objName);
		    PrefixList(contents, radio);
		    SetVar(radio, PARENT, panel);
		    SetVar(radio, HELPSTRING, NewString(prefInfo[k] . helpString));

		    button = NewRadioButton(
			MAJORBORDER * 2 + PREFNAMEWIDTH,
			MAJORBORDER * 2 + PREFNAMEWIDTH + PREFFIELDWIDTH / 3,
			median - CHECKBOXHEIGHT / 2, median + CHECKBOXHEIGHT / 2,
			"Full Quality");
		    AddRadioButton(radio, button);

		    button = NewRadioButton(
			MAJORBORDER * 2 + PREFNAMEWIDTH + PREFFIELDWIDTH / 3,
			MAJORBORDER * 2 + PREFNAMEWIDTH + 2 * PREFFIELDWIDTH / 3,
			median - CHECKBOXHEIGHT / 2, median + CHECKBOXHEIGHT / 2,
			"Faster");
		    AddRadioButton(radio, button);

		    button = NewRadioButton(
			MAJORBORDER * 2 + PREFNAMEWIDTH + 2 * PREFFIELDWIDTH / 3,
			MAJORBORDER * 2 + PREFNAMEWIDTH + PREFFIELDWIDTH,
			median - CHECKBOXHEIGHT / 2, median + CHECKBOXHEIGHT / 2,
			"Automatic");
		    AddRadioButton(radio, button);

		    SetValue(radio, NewInt(prefState[k] . value . integer));
		    SetVar(radio, WHICHPREF, NewInt(k));
		    SetMethod(radio, CHANGEDVALUE, ChangePrefInteger);
		    break;
		case PT_RPINT:
		    sprintf(objName, "%s radio", prefInfo[k] . shortName);
		    radio = NewRadioButtonGroup(objName);
		    PrefixList(contents, radio);
		    SetVar(radio, PARENT, panel);
		    SetVar(radio, HELPSTRING, NewString(prefInfo[k] . helpString));

		    button = NewRadioButton(
			MAJORBORDER * 2 + PREFNAMEWIDTH,
			MAJORBORDER * 2 + PREFNAMEWIDTH + PREFFIELDWIDTH / 3,
			median - CHECKBOXHEIGHT / 2, median + CHECKBOXHEIGHT / 2,
			"IRIS RGB");
		    AddRadioButton(radio, button);

		    button = NewRadioButton(
			MAJORBORDER * 2 + PREFNAMEWIDTH + PREFFIELDWIDTH / 3,
			MAJORBORDER * 2 + PREFNAMEWIDTH + 2 * PREFFIELDWIDTH / 3,
			median - CHECKBOXHEIGHT / 2, median + CHECKBOXHEIGHT / 2,
			"PostScript");
		    AddRadioButton(radio, button);

		    SetValue(radio, NewInt(prefState[k] . value . integer));
		    SetVar(radio, WHICHPREF, NewInt(k));
		    SetMethod(radio, CHANGEDVALUE, ChangePrefInteger);
		    break;
	    }
	}
    }

    if (logging)
    {
	InhibitLogging(false);
    }
}

void InitPreferences()
/*Initialize the preferences*/
{
    int k;
    FILE *prefFile;

    /*Avoid a core dump if I forget to handle all preferences*/
    for (k = 0; k < NPREFERENCES; ++k)
    {
	prefState[k] . value . string = 0;
	prefState[k] . changed = false;
    }

    /*Set up default values for all preferences*/
    prefState[PREF_DEFDIR] . value . string = ".";
    prefState[PREF_ROTINERTIA] . value . truth = true;
    prefState[PREF_NEWWINPLACE] . value . truth = true;
    prefState[PREF_DRAWMOVING] . value . integer = DM_FULL;

    /*Try to read all preferences from environment variables*/
    for (k = 0; k < NPREFERENCES; ++k)
    {
	char *envString;
	prefState[k] . exists = false;

	/*See if it can be read from an environment variable*/
	strcpy(tempStr, "SCIAN_");
	strcat(tempStr, prefInfo[k] . shortName);
	envString = getenv(tempStr);
	if (envString)
	{
	    MakePrefFromString(k, envString);
	}
    }

    /*If a preferences file exists, open it and read it*/
    sprintf(tempStr, "%s/%s", getenv("HOME"), ".scianPrefs");
    prefFile = fopen(tempStr, "r");
    if (prefFile)
    {
	char *s1, *s2, *s3;
	while (fgets(tempStr, TEMPSTRSIZE, prefFile))
	{
	    s1 = tempStr;
	    while (*s1 && *s1 == ' ') ++s1;
	    s2 = s1;
	    while (*s2 && *s2 != ' ') ++s2;
	    *s2++ = 0;
	    while (*s2 && *s2 == ' ') ++s2;
	    s3 = s2;
	    while (*s3 && *s3 != '\n') ++s3;
	    *s3 = 0;

	    for (k = 0; k < NPREFERENCES; ++k)
	    {
		if (0 == strcmp2(s1, prefInfo[k] . shortName))
		{
		    MakePrefFromString(k, s2);
		    break;
		}
	    }
	}
	fclose(prefFile);
    }
}

void KillPreferences()
/*Kills the preferences*/
{
    int k;
    Bool savePreferences;

    savePreferences = false;
    /*Determine if preferences should be saved*/
    for (k = 0; k < NPREFERENCES; ++k)
    {
	if (prefState[k] . changed)
	{
	    savePreferences = true;
	}
    }

    if (savePreferences)
    {
	/*Save the preferences*/
	FILE *prefFile;
	sprintf(tempStr, "%s/%s", getenv("HOME"), ".scianPrefs");
	prefFile = fopen(tempStr, "w");
	if (prefFile)
	{
	    for (k = 0; k < NPREFERENCES; ++k)
	    {
		fprintf(prefFile, "%s ", prefInfo[k] . shortName);
		switch (prefInfo[k] . type)
		{
		    case PT_STRING:
			fprintf(prefFile, "%s\n", prefState[k] . value . string);
			break;
		    case PT_YNBOOL:
			fprintf(prefFile, "%s\n", prefState[k] . value . truth ? "yes" : "no");
			break;
		    case PT_AMBOOL:
			fprintf(prefFile, "%s\n", prefState[k] . value . truth ? "auto" : "manual");
			break;
		    case PT_SFBOOL:
			fprintf(prefFile, "%s\n", prefState[k] . value . truth ? "fast" : "safe");
			break;
		    case PT_AFFINT:
			fprintf(prefFile, "%s\n", prefState[k] . value . integer ? (prefState[k] . value . integer == 2 ? "auto" : "faster") : "full");
			break;
		    case PT_RPINT:
			fprintf(prefFile, "%s\n", prefState[k] . value . integer ? "PostScript" : "RGB");
			break;
		}
	    }
	    fclose(prefFile);
	}
    }

    /*Get rid of the preferences*/
    for (k = 0; k < NPREFERENCES; ++k)
    {
	if (prefInfo[k] . type == PT_STRING && prefState[k] . value . string)
	{
	    free(prefState[k] . value . string);
	}
    }
}
