/******************************************************************************

    MODUL
	$Id: VarStack.c 1.2 1994/09/20 11:05:32 b_noll Exp b_noll $

    DESCRIPTION
	Variable Stack for DME/XDME

    NOTES
      * we are using the non-std functions
	 ???	error(template, ...);
	 int   SetTypedVar   (const char *name, const char *value, int type);
	 char *GetTypedVar (const char *name, int *type);
	and the argument array
	 UBYTE *av[];
	they have to be supported by the application

    BUGS
      * we are only saving the lower 8 bits of "type" from GetTypedVar!
	that behaviour if not planned to be changed in the near future.
	(However, if it's urgently needed, uncomment the "Type"-slot in
	"VRST" and multireplace "Node.ln_Type" with "Type")

    TODO
	-/-

    EXAMPLES
	-/-

    SEE ALSO
	vars.c

    INDEX

    HISTORY
	05-12-92 b_noll created
	26-08-93 VSTK_drop added
	24-06-94 renamed VARS to VRST
	05-09-94 added VSTK_Internal
	21-09-94 b_noll added DEFCMD/DEFHELP-support
	22-09-94 b_noll totally rewritten
	01-10-94 b_noll introduced DEFMESSAGE

******************************************************************************/

/***********************************************************
*!
*!  Variable Stacking:
*!
*!  Stacking for all structures, which may be read and
*!  written via GetTypedVar()
*!  (Please notice that stackable are only variables,
*!   not macros, menues or mappings (they are stackable, but
*!   popping or picking might fail))
*!
*!  Users should be aware to pop their entries from the stack
*!  when they are needed not any more
*!  (We have NOT planned to create a garbage-collector :-))
*!
************************************************************/

/**************************************
		Includes
**************************************/

/* #include "defs.h" */
#include <proto/exec.h>
#include "lists.h"
#include <stdio.h>
#include "xdme_base.h"


/**************************************
	    Globale Exports
**************************************/


/**************************************
      Interne Defines & Strukturen
**************************************/

/* some short names */
#define MLIST struct MinList
#define MNODE struct MinNode
#define  NODE struct Node
#define  LIST struct List


typedef struct _VRST {
    NODE    Node;
    /* int     Type; */
    char*   Str;
} VRST;
#define Type Node.ln_Type


/* possible values for VSTK_PickPop - POP or !POP */
#define POP  0

/* module internal error codes */
#define VSTK_OK 	 0
#define VSTK_NOMEM	-1
#define VSTK_NOVAR	-2
#define VSTK_NOENTRY	-3
#define VSTK_NOTSET	-4


/* is it sensible to put _that_ into an own function? */
#define VSTK_FindEntry(n) ((VRST *)FindName((LIST *)&varstack, (n)))

/**************************************
	   Interne Prototypes
**************************************/


/**************************************
	   Impementation
**************************************/

// int VSTK_Internal = 0;
static MLIST varstack =
{ (MNODE*)&varstack.mlh_Tail, NULL, (MNODE*)&varstack.mlh_Head };

/*************************************************
	    Entry Level Functions
*************************************************/


/* delete an entry */

static void VSTK_FreeEntry (VRST *v) {
    if (v) {
	free(v->Str);
	free(v->Node.ln_Name);
	free(v);
    } /* if */
} /* VSTK_FreeEntry */


/* remove and delete an entry */

static int VSTK_DropEntry (VRST *v) {
    /* ---- Remove an entry from the stack, and free its contents and itself */

    if (v) {
	Remove((NODE*)v);
	VSTK_FreeEntry(v);
	return VSTK_OK;
    } /* if */
    return VSTK_NOENTRY;
} /* VSTK_DropEntry */


/* Restore avariable from an entry and eventually delete it */

static int VSTK_PickPopEntry (VRST *v, int mode) {
    /* ---- restore the value of an entry */
    if (v) {
	VSTK_Internal = 1;

	/* **** ATTN - we must change Vars.c for this **** */
	if (!SetTypedVar(v->Node.ln_Name, v->Str, v->Type)) { /* <- EXTERNAL FUNCTION! */
	/* ******************************************************* */
	    VSTK_Internal = 0;
	    return VSTK_NOTSET;
	} /* if */
	VSTK_Internal = 0;
	if (mode == POP)
	    VSTK_DropEntry (v);
	return VSTK_OK;
    } /* if */
    return VSTK_NOENTRY;
} /* VSTK_PickPopEntry */


/*************************************************
	    Name Level Functions
*************************************************/

/* create a new entry */
static
int VSTK_Push (const char *name) {
    VRST *v;
    int type;
    char *val;

    VSTK_Internal = 1;				/* ask for the variable and get the value and type */
    val = GetTypedVar (name, &type);  /* <- EXTERNAL FUNCTION! */
	  /************************/
    VSTK_Internal = 0;

    if (!val)
	return (type == VAR_NEX)? VSTK_NOVAR: VSTK_NOMEM;

    if ((v = malloc (sizeof (VRST)))) {         /* allocate a node */
	v->Node.ln_Name = strdup (name);        /* and space for its name */
	v->Str		= val;
	v->Type 	= type;

	if (v->Node.ln_Name) {                  /* push the node onto the varstack */
	    AddHead((LIST*)&varstack, (NODE*)v);
	    return VSTK_OK;
	} /* if */
	VSTK_FreeEntry(v);
    }
    return VSTK_NOMEM;

} /* VSTK_Push */


/* Restore a variable entry and eventually delete its top entry */
static
int VSTK_PickPop (const char *name, int mode) {
    VRST *v;

    if ((v = VSTK_FindEntry(name)))
	return VSTK_PickPopEntry(v, mode);
    else
	return VSTK_NOENTRY;

} /* VSTK_PickPop */


/* remove the top entry of a certain name */
static
int VSTK_Drop (const char *name) {
    VRST *v;

    if ((v = VSTK_FindEntry(name)))                 /* find the first matching node */
	return VSTK_DropEntry (v);                  /* ... and drop it              */
    else
	return VSTK_NOENTRY;

} /* VSTK_Drop */


/* swap the contents of a variable with that of an entry */
static
int VSTK_Swap (const char *name) {
    VRST *v;

    if ((v = VSTK_FindEntry(name)))
    {						 /* if there is a matching stack-entry */
	int rv;
	if ((rv = VSTK_Push(name)) != VSTK_OK)   /* push the variables current contents onto stack */
	    return rv;
	Remove((NODE*)v);                        /* remove the elder one */
	AddHead((LIST*)&varstack, (NODE*)v);     /* and set it in front of the new node */
	return VSTK_PickPopEntry(v, POP);        /* and then pop it into the variable */

    } else
	return VSTK_NOENTRY;

} /* VSTK_Swap */


/* remove all entries of a certain name */
static
int VSTK_Purge (const char *name) {
    VRST *v;
#if 0
    VARS *w;

    for (v = (VRST *)varstack.mlh_Head; (w = (VRST *)v->Node.ln_Succ) != NULL; v = w) /* for all nodes ... */
	if (strcmp(v->Node.ln_Name, name) == 0)                                       /* if they match ... */
	    VSTK_DropEntry (v);                                                            /* drop them ...     */
#else
    /* ---- this is less code, slower but does not access the Node-slot */
    while ((v = VSTK_FindEntry(name)))              /* find all matching nodes  */
	VSTK_DropEntry (v);                         /* ... and drop them        */
#endif

    return VSTK_OK;
} /* VSTK_Purge */


/*************************************************
	Application Interface
*************************************************/

/* Checks the stack for thing left from Project prj or if prj==NULL cleans the full stack. */



Prototype void VSTK_CloseProject (APTR prj);
void VSTK_CloseProject (APTR prj) {
    VRST *v;
    VRST *w;

#if 1
    char cmp_val[12];
    sprintf (cmp_val, "%08lx", prj);
#endif

    if (!prj) {
	while ((v = GetHead(&varstack)))
	    VSTK_DropEntry (v);
	return;
    } /* if */

    for (v = (VRST *)varstack.mlh_Head; (w = (VRST *)v->Node.ln_Succ) != NULL; v = w) {
	if (v->Type == VAR_SV) {
#if 0
	    // ---- this is the right way, as soon as SPC_KILLSTACK is usablle
	    if ((l = spc_lock (v->Node.ln_Name)))
		if (spc_killstack (l, v->Value, prj))
		    VSTK_DropEntry (v);
#else
	    // ---- but this one is the only currently viable (and its faster)
	    if ((strcmp(v->Node.ln_Name, "ed") == 0) ||
		(strcmp(v->Node.ln_Name, "pos") == 0) ||
		(strcmp(v->Node.ln_Name, "block") == 0))
		if (strncmp (v->Str, cmp_val, 8) == 0)
		    VSTK_DropEntry (v);
#endif
	} /* if */
    } /* for */
} /* VSTK_CloseProject */



/* Display a VarStack error */

static int VSTK_Error (int code) {
    switch (code) {
    case VSTK_OK:
	return RET_SUCC;
    case VSTK_NOVAR:
DEFMESSAGE( _VTSK_var_not_found, "%s:\nNo Variable `%s' found!" )
	error (_VTSK_var_not_found, av[0], av[1]);
	break;
    case VSTK_NOMEM:
	nomemory();
DEFMESSAGE( _VSTK_no_memory, "%s:\nNo Memory!" )
	// error (_VSTK_no_memory, av[0]); // HA! HA! HA!
	break;
    case VSTK_NOENTRY:
DEFMESSAGE( _VSTK_entry_not_found, "%s:\nNo Stackentry `%s' found!" )
	error (_VSTK_entry_not_found, av[0], av[1]);
	break;
    case VSTK_NOTSET:
DEFMESSAGE( _VSTK_set_failed, "%s:\nUnable to set Variable `%s'!" )
	error(_VSTK_set_failed, av[0], av[1]);
	break;
    default:
DEFMESSAGE( _VSTK_internal, "%s:\nInternal Error!" )
	error(_VSTK_internal, av[0]);
    } /* switch */

    return RET_FAIL;
} /* VSTK_Error */


/*************************************************
	    XDME Command Interface
*************************************************/


/*DEFHELP #cmd stack,var PUSHVAR var - push the contents of the variable @{B}var@{UB} onto the variable stack */
DEFUSERCMD("PushVar", 1, CF_VWM|CF_COK|CF_ICO, int, do_pushVAR, (void),)
{
    return VSTK_Error(VSTK_Push(av[1]));
} /* do_pushVAR */

/*DEFHELP #cmd stack,var DROPVAR var - remove the last pushed occurency of the variable @{B}var@{UB} from the variable stack */
DEFUSERCMD("DropVar", 1, CF_VWM|CF_COK|CF_ICO, int, do_dropVAR, (void),)
{
    return VSTK_Error( VSTK_Drop(av[1]));
} /* do_dropVAR */

/*DEFHELP #cmd stack,var PICKVAR var - restore the last pushed contents of the variable @{B}var@{UB} from the variable stack without modifying the variable stack */
DEFUSERCMD("PickVar", 1, CF_VWM|CF_COK|CF_ICO, int, do_pickVAR, (void),)
{
    return VSTK_Error( VSTK_PickPop(av[1], !POP));
} /* do_pickVAR */

/*DEFHELP #cmd stack,var PURGEVAR var - remove all occurencies the variable @{B}var@{UB} from the variable stack */
DEFUSERCMD("PurgeVar", 1, CF_VWM|CF_COK|CF_ICO, int, do_purgeVAR, (void),)
{
    return VSTK_Error( VSTK_Purge(av[1]));
} /* do_purgeVAR */

/*DEFHELP #cmd stack,var POPVAR var - restore the last pushed contents of the variable @{B}var@{UB} from the variable stack and remove it */
DEFUSERCMD("PopVar", 1, CF_VWM|CF_COK|CF_ICO, int, do_popVAR, (void),)
{
    return VSTK_Error( VSTK_PickPop(av[1], POP));
} /* do_popVAR */

/*DEFHELP #cmd stack,var SWAPVAR var - swap the contents of a variable with that of its last pushed entry in the variable stack */
DEFUSERCMD("SwapVar", 1, CF_VWM|CF_COK|CF_ICO, int, do_swapVAR, (void),)
{
    return VSTK_Error( VSTK_Swap(av[1]));
} /* do_swapVAR */


#if 0 /* das wuerde nochmal ca 30 byte sparen */

int do_varstack (void) {
    int rv = !POP;

    switch (av[0][2]) {
    case 'a':
	rv = VSTK_Swap(av[1]);
	break;
    case 'p':
//	  rv = VSTK_PickPop(av[1], POP);
//	  break;
	rv = POP;
    case 'c':
	rv = VSTK_PickPop(av[1], rv);
	break;
    case 'o':
	rv = VSTK_Drop(av[1]);
	break;
    case 'r':
	rv = VSTK_Purge(av[1]);
	break;
    case 's':
	rv = VSTK_Push(av[1]);
	break;
    default:
	return RET_FAIL;
    } /* switch */

    return VSTK_Error(rv);
}
#endif


#ifdef SPC_VAR

generic internal bit varstackmode = %{
    reference = "VSTK_Internal";
%};

#endif
#if 0
    DEFFLAG( 94-11-15, VSTK_Internal, 0 )
#endif

/******************************************************************************
*****  ENDE varstack.c
******************************************************************************/


/*DEFUSERCMD("_showstack", 0, 0, void, vstk_show, (void),)
{
    VRST *v;
    for (v = GetHead(&varstack); v; v = GetSucc(v))
	printf ("%s:%s\n", v->Node.ln_Name, v->Str);
} /* vstk_show */

