/*ScianLists.c
  Eric Pepke
  March 28, 1990
  Routines for lists of things
*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianLists.h"
#include "ScianIDs.h"
#include "ScianDraw.h"
#include "ScianGarbageMan.h"
#include "ScianWindows.h"
#include "ScianObjWindows.h"

ObjPtr listClass = NULLOBJ;

ObjPtr CopyList(list)
ObjPtr list;
/*Returns a copy of list*/
{
    ObjPtr retVal;
    ThingListPtr runner;

    if (!list || !IsList(list))
    {
	return NULLOBJ;
    }
    retVal = NewList();
    runner = LISTOF(list);
    while (runner)
    {
	PostfixList(list, runner -> thing);
	runner = runner -> next;
    }
    return retVal;
}

static ObjPtr FindListObject(list, name)
ObjPtr list;
char *name;
/*Finds an object with name in list.  Returns it or NULLOBJ*/
{
    ObjPtr retVal;
    ThingListPtr runner;
    ObjPtr objName;
    retVal = NULLOBJ;
    /*First check to see if I am the object*/
    objName = GetVar(list, NAME);
    if (objName && IsString(objName) && 0 == strcmp2(GetString(objName), name))
    {
	if (!retVal)
	{
	    retVal = NewList();
	}
	PostfixList(retVal, list);
    }

    /*Now see if one of my contents is the object*/
    runner = LISTOF(list);
    while (runner)
    {
	ObjPtr foundObject;
	foundObject = FindNamedObject(runner -> thing, name);
	if (foundObject)
	{
	    if (!retVal)
	    {
		retVal = NewList();
	    }
	    AppendList(retVal, foundObject);
	}
	runner = runner -> next;
    }
    return retVal;
}

static ObjPtr ForAllListObjects(list, routine)
ObjPtr list;
FuncTyp routine;
/*Does routine on list*/
{
    ThingListPtr runner;

    (*routine)(list);

    /*Now do on contents*/
    runner = LISTOF(list);
    while (runner)
    {
	ForAllObjects(runner -> thing, routine);
	runner = runner -> next;
    }
    return ObjTrue;
}

ObjPtr NewList()
/*Returns a new, empty list*/
{
    LPtr retVal;

    retVal = (LPtr) NewObject(listClass, sizeof(List) - sizeof(Thing));
    if (!retVal)
    {
	return 0;
    }
    SETOBJTYPE(retVal -> thing . flags, LIST);

    retVal -> list = 0;

    return (ObjPtr) retVal;
}

Bool PrefixList(list, thing)
ObjPtr list;
ObjPtr thing;
/*Prefixes thing on to the beginning of list*/
{
    ThingListPtr newNode;
    newNode = new(ThingListElement);
    if (!newNode)
    {
	OMErr();
	return false;
    }

    newNode -> thing = thing;
    newNode -> next = LISTOF(list);
    LISTOF(list) = newNode;
    return true;
}

Bool InsertList(listPtr, object)
ThingListPtr listPtr;
ObjPtr object;
/*Inserts object after listPtr*/
{
    ThingListPtr newNode;
    newNode = new(ThingListElement);
    if (!newNode)
    {
	OMErr();
	return false;
    }

    newNode -> thing = object;
    newNode -> next = listPtr -> next;
    listPtr -> next = newNode;
    return true;
}

Bool AppendList(list, appList)
ObjPtr list;
ObjPtr appList;
/*Postfixes appList on to the end of list*/
{
    ThingListPtr runner;
    runner = LISTOF(appList);
    while (runner)
    {
	PostfixList(list, runner -> thing);
	runner = runner -> next;
    }
    return true;
}

Bool PostfixList(list, thing)
ObjPtr list;
ObjPtr thing;
/*Postfixes thing on to the end of list*/
{
    ThingListPtr newNode;
    ThingListPtr *nnr;

    newNode = new(ThingListElement);
    if (!newNode)
    {
	OMErr();
	return false;
    }

    nnr = &(LISTOF(list));
    while (*nnr)
    {
	nnr = &((*nnr) -> next);
    }

    newNode -> thing = thing;
    newNode -> next = 0;
    *nnr = newNode;
    return true;
}

int ListCount(theList) /* return number of elements in a list */
ObjPtr theList;
{
	int n=1;
	ThingListPtr p;

	if (!IsList(theList) || !(p = LISTOF(theList))) return 0;
	while (p = p -> next) ++n;
	return n;
}

ObjPtr GetListElem(theList, index) /* return element of a list (0=first) */
ObjPtr theList;
int index;
{
	ThingListPtr p;

	if (index < 0 || !IsList(theList)
	|| !(p = LISTOF(theList))) return NIL;
	do { if (--index < 0) return p -> thing; } while (p = p -> next);
	return NIL; /* no more elements */
}

int DeleteFromList(list, thing)
ObjPtr list;
ObjPtr thing;
/*Deletes all occurences of thing from list.  Returns the number of things
  it had to delete.*/
{
    ThingListPtr *runner;
    int retVal;

    retVal = 0;

    runner = &(LISTOF(list));
    while (*runner)
    {
	if ((*runner) -> thing == thing)
	{
	    ThingListPtr next;
	    next = (*runner) -> next;
	    free(*runner);
	    *runner = next;
	    ++retVal;
	}
	else
	{
	    runner = &(*runner) -> next;
	}
    }
    return retVal;
}

void EmptyList(list)
ObjPtr list;
/*Empties list, i.e. deletes all the elements*/
{
    ThingListPtr *runner;

    runner = &(LISTOF(list));
    while (*runner)
    {
	    ThingListPtr next;
	    next = (*runner) -> next;
	    free(*runner);
	    *runner = next;
    }
}

void DisposeList(list)
ObjPtr list;
/*Disposes of the contents of list*/
{
    while (LISTOF(list))
    {
	ThingListPtr next;
	next = LISTOF(list) -> next;
	free(LISTOF(list));
	LISTOF(list) = next;
    }
    LISTOF(list) = 0;
    free(list);
}

#ifdef GRAPHICS
static void DrawRunner(runner)
ThingListPtr runner;
/*Draws a portion of a list starting at runner, back to front*/
{
    if (runner)
    {
    	ObjPtr oneObject;
	DrawRunner(runner -> next);

	oneObject = runner -> thing;
	if (IsObject(oneObject))
	{
	    DrawObject(oneObject);
	}
	else if (IsList(oneObject))
	{
	    DrawList(oneObject);
	}
    }
}
#endif

void DrawList(list)
ObjPtr list;
/*Draws a list of objects list.*/
{
#ifdef GRAPHICS
    ThingListPtr runner;

    runner = LISTOF(list);
    DrawRunner(runner);
#endif
}

ObjPtr PressList(list, x, y, flags)
ObjPtr list;
int x, y;
long flags;
/*Does a press in list starting at x, y.*/
{
#ifdef INTERACTIVE
    ThingListPtr runner;
    ObjPtr current;

    current = GetVar((ObjPtr) selWinInfo, CURRENT);

    if (current)
    {
	/*Give the current object first dibs*/
	runner = LISTOF(list);
	while (runner)
	{
    	    ObjPtr oneObject;
	    ObjPtr test;			/*Test to see if press worked*/

	    oneObject = runner -> thing;
	    if (oneObject == current)
	    {
		test = PressObject(oneObject, x, y, flags);
		if (IsTrue(test))
		{
		    return test;
		}
	    }
	    runner = runner -> next;
	}
    }

    runner = LISTOF(list);
    while (runner)
    {
    	ObjPtr oneObject;
	ObjPtr test;			/*Test to see if press worked*/

	oneObject = runner -> thing;
	test = PressObject(oneObject, x, y, flags);
	if (IsTrue(test))
	{
	    return test;
	}
	runner = runner -> next;
    }
#endif
    return ObjFalse;
}

ObjPtr DropList(list, object, x, y)
ObjPtr list, object;
int x, y;
/*Drops object in list at x, y.*/
{
    ThingListPtr runner;

    runner = LISTOF(list);
    while (runner)
    {
    	ObjPtr oneObject;
	ObjPtr test;			/*Test to see if drop worked*/

	oneObject = runner -> thing;
	if (IsObject(oneObject))
	{
	    test = DropObjects(oneObject, object, x, y);
	    if (IsTrue(test))
	    {
		return test;
	    }
	}
	else if (IsList(oneObject))
	{
	    test = DropList(oneObject, object, x, y);
	    if (IsTrue(test))
	    {
		return test;
	    }
	}
	runner = runner -> next;
    }

    return ObjFalse;
}

#ifdef PROTO
ObjPtr KeyDownList(ObjPtr list, int key, long flags)
#else
ObjPtr KeyDownList(list, key, flags)
ObjPtr list;
int key;
long flags;
#endif
/*Does a key down in list with key and flags*/
{
    ThingListPtr runner;
    runner = LISTOF(list);
    while (runner)
    {
    	ObjPtr oneObject;
	ObjPtr test;			/*Test to see if press worked*/

	oneObject = runner -> thing;
	if (IsObject(oneObject))
	{
	    ObjPtr bounds;		/*The bounds of the object*/

	    test = KeyDownObject(oneObject, key, flags);
	    if (IsTrue(test))
	    {
		return test;
	    }
	}
	else if (IsList(oneObject))
	{
	    test = KeyDownList(oneObject, key, flags);
	    if (IsTrue(test))
	    {
		return test;
	    }
	}
	runner = runner -> next;
    }

    return ObjFalse;
}

void InitLists()
/*Initializes lists*/
{
    listClass = NewList();
    SetMethod(listClass, FINDOBJECT, FindListObject);
    SetMethod(listClass, FORALLOBJECTS, ForAllListObjects);
    SetMethod(listClass, MARK, MarkList);
    SetMethod(listClass, PRESS, PressList);
    AddToReferenceList(listClass);
}

void KillLists()
/*Destroys the lists*/
{
    DeleteThing(listClass);
}


