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

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianNames.h"
#include "ScianLists.h"
#include "ScianIDs.h"
#include "ScianGarbageMan.h"
#include "ScianWindows.h"
#include "ScianObjWindows.h"
#include "ScianDraw.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) && ObjectNameMatches(name, GetString(objName)))
    {
	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;
    retVal -> lastNode = 0;

    return (ObjPtr) retVal;
}

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

    newNode -> thing = thing;
    newNode -> next = LISTOF(list);

    if (!LISTOF(list))
    {
	((LPtr) list) -> lastNode = newNode;
    }

    LISTOF(list) = 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;

    newNode = newp(ThingListElement);
    if (!newNode)
    {
	OMErr();
	return false;
    }
    newNode -> thing = thing;
    newNode -> next = 0;

    if (((LPtr) list) -> lastNode)
    {
	((LPtr) list) -> lastNode -> next = newNode;
	((LPtr) list) -> lastNode = newNode;
    }
    else
    {
	((LPtr) list) -> list = newNode;
	((LPtr) list) -> lastNode = 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 WhichListIndex(list, object)
ObjPtr list, object;
/*Returns index of object in list, or -1*/
{
    int k;
    ThingListPtr runner;

    k = 0;
    runner = LISTOF(list);
    while (runner)
    {
	if (runner -> thing == object) return k;
	++k;
	runner = runner -> next;
    }
    return -1;
}

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;
    ThingListPtr lastNotDeleted;
    int retVal;

    retVal = 0;

    lastNotDeleted = NULL;
    runner = &(LISTOF(list));
    while (*runner)
    {
	if ((*runner) -> thing == thing)
	{
	    ThingListPtr next;
	    next = (*runner) -> next;
	    free(*runner);
	    *runner = next;
	    ++retVal;
	}
	else
	{
	    lastNotDeleted = *runner;
	    runner = &(*runner) -> next;
	}
    }
    ((LPtr) list) -> lastNode = lastNotDeleted;
    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;
    }
    ((LPtr) list) -> lastNode = NULL;
}

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;
	if (oneObject != current)
	{
	    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;
}



#ifdef PROTO
ObjPtr SortListByStringVar(ObjPtr list, NameTyp id, Bool ascending)
#else
ObjPtr SortListByStringVar(list, id, ascending)
ObjPtr list;
NameTyp id;
Bool ascending;
#endif
/*Returns a new list which is sorted by id in ascending or !ascending order.
  Uses a merge sort, always a favorite of mine.  Currently it's not stable,
  but that could be fixed by changing how firstHalf and secondHalf are
  calculated.*/
{
    ObjPtr retVal;

    if (((LPtr) list) -> list == ((LPtr) list) -> lastNode)
    {
	/*0 or 1 elements, already sorted*/
	retVal = NewList();
	if (LISTOF(list))
	{
	    PrefixList(retVal, LISTOF(list) -> thing);
	}
    }
    else
    {
	ObjPtr firstHalf, secondHalf;
	ThingListPtr runner, firstRunner, secondRunner;
	ObjPtr var;
	char *test1, *test2;
	int comparison;

	/*Make first and second halves*/
	firstHalf = NewList();
	secondHalf = NewList();

	runner = LISTOF(list);
	while (runner)
	{
	    PostfixList(firstHalf, runner -> thing);
	    runner = runner -> next;
	    if (runner)
	    {
		PostfixList(secondHalf, runner -> thing);
		runner = runner -> next;
	    }
	}

	/*Sort them*/
	firstHalf = SortListByStringVar(firstHalf, id, ascending);
	secondHalf = SortListByStringVar(secondHalf, id, ascending);

	/*Merge them*/
	retVal = NewList();
	firstRunner = LISTOF(firstHalf);
	secondRunner = LISTOF(secondHalf);

	/*Do the main merge*/
	while(firstRunner && secondRunner)
	{
	    var = GetVar(firstRunner -> thing, id);
	    if (var)
	    {
		test1 = GetString(var);
	    }
	    else
	    {
		test1 = "";
	    }
	    var = GetVar(secondRunner -> thing, id);
	    if (var)
	    {
		test2 = GetString(var);
	    }
	    else
	    {
		test2 = "";
	    }
	    comparison = strcmp2(test1, test2);

	    if (ascending)
	    {
		if (comparison <= 0)
		{
		    PostfixList(retVal, firstRunner -> thing);
		    firstRunner = firstRunner -> next;
		}
		else
		{
		    PostfixList(retVal, secondRunner -> thing);
		    secondRunner = secondRunner -> next;
		}
	    }
	    else
	    {
		if (comparison >= 0)
		{
		    PostfixList(retVal, firstRunner -> thing);
		    firstRunner = firstRunner -> next;
		}
		else
		{
		    PostfixList(retVal, secondRunner -> thing);
		    secondRunner = secondRunner -> next;
		}
	    }
	}

	/*Take care of fiddly bits at the end*/
	while(firstRunner)
	{
	    PostfixList(retVal, firstRunner -> thing);
	    firstRunner = firstRunner -> next;
	}
	while(secondRunner)
	{
	    PostfixList(retVal, secondRunner -> thing);
	    secondRunner = secondRunner -> next;
	}
    }
    return retVal;
}

ObjPtr CleanupList(list)
ObjPtr list;
/*Method to clean up a list*/
{
    EmptyList(list);
    return ObjTrue;
}

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

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


