/*ScianSnap.c
  Eric Pepke
  14 January 1993

  Routines for variable snapshots in SciAn
*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianIDs.h"
#include "ScianLists.h"
#include "ScianErrors.h"
#include "ScianSnap.h"
#include "ScianWindows.h"
#include "ScianObjWindows.h"
#include "ScianControls.h"
#include "ScianSymbols.h"
#include "ScianGarbageMan.h"
#include "ScianMethods.h"
#include "ScianDatabase.h"
#include "ScianDepend.h"
#include "ScianTextFiles.h"
#include "ScianScripts.h"

ObjPtr snapshotClass;			/*Class for all snapshots*/

#ifdef PROTO
void AddSnapVar(ObjPtr object, NameTyp var)
#else
void AddSnapVar(object, var)
ObjPtr object;
NameTyp var;
#endif
/*Adds a snap var to object*/
{
    ObjPtr snapVars;

    snapVars = Get1Var(object, SNAPVARS);

    if (!snapVars)
    {
	snapVars = NewList();
    }

    PostfixList(snapVars, NewSymbol(var));
    SetVar(object, SNAPVARS, snapVars);
}

#ifdef PROTO
ObjPtr AssembleSnapVars(ObjPtr object)
#else
ObjPtr AssembleSnapVars(object)
ObjPtr object;
#endif
/*Assembles some snap vars from object, superclass first*/
{
    if (object)
    {
        ObjPtr retVal;
	ObjPtr curSnapVars;
	ThingListPtr runner;

	retVal = AssembleSnapVars(ClassOf(object));
	curSnapVars = Get1Var(object, SNAPVARS);
	if (curSnapVars && IsList(curSnapVars))
	{
	    if (!retVal)
	    {
		retVal = NewList();
	    }
	    for (runner = LISTOF(curSnapVars); runner; runner = runner -> next)
	    {
		if (WhichListIndex(retVal, runner -> thing) < 0)
		{
		    PostfixList(retVal, runner -> thing);
		}
	    }
	}
	return retVal;
    }
    else
    {
	return NULLOBJ;
    }
}

#ifdef PROTO
ObjPtr TakeSnapshot(ObjPtr object)
#else
ObjPtr TakeSnapshot(object)
ObjPtr object;
#endif
/*Takes a snapshot of object*/
{
    ObjPtr list;
    ObjPtr retVal;

    /*Make the snap vars*/
    list = AssembleSnapVars(object);

    retVal = NewObject(snapshotClass, 0L);
    SetVar(retVal, REPOBJ, object);
    SetVar(retVal, SNAPVARS, list);
    if (list)
    {
	ThingListPtr runner;

	runner = LISTOF(list);
	while (runner)
	{
	    NameTyp id;

	    id = GetSymbolID(runner -> thing);
	    if (id)
	    {
		MakeVar(object, id);
		SetVar(retVal, id, GetVar(object, id));
	    }
	    runner = runner -> next;
	}
    }
    SetVar(retVal, NAME, GetVar(object, NAME));
    SetVar(retVal, REPCLASSID, GetVar(object, CLASSID));

    return retVal;
}

#ifdef PROTO
void PullSnapVars(ObjPtr newObject, ObjPtr oldObject)
#else
void TakeSnapshot(newObject, oldObject)
ObjPtr newObject, oldObject;
#endif
/*Transters the snap vars of newObject from corresponding values in oldObject*/
{
    ObjPtr list;
    ObjPtr retVal;

    /*Make the snap vars*/
    list = AssembleSnapVars(newObject);

    if (list)
    {
	ThingListPtr runner;

	runner = LISTOF(list);
	while (runner)
	{
	    NameTyp id;

	    id = GetSymbolID(runner -> thing);
	    if (id)
	    {
		SetVar(newObject, id, GetVar(oldObject, id));
	    }
	    runner = runner -> next;
	}
    }
}

#ifdef PROTO
void ApplySnapshotTo(ObjPtr snapshot, ObjPtr object)
#else
void ApplySnapshotTp(snapshot, object)
ObjPtr snapshot;
ObjPtr object;
#endif
/*Applies snapshot to an object*/
{
    ObjPtr list;

    list = GetVar(snapshot, SNAPVARS);

    if (list && object)
    {
	ThingListPtr runner;
	ObjPtr value;

	runner = LISTOF(list);
	while (runner)
	{
	    NameTyp id;

	    id = GetSymbolID(runner -> thing);
	    if (id)
	    {
		if (id == VALUE)
		{
		    value = GetVar(snapshot, VALUE);
		    SetValue(object, value);
		}
		else
		{
		    SetVar(object, id, GetVar(snapshot, id));
		}
	    }
	    runner = runner -> next;
	}
	ImInvalid(object);
    }
}

#ifdef PROTO
void ApplySnapshot(ObjPtr snapshot)
#else
void ApplySnapshot(snapshot)
ObjPtr snapshot;
#endif
/*Applies snapshot to its object*/
{
    ObjPtr object;

    object = GetVar(snapshot, REPOBJ);
    ApplySnapshotTo(snapshot, object);
}

#ifdef PROTO
void LogSnapshot(ObjPtr object)
#else
void LogSnapshot(object)
ObjPtr object;
#endif
/*Logs a snapshot of object*/
{
    ObjPtr list;
    ObjPtr retVal;
    Bool needsWindow = false;

    needsWindow = MakeObjectName(tempStr, object);

    if (needsWindow)
    {
	Log("begin snapshot ");
	Log(tempStr);
	Log("\n");
    }
    else
    {
	LogNoWindow("begin snapshot ");
	LogNoWindow(tempStr);
	LogNoWindow("\n");
    }

    /*Make the snap vars*/
    list = AssembleSnapVars(object);

    if (list)
    {
	ThingListPtr runner;
	char cmd[256];
	char *s;

	runner = LISTOF(list);
	while (runner)
	{
	    NameTyp id;
	    ObjPtr value;

	    id = GetSymbolID(runner -> thing);
	    if (id)
	    {
		value = GetVar(object, id);
		if (IsInt(value))
		{
		    sprintf(cmd, "    set variable %s (int) ", GetInternalString(id));
		}
		else
		{
		    sprintf(cmd, "    set variable %s ", GetInternalString(id));
		}
		s = &(cmd[0]);
		while (*s) ++s;
		PrintScriptObject(s, value);
		while (*s) ++s;
		*s++ = '\n';
		*s = 0;
		if (needsWindow)
		{
		    Log(cmd);
		}
		else
		{
		    LogNoWindow(cmd);
		}
	    }
	    runner = runner -> next;
	}
    }
    if (needsWindow)
    {
	Log("end snapshot\n");
    }
    else
    {
	LogNoWindow("end snapshot\n");
    }
}

#ifdef PROTO
void SubsumeObjIntoDatabase(ObjPtr object)
#else
void SubsumeObjIntoDatabase(object)
ObjPtr object;
#endif
/*Subsumes an object into the database.  This means---
  1) Looks for an object with the same name and CLASS_ID
  2) If found,
	Replaces the object in the database with this object, but takes a
	snapshot of the old object from the new object's snapvars and 
	applies it to the new object.
  3) If not found, just adds this object to the database.
*/
{
    ObjPtr keyList;
    ObjPtr resultList;

    keyList = NewList();
    PostfixList(keyList, NewSymbol(NAME));
    MakeVar(object, NAME);
    PostfixList(keyList, GetVar(object, NAME));
    PostfixList(keyList, NewSymbol(CLASSID));
    MakeVar(object, CLASSID);
    PostfixList(keyList, GetVar(object, CLASSID));
    if (resultList = SearchDatabase(keyList))
    {
	if (LISTOF(resultList))
	{
	    PullSnapVars(object, LISTOF(resultList) -> thing);
	    DeleteObjFromDatabase(LISTOF(resultList) -> thing);
	}
    }
    AddObjToDatabase(object);
}

#ifdef PROTO
void ApplySavedSettings(ObjPtr object)
#else
void ApplySavedSettings(object)
ObjPtr object;
#endif
/*Applies the saved settings to object*/
{
    ObjPtr directory;

    directory = GetSettingsDirectory();

    ReadObjectControls(object, directory, false);
}

#ifdef PROTO
ObjPtr GetSettingsDirectory(void)
#else
ObjPtr GetSettingsDirectory()
#endif
/*Returns a string of the settings directory, which by default is
  ~/scianSettings.  Creates the directory if need be*/
{
    char dirName[256];
    struct stat buf;
    int notThere;

    sprintf(dirName, "%s/%s", getenv("HOME"), ".scianSettings");
    notThere = stat(dirName, &buf);
    if (notThere)
    {
	FILE *outFile;

	/*Have to create it*/
	if (mkdir(dirName, S_IREAD | S_IWRITE | S_IEXEC))
	{
	    /*Failed to create*/
	    ReportError("GetSettingsDirectory", "Cannot create directory");
	    return NULLOBJ;
	}
	notThere = stat(dirName, &buf);
	if (notThere)
	{
	    /*Still not there, return null*/
	    ReportError("GetSettingsDirectory", "Tried create directory, but it's not there");
	    return NULLOBJ;
	}

	/*Put in a README*/
	strcpy(tempStr, dirName);
	strcat(tempStr, "/README");
	outFile = fopen(tempStr, "w");
	if (!outFile)
	{
	    ReportError("GetSettingsDirectory", "Cannot create files in directory");
	    return NULLOBJ;
	}
	fprintf(outFile, "This directory is used to save settings from within SciAn.\n");
	fclose(outFile);
    }

    /*Directory exists, see if it's OK*/
    if ((buf.st_mode & S_IFMT) == S_IFDIR)
    {
	/*It's OK*/
	return NewString(dirName);
    }
    ReportError("GetSettingsDirectory", "Problem using settings directory");
    return NULLOBJ;
}

ObjPtr SaveSnapshotControls(object)
ObjPtr object;
/*Saves an object's controls from a snapshot*/
{
    ObjPtr directory;

    directory = GetSettingsDirectory();

    SaveObjectControls(object, directory);
    return ObjTrue;
}

ObjPtr LogSnapshotControls(object)
ObjPtr object;
/*Logs all the controls for an object*/
{
    LogSnapshot(object);
    return ObjTrue;
}

#ifdef PROTO
void InitSnapshots(void)
#else
void InitSnapshots()
#endif
/*Initializes snapshot system*/
{
    snapshotClass = NewObject(NULLOBJ, 0L);
    AddToReferenceList(snapshotClass);
    SetVar(snapshotClass, CLASSID, NewInt(CLASS_SNAPSHOT));
    classClasses[CLASS_SNAPSHOT] = snapshotClass;
}

#ifdef PROTO
void KillSnapshots(void)
#else
void KillSnapshots()
#endif
/*Kills the snapshot system*/
{
    RemoveFromReferenceList(snapshotClass);
}

