/*John Murray*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianLists.h"
#include "ScianIDs.h"
#include "ScianGarbageMan.h"
#include "ScianWindows.h"
#include "ScianDialogs.h"
#include "ScianNetObjects.h"

#define MARKED 1
#define CLEARED 0

#define ISDELETED 69234269

#define IsDeleted(thing) ((thing) -> garbageFlag == ISDELETED)

int TrashDayFlag = 0;
ObjPtr globalObjectList = NULLOBJ;
GlobalReferenceListPtr globalReferenceList = (GlobalReferenceListPtr) 0;
ObjPtr deletedList = NULLOBJ;

long int freedObjects = 0;

void InitMem()
{
#if MALLOPT
/* HAK optimizing adjustments for the new malloc */
    if (mallopt(M_MXFAST, 128))
    {
	struct mallinfo mi;

	fprintf(stderr, "Uh-oh! malloc'ed something before mallopt call!\n");
	
	mi = mallinfo();
	fprintf(stderr,"arena=%d, ordblks=%d, smblks=%d, hblkhd=%d, hblks=%d\n",
			mi.arena, mi.ordblks, mi.smblks, mi.hblkhd, mi.hblks);
	fprintf(stderr,"usmblks=%d, fsmblks=%d, uordblks=%d, fordblks=%d, keepcost=%d\n",
		mi.usmblks, mi.fsmblks, mi.uordblks, mi.fordblks, mi.keepcost);
    }
#endif
}

void KillMem()
{
}

void AddToGlobalList(obj)
ObjPtr obj;
{
    obj -> gNext = globalObjectList;
    globalObjectList = obj;
}

/* took rNext out of Obj since only a tiny percentage of objects use it. */

#if 0
void AddToReferenceList(obj)
ObjPtr obj;
{
    if (obj->refCount > 0)
    {
	++(obj -> refCount);
    }
    else if (obj->refCount == 0)
    {
	obj -> rNext = globalReferenceList;
	globalReferenceList = obj;
	++(obj -> refCount);
    }
    else
    {
	ReportError("AddToReferenceList", "found negative refCount!");
    }
}

#else
void AddToReferenceList(obj)
ObjPtr obj;
{
    GlobalReferenceListPtr temp;

    if (!obj)
	return;

    temp = malloc(sizeof(struct GlobalReferenceListStruct));
    temp -> obj = obj;
    temp -> next = globalReferenceList;
    globalReferenceList = temp;
}
#endif

#if 0
int RemoveFromReferenceList(obj)
ObjPtr obj;
{
    ObjPtr *runner;

    if (!obj)
	return true;

    if (!globalReferenceList)
	return false;

    --(obj -> refCount);

    if (obj -> refCount < 0)
	ReportError("RemoveFromReferenceList", "found negative refCount!");

    if (obj->refCount > 0)
    {
	return true;
    }

    runner = &globalReferenceList;
    while (((*runner) -> rNext) && *runner != obj )
    {
	runner = &((*runner) -> rNext);
    }

    if (*runner == obj)
    {
	/* really removing the thing from the reference list this time! */
	*runner = (*runner) -> rNext;
	++TrashDayFlag;
	return true;
    }
    return false;
}
#else
int RemoveFromReferenceList(obj)
ObjPtr obj;
{
    GlobalReferenceListPtr *runner, temp;

    if (!obj)
	return true;

    if (!globalReferenceList)
    {
	ReportError("RemoveFromReferenceList", "object not in global list");
	return false;
    }

    runner = &globalReferenceList;
    while (((*runner) -> next) && (*runner) -> obj != obj)
    {
	runner = &((*runner) -> next);
    }

    if ((*runner) -> obj == obj)
    {
	temp = *runner;
	*runner = (*runner) -> next;	/* remove ref list node. */
	free (temp);
	++TrashDayFlag;		/* may have been last reference to obj */
	return true;
    }
    else
    {
	ReportError("RemoveFromReferenceList", "object not in global list");
        return false;
    }
}
#endif

void MarkVarTree(nodep)
VarsPtr		nodep;		/* pointer to node to delete */
/* MarkVarTree:	frees space allocated to a var node and all its kids */
{
    if (!nodep) return;
    MarkVarTree(nodep->left);
    MarkVarTree(nodep->right);
    MarkObject(nodep->value);
}

ObjPtr MarkList(list)
ObjPtr list;
/*(trial) Disposes of the contents of list*/
{
    ThingListPtr runner;

    runner = LISTOF(list);

    while (runner)
    {
	MarkObject(runner -> thing);
	runner = runner -> next;
    }
    return ObjTrue;
}

/* Garbage collection routine that marks an object and all of
 * its children, if it can find them...
 */
int MarkObject(thingp)
ObjPtr thingp;
{
    FuncTyp marker;

    if (!thingp)
	return;
    if (thingp == (ObjPtr) NETSTUBFLAG)
    {
	/* this special case ought to be handled in MarkObjectArray */
	return;
    }

    if (IsDeleted(thingp))
    {
	sprintf(tempStr, "Hey! tried to mark something already deleted! 0x%lx", thingp);
	ReportError("MarkObject", tempStr);
    }
    else if (thingp->garbageFlag != MARKED && thingp->garbageFlag != CLEARED)
    {
	sprintf(tempStr, "Invalid GarbageMan flag! 0x%lx.\n", thingp);
	ReportError("MarkObject", tempStr);
    }

    if (thingp->garbageFlag == MARKED)
    {
	return; /* hit something already marked */
    }

    thingp->garbageFlag = MARKED;
    Mark(thingp);
    MarkVarTree(thingp->vars);
    MarkObject(thingp->class);
}

int CountReferenceList()
{
    int counter = 0;
    GlobalReferenceListPtr runner;

    runner = globalReferenceList;
    while (runner)
    {
	++counter;
	runner = runner -> next;
    }
    return counter;
}

int CountObjectList()
{
    int counter = 0;
    ObjPtr runner;

    runner = globalObjectList;
    while (runner)
    {
	++counter;
	runner = runner -> gNext;
    }
    return counter;
}

void TrashDay()
{
    ObjPtr *prunner, temp;
    GlobalReferenceListPtr runner;
    FuncTyp marker;
#ifdef GCTIMES
    struct tms foo, newfoo, newerfoo;
#endif
#if DISABLETRASHDAY
    return;
#endif
#ifdef GCTIMES
    times(&foo);
#endif
    TrashDayFlag = 0;

    runner = globalReferenceList;
    while (runner)
    {
	MarkObject((runner) -> obj);
	runner = runner -> next;
    }

#ifdef GCTIMES
    times(&newfoo);
    fprintf(stderr, "marked all things, utime=%ld, stime=%ld\n",
	    newfoo.tms_utime - foo.tms_utime, newfoo.tms_stime - foo.tms_stime);
#endif
    /*all referenced things marked, traverse all things, and dooda rite t'ing*/
    prunner = &globalObjectList;

    /* figuring out what happens here is left as an exercise for the reader */
    while (*prunner)
    {
	if ( (*prunner) -> garbageFlag == MARKED)
	{
	    (*prunner) -> garbageFlag = CLEARED;
	    prunner = & ((*prunner) -> gNext);
	}
	else if ( (*prunner) -> garbageFlag == CLEARED)
	{
	    if (!IsPublished(*prunner))
	    {
	    temp = *prunner;
	    *prunner = (*prunner) -> gNext;
	    DeleteWithPrejudice(temp);
	    }
	}
	else
	{
	    /* What to do, what to do? */
	    sprintf(tempStr, "Illegal garbageFlag value! 0x%lx\n", *prunner);
	    ReportError("TrashDay", tempStr);
	    prunner = & ((*prunner) -> gNext);
	}
    }
#ifdef GCTIMES
    times(&newerfoo);
    fprintf(stderr, "done with cleanup, utime=%ld, stime=%ld\n",
	    newerfoo.tms_utime - newfoo.tms_utime, newerfoo.tms_stime - newfoo.tms_stime);
    fprintf(stderr, "total time: utime = %ld, stime = %ld, tot=%ld\n",
	    newerfoo.tms_utime - foo.tms_utime, newerfoo.tms_stime - foo.tms_stime,
	    (newerfoo.tms_utime - foo.tms_utime) + (newerfoo.tms_stime - foo.tms_stime));
#endif
}

void DeleteWithPrejudiceVarNode(nodep)
VarsPtr		nodep;		/* pointer to node to delete */
/* DeleteVarNode:	frees space allocated to a var node and all its kids */
{
    if (!nodep) return;
    DeleteWithPrejudiceVarNode(nodep->left);
    DeleteWithPrejudiceVarNode(nodep->right);
    free(nodep);
}

void DisposeListWithPersonalDislike(list)
ObjPtr list;
{
    while (LISTOF(list))
    {
	ThingListPtr next;
	next = LISTOF(list) -> next;
	free(LISTOF(list));
	LISTOF(list) = next;
    }
    LISTOF(list) = 0;
    list -> garbageFlag = ISDELETED;
    free(list);
    DecGlobalThingCount();
}

void DeleteWithPrejudice(obj)
ObjPtr obj;
{
    FuncTyp cleaner;
    ObjPtr ret;

    if (IsPublished(obj))
    {
	fprintf(stderr, "HEY! tried to delete published object!\n");
	return;
    }

#if 0
    if (IsPublished(obj))
    {
#ifdef DEBUG
	fprintf(stderr, "removing published object 0x%x\n", obj);
#endif
	UnPublishObject(obj);
	return;
    }
#endif

    if (IsRemote(obj))
    {
	/* fprintf(stderr, "removing remote object 0x%x\n", obj); */
    }

    cleaner = GetMethod(obj, CLEANUP);
    if (cleaner)
    {
	ret = (* cleaner) (obj, NIL);
	if (!IsTrue(ret))
	{
	    sprintf(tempStr,"CLEANUP on object 0x%x reported an error!\n", obj);
	    ReportError("DeleteWithPrejudice", tempStr);
	    sprintf(tempStr, "obj->flags == 0x%x\n", obj->flags);
	    ReportError("DeleteWithPrejudice", tempStr);
	}
    }

    if (IsList(obj))
    {
	DeleteWithPrejudiceVarNode(obj->vars);
	DeleteMethodNode(obj->methods);
	DisposeListWithPersonalDislike(obj);
    }
    else
    {
	DeleteWithPrejudiceVarNode(obj->vars);
	DeleteMethodNode(obj->methods);
	obj -> garbageFlag = ISDELETED;
	free (obj);
	DecGlobalThingCount();
    }
    ++freedObjects;
}

