/********************************************************************************************
	Clean OS Windows library module version 1.2.1.
	This module is part of the Clean Object I/O library, version 1.2.1,
	for the Windows platform.
********************************************************************************************/

/********************************************************************************************
	About this module:
	Implementation of cross call procedure table.
	In this table a linked list of cross call entries is kept.
	A cross call entry is a pair of a cross call code (CcRq...) number (see intrface_121.h)
	and a pointer to a cross call procedure.
	Routines related to printer handling.
********************************************************************************************/
#include "cCrossCallProcedureTable_121.h"
#include <stdlib.h>

/*
 * A CrossCallEntry contains a CcRq number and a pointer to a
 * CrossCallProcedure.
 */
struct _crosscallentry
{
    int					cce_code;	/* CcRq... number */
	CrossCallProcedure	cce_proc;   /* The procedure to be called in case of
                                       cce_code */
	CrossCallEntry		cce_next;	/* The next entry in the list */
};

/*
 * A CrossCallProcedureTable contains a linked list of CrossCallEntries.
 */
struct _crosscallproceduretable
{	int                 ccpt_size;		// nr of entries
	CrossCallEntry		ccpt_first;		// first entry
	CrossCallEntry		ccpt_last;		// last entry
};


/* NewCrossCallEntry creates a CrossCallEntry with cce_next field NULL. */
CrossCallEntry NewCrossCallEntry (int cce_code, CrossCallProcedure cce_proc)
{
	CrossCallEntry cce;
    /* printf("NewCrossCallEntry\n"); */
    cce = rmalloc (sizeof (struct _crosscallentry));

	cce->cce_code = cce_code;
	cce->cce_proc = cce_proc;
	cce->cce_next = NULL;

	return cce;
}

/* FreeCrossCallEntry frees a CrossCallEntry. */
void FreeCrossCallEntry (CrossCallEntry cce)
{
    /* printf("FreeCrossCallEntry\n"); */
	rfree (cce);
}

/* EmptyCrossCallProcedureTable creates an empty table. */
CrossCallProcedureTable EmptyCrossCallProcedureTable (void)
{
	CrossCallProcedureTable ccpt;
    /* printf("EmptyCrossCallProcedureTable\n"); */
    ccpt = rmalloc (sizeof (struct _crosscallproceduretable));

	ccpt->ccpt_size  = 0;
	ccpt->ccpt_first = NULL;
	ccpt->ccpt_last  = NULL;

	return ccpt;
}

/*
 * GetCrossCallProcedureTableSize returns the current number of installed
 * cross call procedures.
 */
int GetCrossCallProcedureTableSize (CrossCallProcedureTable ccpt)
{
    /* printf("GetCrossCallProcedureTableSize\n"); */
	return ccpt->ccpt_size;
}

/* FreeCrossCallProcedureTable frees a CrossCallProcedureTable. */
void FreeCrossCallProcedureTable (CrossCallProcedureTable ccpt)
{
    /* printf("FreeCrossCallProcedureTable\n"); */
	rfree (ccpt);
}

/*	SearchCrossCallEntry (nr,entry) returns the first CrossCallEntry
	following/including entry that either:
		matches nr, or
		is the entry after which a new entry with nr should be added, or
		NULL in case nr should be placed before entry.
*/
static CrossCallEntry SearchCrossCallEntry (int nr,CrossCallEntry entry)
{
    /* printf("SearchCrossCallEntry\n"); */
	if (nr == entry->cce_code)
		return entry;				// entry found
	if (nr < entry->cce_code)
		return NULL;				// no entry found
	if (entry->cce_next == NULL)
		return entry;				// last entry; should insert new entry after this one
	if (nr < entry->cce_next->cce_code)
		return entry;				// next entry exceeds nr; should insert new entry after this one
	return SearchCrossCallEntry (nr,entry->cce_next);
}

/*
 * AddCrossCallEntry (table,nr,proc) adds a new entry (nr,proc) if an entry
 * with nr is not already present.
 */
void AddCrossCallEntry (CrossCallProcedureTable ccpt, int cce_code,
                CrossCallProcedure cce_proc)
{
	CrossCallEntry entry;
    /* printf("AddCrossCallEntry\n"); */
    entry = NewCrossCallEntry (cce_code,cce_proc);

	if (ccpt->ccpt_size == 0)
	{	/* table is empty; create entry and add it */
		ccpt->ccpt_size  = 1;
		ccpt->ccpt_first = entry;
		ccpt->ccpt_last  = entry;
	}
	else if (cce_code < ccpt->ccpt_first->cce_code)
	{	/* entry should be inserted before first entry */
		ccpt->ccpt_size += 1;
		entry->cce_next = ccpt->ccpt_first;
		ccpt->ccpt_first= entry;
	}
	else if (cce_code > ccpt->ccpt_first->cce_code)
	{	/* entry could be in table; look for it and add it if not present */
		CrossCallEntry searchCCE;
		searchCCE = SearchCrossCallEntry (cce_code,ccpt->ccpt_first);

		if (searchCCE == NULL)
		{
			/* printf("\'AddCrossCallEntry\' SearchCrossCallEntry returned NULL CrossCallEntry"); */
			exit(1);
		}
		if (searchCCE->cce_code != cce_code)
		{	/* entry not in table but should be linked after searchCCE */
			gboolean appendLast = (ccpt->ccpt_last == searchCCE);
			ccpt->ccpt_size     += 1;
			entry->cce_next     = searchCCE->cce_next;
			searchCCE->cce_next = entry;

			if (appendLast)
				ccpt->ccpt_last  = entry;	// adjust last if entry is appended at end
		}
	}
}

/* AddCrossCallEntries (table,entries) adds the entries to table */
void AddCrossCallEntries (CrossCallProcedureTable theTable, CrossCallProcedureTable entries)
{
	CrossCallEntry cce;
    /* printf("AddCrossCallEntries\n"); */
    cce = entries->ccpt_first;

	while (cce != NULL)
	{
		AddCrossCallEntry (theTable, cce->cce_code, cce->cce_proc);
		cce = cce->cce_next;
	}
}

/*
 * FindCrossCallEntry returns the found CrossCallProcedure or NULL if not found.
 */
CrossCallProcedure FindCrossCallEntry (CrossCallProcedureTable ccpt, int cce_code)
{
    /* printf("FindCrossCallEntry\n"); */
	if (ccpt->ccpt_size == 0)
    {   /* table is empty, return NULL */
		return NULL;
    }
	else
	{
		CrossCallEntry searchCCE;
		searchCCE = SearchCrossCallEntry (cce_code,ccpt->ccpt_first);
		if (searchCCE && searchCCE->cce_code == cce_code)
			return searchCCE->cce_proc;
		else
			return NULL;
	}
}