/*ScianErrors.c
  Eric Pepke
  March 26, 1990
  Error routines for scian
*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianNames.h"
#include "ScianIDs.h"
#include "ScianWindows.h"
#include "ScianErrors.h"
#include "ScianDialogs.h"
#include "ScianTimers.h"
#include "ScianArrays.h"
#include "ScianColors.h"
#include "ScianStyle.h"
#include "ScianScripts.h"

extern ObjPtr timedObjClass;		/*Timed variable, just in case*/

char *IDName(id)
NameTyp id;
/*Returns the internal string of id, or a code giving its number*/
{
    static char emergencyName[40];
    char *retVal;

    retVal = GetInternalString(id);
    if (retVal)
    {
	return retVal;
    }
    else
    {
	sprintf(emergencyName, "ID %d", id);
	return emergencyName;
    }
}

void OMErr()
/*Out of memory error*/
{
    ReportError("SciAn", "Out of memory!");
}

#ifdef PROTO
void VarError(char *whichFunction, int whichError, ObjPtr object, NameTyp var)
#else
void VarError(whichFunction, whichError, object, var)
char *whichFunction;
int whichError;
ObjPtr object;
NameTyp var;
#endif
/*Announces error whichError determined in function whichFunction.
  Additional information may follow whichError*/
{
    ObjPtr name;

    switch(whichError)
    {
	case VARNOTFOUND:
	    name = GetVar(object, NAME);
	    if (name && IsString(name))
	    {
		sprintf(tempStr, "Variable %s was not found in object %s",
			IDName(var), GetString(name));
	    }
	    else
	    {
		sprintf(tempStr, "Variable %s was not found", IDName(var));
	    }
	    break;
	case METHODNOTFOUND:
	    name = GetVar(object, NAME);
	    if (name && IsString(name))
	    {
		sprintf(tempStr, "Method %s was not found in object %s",
			IDName(var), GetString(name));
	    }
	    else
	    {
		sprintf(tempStr, "Method %s was not found in object %lx", IDName(var), object);
	    }
	    break;
	case VARNOTOBJECT:
	    name = GetVar(object, NAME);
	    if (name && IsString(name))
	    {
		sprintf(tempStr, "Variable %s in object %s is not an object",
			IDName(var), GetString(name));
	    }
	    else
	    {
		sprintf(tempStr, "Variable %s is not an object", IDName(var));
	    }
	    break;
	case VARNOTARRAY:
	    name = GetVar(object, NAME);
	    if (name && IsString(name))
	    {
		sprintf(tempStr, "Variable %s in object %s is not an array",
			IDName(var), GetString(name));
	    }
	    else
	    {
		sprintf(tempStr, "Variable %s is not an array", IDName(var));
	    }
	    break;
	case VARNOTPICTURE:
	    name = GetVar(object, NAME);
	    if (name && IsString(name))
	    {
		sprintf(tempStr, "Variable %s in object %s is not a picture",
			IDName(var), GetString(name));
	    }
	    else
	    {
		sprintf(tempStr, "Variable %s is not a picture", IDName(var));
	    }
	    break;
	case VARNOTWINDOW:
	    name = GetVar(object, NAME);
	    if (name && IsString(name))
	    {
		sprintf(tempStr, "Variable %s in object %s is not a window",
			IDName(var), GetString(name));
	    }
	    else
	    {
		sprintf(tempStr, "Variable %s is not a window", IDName(var));
	    }
	    break;
	case VARNOTINT:
	    name = GetVar(object, NAME);
	    if (name && IsString(name))
	    {
		sprintf(tempStr, "Variable %s in object %s is not an integer",
			IDName(var), GetString(name));
	    }
	    else
	    {
		sprintf(tempStr, "Variable %s is not an integer", IDName(var));
	    }
	    break;
	case VARNOTSYMBOL:
	    name = GetVar(object, NAME);
	    if (name && IsString(name))
	    {
		sprintf(tempStr, "Variable %s in object %s is not a symbol",
			IDName(var), GetString(name));
	    }
	    else
	    {
		sprintf(tempStr, "Variable %s is not a symbol", IDName(var));
	    }
	    break;
	case VARNOTSTRING:
	    name = GetVar(object, NAME);
	    if (name && IsString(name))
	    {
		sprintf(tempStr, "Variable %s in object %s is not a string",
			IDName(var), GetString(name));
	    }
	    else
	    {
		sprintf(tempStr, "Variable %s is not a string", IDName(var));
	    }
	    break;
	case VARNOTREAL:
	    name = GetVar(object, NAME);
	    if (name && IsString(name))
	    {
		sprintf(tempStr, "Variable %s in object %s is not a real number",
			IDName(var), GetString(name));
	    }
	    else
	    {
		sprintf(tempStr, "Variable %s is not a real number", IDName(var));
	    }
	    break;
	case VARNOTPALETTE:
	    name = GetVar(object, NAME);
	    if (name && IsString(name))
	    {
		sprintf(tempStr, "Variable %s in object %s is not a palette",
			IDName(var), GetString(name));
	    }
	    else
	    {
		sprintf(tempStr, "Variable %s is not a palette", IDName(var));
	    }
	    break;
	case VARNOTLIST:
	    name = GetVar(object, NAME);
	    if (name && IsString(name))
	    {
		sprintf(tempStr, "Variable %s in object %s is not a list",
			IDName(var), GetString(name));
	    }
	    else
	    {
		sprintf(tempStr, "Variable %s is not a list", IDName(var));
	    }
	    break;
	case VARBADRANK:
	    name = GetVar(object, NAME);
	    if (name && IsString(name))
	    {
		sprintf(tempStr, "The rank of variable %s in object %s is wrong",
			IDName(var), GetString(name));
	    }
	    else
	    {
		sprintf(tempStr, "The rank of variable %s is wrong", IDName(var));
	    }
	    break;
	case VARBADDIM:
	    name = GetVar(object, NAME);
	    if (name && IsString(name))
	    {
		sprintf(tempStr, "The dimensions of variable %s in object %s are wrong",
			IDName(var), GetString(name));
	    }
	    else
	    {
		sprintf(tempStr, "The dimensions of variable %s are wrong", IDName(var));
	    }
	    break;
	default:
	    sprintf(tempStr, "Unrecognized error %d", whichError);
	    break;
    }
    ReportError(whichFunction, tempStr);
}

void Error(whichFunction, whichError, extraInfo)
char *whichFunction;
int whichError;
char *extraInfo;
/*Announces error whichError determined in function whichFunction.
  Additional information may follow whichError*/
{
    switch(whichError)
    {
	case CREATEFILEERROR:
	    /*Error creating a file*/
	    sprintf(tempStr, "Failed to create or open file %s for writing",
		    extraInfo);
	    break;
	case OPENFILEERROR:
	    /*Error creating a file*/
	    sprintf(tempStr, "File %s does not exist or could not be opened",
		    extraInfo);
	    break;
	default:
	    sprintf(tempStr, "Unrecognized error %d", whichError);
	    break;
    }
    ReportError(whichFunction, tempStr);
}

ObjPtr GetVarSurely(whichFunction, object, var)
char *whichFunction;
ObjPtr object;
NameTyp var;
/*Gets var from object.  If it is an object, returns it.  If not, prints
  error as if from function whichFunction.  If object is 0, returns 0, so this
  can be used in a dataflow sort of way*/
{
    ObjPtr retVal;
    if (!object)
    {
	return NULLOBJ;
    }
    retVal = GetVar(object, var);
    if (!retVal)
    {
	VarError(whichFunction, VARNOTFOUND, object, var);
	return NULLOBJ;
    }
    return retVal;
}

ObjPtr GetObjectVar(whichFunction, object, var)
char *whichFunction;
ObjPtr object;
NameTyp var;
/*Gets var from object.  If it is an object, returns it.  If not, prints
  error as if from function whichFunction.  If object is 0, returns 0, so this
  can be used in a dataflow sort of way*/
{
    ObjPtr retVal;
    if (!object)
    {
	return NULLOBJ;
    }
    retVal = GetVar(object, var);
    if (!retVal)
    {
	VarError(whichFunction, VARNOTFOUND, object, var);
	return NULLOBJ;
    }
    if (!IsObject(retVal))
    {
	VarError(whichFunction, VARNOTOBJECT, object, var);
	return NULLOBJ;
    }
    return retVal;
}

ObjPtr GetListVar(whichFunction, object, var)
char *whichFunction;
ObjPtr object;
NameTyp var;
/*Gets var from object.  If it is a list, returns it.  If not, prints
  error as if from function whichFunction.  If object is 0, returns 0, so this
  can be used in a dataflow sort of way*/
{
    ObjPtr retVal;
    if (!object)
    {
	return NULLOBJ;
    }
    retVal = GetVar(object, var);
    if (!retVal)
    {
	VarError(whichFunction, VARNOTFOUND, object, var);
	return NULLOBJ;
    }
    if (!IsList(retVal))
    {
	VarError(whichFunction, VARNOTLIST, object, var);
	return NULLOBJ;
    }
    return retVal;
}

ObjPtr GetPictureVar(whichFunction, object, var)
char *whichFunction;
ObjPtr object;
NameTyp var;
/*Gets var from object.  If it is a picture, returns it.  If not, prints
  error as if from function whichFunction.  If object is 0, returns 0, so this
  can be used in a dataflow sort of way*/
{
    ObjPtr retVal;
    if (!object)
    {
	return NULLOBJ;
    }
    retVal = GetVar(object, var);
    if (!retVal)
    {
	VarError(whichFunction, VARNOTFOUND, object, var);
	return NULLOBJ;
    }
    if (!IsPicture(retVal))
    {
	VarError(whichFunction, VARNOTPICTURE, object, var);
	return NULLOBJ;
    }
    return retVal;
}

ObjPtr GetWindowVar(whichFunction, object, var)
char *whichFunction;
ObjPtr object;
NameTyp var;
/*Gets var from object.  If it is a window, returns it.  If not, prints
  error as if from function whichFunction.  If object is 0, returns 0, so this
  can be used in a dataflow sort of way*/
{
    ObjPtr retVal;
    if (!object)
    {
	return NULLOBJ;
    }
    retVal = GetVar(object, var);
    if (!retVal)
    {
	VarError(whichFunction, VARNOTFOUND, object, var);
	return NULLOBJ;
    }
    if (!IsWindow(retVal))
    {
	VarError(whichFunction, VARNOTWINDOW, object, var);
	return NULLOBJ;
    }
    return retVal;
}


ObjPtr GetArrayVar(whichFunction, object, var)
char *whichFunction;
ObjPtr object;
NameTyp var;
/*Gets var from object.  If it is an array, returns it.  If not, prints
  error as if from function whichFunction.  If object is 0, returns 0, so this
  can be used in a dataflow sort of way*/
{
    ObjPtr retVal;

    if (!object)
    {
	return NULLOBJ;
    }
    retVal = GetVar(object, var);
    if (!retVal)
    {
	VarError(whichFunction, VARNOTFOUND, object, var);
	return NULLOBJ;
    }
    if (!IsRealArray(retVal))
    {
	VarError(whichFunction, VARNOTARRAY, object, var);
	return NULLOBJ;
    }
    return retVal;
}

FuncTyp GetMethodSurely(whichFunction, object, method)
char *whichFunction;
ObjPtr object;
NameTyp method;
/*Gets method from object.  If it is there, returns it.  If not, prints
  error as if from function whichFunction.  If object is 0, returns 0, so this
  can be used in a dataflow sort of way

  And don't call me surely.
*/
{
    FuncTyp retVal;

    if (!object)
    {
	return 0;
    }
    retVal = GetMethod(object, method);
    if (!retVal)
    {
	VarError(whichFunction, METHODNOTFOUND, object, method);
	return 0;
    }
    return retVal;
}

ObjPtr GetIntVar(whichFunction, object, var)
char *whichFunction;
ObjPtr object;
NameTyp var;
/*Gets var from object.  If it is an int, returns it.  If not, prints
  error as if from function whichFunction.  If object is 0, returns 0, so this
  can be used in a dataflow sort of way*/
{
    ObjPtr retVal;

    if (!object)
    {
	return NULLOBJ;
    }
    retVal = GetVar(object, var);
    if (!retVal)
    {
	VarError(whichFunction, VARNOTFOUND, object, var);
	return NULLOBJ;
    }
    if (!IsInt(retVal))
    {
	VarError(whichFunction, VARNOTINT, object, var);
	return NULLOBJ;
    }
    return retVal;
}

ObjPtr GetSymbolVar(whichFunction, object, var)
char *whichFunction;
ObjPtr object;
NameTyp var;
/*Gets var from object.  If it is a symbol, returns it.  If not, prints
  error as if from function whichFunction.  If object is 0, returns 0, so this
  can be used in a dataflow sort of way*/
{
    ObjPtr retVal;

    if (!object)
    {
	return NULLOBJ;
    }
    retVal = GetVar(object, var);
    if (!retVal)
    {
	VarError(whichFunction, VARNOTFOUND, object, var);
	return NULLOBJ;
    }
    if (!IsSymbol(retVal))
    {
	VarError(whichFunction, VARNOTSYMBOL, object, var);
	return NULLOBJ;
    }
    return retVal;
}

ObjPtr GetStringVar(whichFunction, object, var)
char *whichFunction;
ObjPtr object;
NameTyp var;
/*Gets var from object.  If it is a string, returns it.  If not, prints
  error as if from function whichFunction.  If object is 0, returns 0, so this
  can be used in a dataflow sort of way*/
{
    ObjPtr retVal;

    if (!object)
    {
	return NULLOBJ;
    }
    retVal = GetVar(object, var);
    if (!retVal)
    {
	VarError(whichFunction, VARNOTFOUND, object, var);
	return NULLOBJ;
    }
    if (!IsString(retVal))
    {
	VarError(whichFunction, VARNOTSTRING, object, var);
	return NULLOBJ;
    }
    return retVal;
}

ObjPtr GetRealVar(whichFunction, object, var)
char *whichFunction;
ObjPtr object;
NameTyp var;
/*Gets var from object.  If it is a real, returns it.  If not, prints
  error as if from function whichFunction.  If object is 0, returns 0, so this
  can be used in a dataflow sort of way*/
{
    ObjPtr retVal;

    if (!object)
    {
	return NULLOBJ;
    }
    retVal = GetVar(object, var);
    if (!retVal)
    {
	VarError(whichFunction, VARNOTFOUND, object, var);
	return NULLOBJ;
    }
    if (!IsReal(retVal))
    {
	VarError(whichFunction, VARNOTREAL, object, var);
	return NULLOBJ;
    }
    return retVal;
}

ObjPtr GetPaletteVar(whichFunction, object, var)
char *whichFunction;
ObjPtr object;
NameTyp var;
/*Gets var from object.  If it is a palette, returns it.  If not, prints
  error as if from function whichFunction.  If object is 0, returns 0, so this
  can be used in a dataflow sort of way*/
{
    ObjPtr retVal;

    if (!object)
    {
	return NULLOBJ;
    }
    retVal = GetVar(object, var);
    if (!retVal)
    {
	VarError(whichFunction, VARNOTFOUND, object, var);
	return NULLOBJ;
    }
    if (!IsPalette(retVal))
    {
	VarError(whichFunction, VARNOTPALETTE, object, var);
	return NULLOBJ;
    }
    return retVal;
}

#ifdef PROTO
ObjPtr GetFixedArrayVar(char *whichFunction, ObjPtr object, 
	NameTyp var, int rank, ...) 
#else
ObjPtr GetFixedArrayVar(whichFunction, object, var, rank)
char *whichFunction;
ObjPtr object;
NameTyp var;
int rank;
#endif
/*Gets var from object.  If it is an array and conforms to rank and
  the following dimensions, returns it.  If not, prints error as if from 
  function whichFunction.  If object is 0, returns 0, so this
  can be used in a dataflow sort of way*/
{
    ObjPtr retVal;
    long *dims;
    int k;

    dims = (long *)(&rank + 1);

    if (!object)
    {
	return NULLOBJ;
    }
    retVal = GetVar(object, var);
    if (!retVal)
    {
	VarError(whichFunction, VARNOTFOUND, object, var);
	return NULLOBJ;
    }
    if (!IsRealArray(retVal))
    {
	VarError(whichFunction, VARNOTARRAY, object, var);
	return NULLOBJ;
    }
    if (rank != RANK(retVal))
    {
	VarError(whichFunction, VARBADRANK, object, var);
	return NULLOBJ;
    }
    for (k = 0; k < rank; ++k)
    {
	if (*dims != DIMS(retVal)[k])
	{
	    VarError(whichFunction, VARBADDIM, object, var);
	    return NULLOBJ;
	}
	++dims;
    }
    return retVal;
}

Bool GetPredicate(object, var)
ObjPtr object;
NameTyp var;
/*Checks predicate var in object*/
{
    ObjPtr value;
    value = GetVar(object, var);
    if (value && IsInt(value) && GetInt(value))
    {
	return true;
    }
    else
    {
	return false;
    }
}

Bool IntVarEql(object, var, value)
ObjPtr object;
NameTyp var;
int value;
/*Returns true iff object has an integer variable var which has value value,
  false otherwise*/
{
    ObjPtr varval;
    if (!object)
    {
	return false;
    }
    varval = GetVar(object, var);
    if (varval && IsInt(varval))
    {
	return GetInt(varval) == value ? true : false;
    }
    else
    {
	return false;
    }
}

/*
  A warning is a kind of alert which only exists to give information to
  the user.  The user can read the alert and dismiss it; that's all.

  Unlike general alerts, warning alerts are not saved in scripts.
  When a log is being made, no user interface operations to the alert window
  are saved to the log.  When a script is being read, warnings do not appear
  in alert boxes but are rather sent to stderr.
*/

int warningsToDo[MAXNWARNINGS];
int curWarning = 0;

static void EmitWarnings()
/*Emits all the warnings in the stack*/
{
    int k;
    int color;
    char *message;

    color = UICAUTIONALERT;
    message = "An unknown error occurred.";

    for (k = 0; k < curWarning; ++k)
    {
	switch (warningsToDo[k])
	{
	    case CW_NUMBERERROR:
		color = UIERRORALERT;
		message = "There is a syntax error in the number you have just entered.  \
Please enter the number once more.";
		break;
	    case CW_NONINTERROR:
		color = UIERRORALERT;
		message = "You have entered a real number.  This number must be an integer.  \
Please enter the number once more.";
		break;
	    case CW_INFINITYERROR:
		color = UIERRORALERT;
		message = "The number entered may not be infinite.  \
Please enter the number once more.";
		break;
	    case CW_MISSINGERROR:
		color = UIERRORALERT;
		message = "The number entered may not be missing.  \
Please enter the number once more.";
		break;
	    case CW_NOTPALETTEERROR:
		color = UIERRORALERT;
		message =  "Only palettes can be dropped in this icon corral.";
		break;
	    case CW_FILEFORMATERROR:
		color = UIERRORALERT;
		message = "The file you have selected does not have a format \
recognized by SciAn.  If you know the format of the file, set it with Set Format \
and try again.";
		break;
	    case CW_READDIRERROR:
		color = UIERRORALERT;
		message = "Entire directories cannot be read as files into SciAn.  \
Please go into the directory, select the files you want to open, and try again.";
		break;
	    case CW_NOTOGETHERMODIFY:
		color = UIERRORALERT;
		message = "There is no modifier which can modify the selected datasets together.";
		break;
	    case CW_NOSEPARATEMODIFY:
		color = UIERRORALERT;
		message = "There is no modifier which can modify the selected dataset.";
		break;
	    case CW_CANNOTVISFILE:
		color = UIERRORALERT;
		message = "Files cannot be visualized directly.  Please open the file and \
then visualize the resulting datasets.";
		break;
	    case CW_CANNOTVISERROR:
		color = UIERRORALERT;
		message = "Some of the selected objects could not be visualized directly.  \
Try Visualize As.";
		break;
	    case CW_LOOKATERROR:
		color = UIERRORALERT;
		message = "The lookat point must be different from the position of \
the observer.";
		break;
	    case CW_NOGEOMETRYERROR:
		color = UIERRORALERT;
		message = "A geometric dataset is not appropriate here.  Use a numeric \
field instead.";
		break;
	    case CW_CANNOTDROPINMAIN:
		color = UIERRORALERT;
		message = "Once a visualization object has been generated, its \
main dataset cannot be changed.  Create a new visualization object of the same \
type with the new dataset.";
		break;
	    case CW_SCRSAVEFAILED:
		color = UIERRORALERT;
		message = "The attempt to save the screen failed.  This is probably \
because of insufficient privileges in the directory or a full disk.";
		break;
	    case CW_OPENERROR:
		color = UIERRORALERT;
		message = "Unable to access file.";
		break;
	    case CW_CHDIRERROR:
		color = UIERRORALERT;
		message = "Unable to access directory.";
		break;
	    case CW_PATHTOOLONGERROR:
		color = UIERRORALERT;
		message = "Could not open the folder because the path name is too long.";
		break;
	}
    }

    if (runningScript)
    {
	fprintf(stderr, "The following error occured while reading the script\n(It is probably safe to ignore):\n");
	fprintf(stderr, "%s\n", message);
    }
    else
    {
	WinInfoPtr errWindow;
	errWindow = AlertUser(UIERRORALERT, (WinInfoPtr) 0, message, 0, 0, "");
	SetVar((ObjPtr) errWindow, INHIBITLOGGING, ObjTrue);
    }
    curWarning = 0;
}

#ifdef PROTO
void WarnUser(int warning)
#else
void WarnUser(warning)
int warning;
#endif
/*Warns a user with warning*/
{
    if (curWarning >= MAXNWARNINGS) return;
    warningsToDo[curWarning++] = warning;
    DoUniqueTask(EmitWarnings);
}
