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

    MODULE
	HL.c

    DESCRIPTION
	``Hierachic Lists''

    NOTES

    BUGS
	none known - never tested

    TODO
	connections: Connect, RemoveLock, Notify

    EXAMPLES

    SEE ALSO

    INDEX

    HISTORY
	22-11-94 b_noll created

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


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

#include "HL.h"
#include "Definitions.h"
#include "xdme_base.h"

#include <stdlib.h>
#include <string.h>


#include <intuition/classes.h>

/**************************************
	    Global Variables
**************************************/

extern int NodeNodeComparison(void *, void *);
extern int NodeStringComparison(void *, const UBYTE *);

/**************************************
      Internal Defines & Structures
**************************************/

#define HL_OK		    0
#define HL_ERROR	    1
#define HL_NOMEM	    2
#define HL_NOLIST	    3
#define HL_DUPTREENODE	    4


struct Locker {
    struct SNode  Node;
    struct Locker*Base;
    struct HNode *Partner;
    ULONG	  Attr;
}; /* struct  */

#define SCAN void *
#define FIND void *

/**************************************
	   Internal Variables
**************************************/

struct HNode *HL_Root  = NULL;

/**************************************
	   Internal Prototypes
**************************************/


/**************************************
		 Macros
**************************************/


/**************************************
	     Implementation
**************************************/

#if 0
#include <proto/exec.h>
static APTR allocmem (int n)
{
    return AllocVec (n, 0);
} /* malloc */

static void freemem (APTR n)
{
    FreeVec (n);
} /* free */
#else
#define allocmem malloc
#define freemem free
#endif


#if 0
struct LockerNode {
    struct SNode   Node;
    struct Locker *Base;
    APTR	   Partner;
    ULONG	   Attr;
}; /* struct LockerNode */

struct Locker {
    struct LockerNode Parts[2];
    ULONG  Flags;
}; /* struct Locker */

static __inline struct LockerNode *PartnerNode(struct LockerNode *n) {
    int is_Header = (n->Base->Parts == n)? 1: 0;
    return n->Base->Parts + is_Header;
} /* PartnerNode */

static void PBL__DropLocks (struct LockerNode *n, struct HNode *o) {
    int is_Header = (n->Base->Parts == n)? 1: 0;

    /* ---- we avoid endless loops by removing the node from our own
    **	    list of locks before notifying the partner, and by checking
    **	    if we have this node before sending ...		     */

    if (SLL_Remove (&o->Locks, &n->Node)) {
	DoDispatch (n->Partner, PB_CONNECT, PBA_Resolve, PartnerNode(n));

	/* ---- Only one of the 2 Partners may free the Locker */
	if (is_Header)
	    free (n->Base);
    } /* if */
} /* PBL__DropLocks */

static void PBL__NotifyLocks (struct LockerNode *n, struct PB_Msg *m) {
    int is_Header = (n->Base->Parts == n)? 1: 0;

    if (m->Attr == n->Attr)
	if  (is_Header)        // (IsSendingPart(n))
	    DoSet (n->Partner, PartnerNode(n)->Attr, m->Value);
} /* PBL_NotifyLocks */


UNVOLLSTAENDIG - BRAUCHT PARTNER INITIALISIERUNG!


/* ---- send a notification */

ULONG HL_Notify (struct GadNode *gadget, ULONG attr, APTR value, struct HNode *base) {
    return GTB_Notify(gadget, attr, value, base);
} /* HL_Notify */


/* ---- remove a certain lock from base */

static void HL_RemoveLock (struct Locker *l, struct HNode *base)
{
    SLL_Remove (&base->Locks, &l->Node);
    HL_Notify  (l->Partner, l->Attr, NULL, NULL);
    DISPOSE(l);
} /* HL_RemoveLock */


/* ---- build a connection between base and its partner */

ULONG HL_Connect (struct HNode *base, struct HNode *partner, ULONG attr)
{
    struct Locker *l, *pl;
    if (attr != N_NAME && attr != N_LIST && attr != N_VALUE)
	return 0;

    if (l = malloc (sizeof (*l))) {
	bzero(l, sizeof (*l));
	SLL_AddHead(&base->Locks, &l->Node);
	l->Partner = partner;
	l->Attr   = attr;

	//if (attr == N_NAME)
	//    HL_Notify(partner, attr, base->Name, base);

	//if (attr == N_VALUE)
	//    HL_Notify(partner, attr, base->Value, base);

	//if (attr == N_LIST)
	//    HL_Notify(partner, attr, &base->Subs, base);

	return 1;
    } /* if */
    return 0;
} /* HL_Connect */


AB HIER OK (HOFFE ICH)

#else
void HL_RemoveLock (struct Locker *l, struct HNode *base);
ULONG  HL_Notify (struct HNode *gadget, ULONG attr, APTR value, struct HNode *base);
#endif


/* ---- Find a Lock between base and its partner */

static struct Locker *HL_FindLock (struct HNode *base, APTR partner)
{
    struct Locker *l;
    if (base)
	for (l = (APTR)base->Locks.Head; l; l = (APTR)l->Node.Succ)     /* 1 */
	    if (l->Partner == partner)
		return l;
    return NULL;
} /* HL_FindLock */


/* ---- remove all connections between base and its partner */

ULONG HL_Disconnect (struct HNode *base, struct HNode *partner)
{
    struct Locker *l;
    if (base && partner)
	while ((l = HL_FindLock(base, partner)))
	    HL_RemoveLock(l, base);
    return HL_OK;
} /* HL_Disconnect */



/* ---- strip all locks from base */

static void HL_RemoveLocks (struct HNode *base)
{
    SLL_Scan(&base->Locks, (SCAN)HL_RemoveLock, base);
} /* HL_RemoveLocks */



/* ---- notify all partners which are listening 2 attr */

static void HL_NotifyLocks (struct HNode *base, ULONG attr, APTR value)
{
    struct Locker *l;
//printf (" StratNotificaton %d %08lx\n", attr, value);
    if (base) {
	base->Flags |= HLF_PRIVATESET;
	for (l = (APTR)base->Locks.Head; l; l = (APTR)l->Node.Succ) {
//printf (" ... %d ", l->Attr);
	    if (l->Attr == attr)
		HL_Notify (l->Partner, attr, value, base);
	}
	base->Flags &= ~HLF_PRIVATESET;
     }
} /* HL_NotifyLocks */








/* ---- get the Value instance data of a HNode */

//static
struct HLAValue *HL_InstValue (struct HNode *base)
{
    ULONG len = sizeof (*base);

    if (!(base->Flags & HLF_VALUE))
	return NULL;

    return (struct HLAValue *)(len + (ULONG)base);
} /* HL_InstValue */


/* ---- get the SubsList instance data of a HNode */

//static
struct HLASubs *HL_InstSubs (struct HNode *base)
{
    ULONG len = sizeof (*base);

    if (!(base->Flags & HLF_SUBCARRIER) || (base->Flags & HLF_TREE))
	return NULL;

    if (base->Flags & HLF_VALUE)
	len += sizeof (struct HLAValue);

    return (struct HLASubs *)(len + (ULONG)base);
} /* HL_InstSubs */


/* ---- get the SubsTree instance data of a HNode */

static struct HLATree *HL_InstTree (struct HNode *base)
{
    ULONG len = sizeof (*base);

    if (!(base->Flags & HLF_TREE))
	return NULL;

    if (base->Flags & HLF_VALUE)
	len += sizeof (struct HLAValue);

    return (struct HLATree *)(len + (ULONG)base);
} /* HL_InstTree */


/* ---- change the parent slot of a HNode */

static void HL_SetParent (struct HNode *n, struct HNode *parent) {
    n->Parent = parent;
} /* HL_SetParent */


/* **** Lock the SubsList of a HNode for Manipulation */

struct DList *HL_LockSubs (struct HNode *base)
{
    struct HLASubs  *s;

    if (!base )
	return NULL;

    if (!base || !(base->Flags & HLF_SUBCARRIER) || (base->Flags & HLF_TREE))
	return NULL;

    if (!base->BlockCnt++)
	if (base->Locks.Head)
	    HL_NotifyLocks (base, HLA_Labels, (void *)~0L);

    s = HL_InstSubs(base);
    //if (!base->BlockCnt++)
    //	  DLL_Scan (&s->Subs, (SCAN)HL_SetParent, NULL);

    return &s->Subs;
} /* HL_LockSubs */


/* **** Unlock a previously locked SubsList  */

ULONG HL_UnlockSubs (struct HNode *base)
{
    struct HLASubs  *s;

    if (!base || !(base->Flags & HLF_SUBCARRIER) || (base->Flags & HLF_TREE))
	return HL_NOLIST;

    s = HL_InstSubs(base);

    if (!--base->BlockCnt)
	DLL_Scan (&s->Subs, (SCAN)HL_SetParent, base);

    if (!base->BlockCnt)
	if (base->Locks.Head)
	    HL_NotifyLocks (base, HLA_Labels, &s->Subs);
} /* HL_UnlockSubs */


/* ---- append a new member 2 base (notifying in Lists) */

ULONG HL_AddMember (struct HNode *base, struct HNode *member, struct HNode *where)
{
    struct HLATree  *t;
    struct HLASubs  *s;

    if (!base || !member )
	return HL_ERROR;

    if (base->Flags & HLF_TREE) {
	t = HL_InstTree(base);
	if (AVL_Find(&t->Subs, NodeStringComparison, member->Name))
	    return HL_DUPTREENODE;

	AVL_Append (&t->Subs, NodeNodeComparison, (struct TreeNode *)member);
	member->Parent = base;
	return HL_OK;
    } /* if */

    if (!(base->Flags & HLF_SUBCARRIER))
	return HL_NOLIST;

    if (!base->BlockCnt++)
	if (base->Locks.Head)
	    HL_NotifyLocks (base, HLA_Labels, (void *)~0L);

    s = HL_InstSubs(base);
    if (!where)
	DLL_AddTail (&s->Subs, &member->Node);
    else
	DLL_AddBehind (&s->Subs, &member->Node, &where->Node);
    member->Parent = base;

    if (!--base->BlockCnt)
	if (base->Locks.Head)
	    HL_NotifyLocks (base, HLA_Labels, &s->Subs);
    return HL_OK;
} /* HL_AddMember */


/* ---- remove a member from base (notifying in lists) */

ULONG HL_RemMember (struct HNode *base, struct HNode *member)
{
    struct HLATree  *t;
    struct HLASubs  *s;

    if (!base || !member )
	return HL_ERROR;

    if (base->Flags & HLF_TREE) {
	t = HL_InstTree(base);
	AVL_Remove (&t->Subs, NodeNodeComparison, (struct TreeNode *)member);
	member->Parent = NULL;
	return HL_OK;
    } /* if */

    if (!(base->Flags & HLF_SUBCARRIER))
	return HL_NOLIST;

    if (!base->BlockCnt++)
	if (base->Locks.Head)
	    HL_NotifyLocks (base, HLA_Labels, (void *)~0L);

    s = HL_InstSubs(base);
    DLL_Remove (&s->Subs, &member->Node);
    member->Parent = NULL;

    if (!--base->BlockCnt)
	if (base->Locks.Head)
	    HL_NotifyLocks (base, HLA_Labels, &s->Subs);
    return HL_OK;
} /* HL_RemMember */


/* **** free all HL data of a node like structure */

Prototype ULONG HL_Uninit (struct HNode *n);
ULONG HL_Uninit (struct HNode *n)
{
    struct HLATree  *t;
    struct HLASubs  *s;
    struct HLAValue *v;

    if (!n)
	return HL_ERROR;

    if (n->Parent) {
	ULONG rv;
	rv = HL_RemMember (n->Parent, n);
	if (rv != HL_OK)
	    return rv;
    } /* if */

    if (n->Locks.Head)
	HL_RemoveLocks (n);

    if (n->Flags & HLF_TREE) {
	t = HL_InstTree(n);
	AVL_ScanTree (&t->Subs, (SCAN)HL_SetParent, AVL_SCAN_INFIX);
	AVL_FreeTree (&t->Subs, (SCAN)HL_Dispose);
    } else if (n->Flags & HLF_SUBCARRIER) {
	s = HL_InstSubs(n);
	DLL_Scan (&s->Subs, (SCAN)HL_Dispose, NULL);
    }

    if (n->Flags & HLF_VALUE) {
	v = HL_InstValue(n);
	if (v->Value)
	    free (v->Value);
    }

    if (n->Name)
	free (n->Name);

    return HL_OK;
} /* HL_Uninit */


/* **** prepare the HL part of a node like structure */

Prototype ULONG HL_Init (struct HNode *n, const UBYTE *name, struct HNode *parent, UWORD flags);
ULONG HL_Init (struct HNode *n, const UBYTE *name, struct HNode *parent, UWORD flags)
{
    struct HLATree  *t;
    struct HLASubs  *s;
    struct HLAValue *v;

    n->Name	    = strdup (name);
    n->Locks.Head   = NULL;
    n->Parent	    = NULL;
    n->Flags	    = flags;

    if (flags & HLF_TREE) {
	t = HL_InstTree(n);
	AVL_Init (&t->Subs);
    } else if (flags & HLF_SUBCARRIER) {
	s = HL_InstSubs(n);
	DLL_Init (&s->Subs);
    }

    if (flags & HLF_VALUE) {
	v = HL_InstValue(n);
	v->Value    = strrep (NULL, "");
    }

    if ( !n->Name || ((flags & HLF_VALUE) && !v->Value) ) {
	HL_Uninit (n);
	return HL_NOMEM;
    } /* if */

    if (parent) {
	ULONG rv;
	if ((rv = HL_AddMember (parent, n, NULL)) != HL_OK) {
	    HL_Uninit (n);
	    return rv;
	} /* if */
    } /* if */

    return HL_OK;
} /* HL_Init */


/* **** dispose a HNode entry (safe) */

Prototype ULONG HL_Dispose (struct HNode *n);
ULONG HL_Dispose (struct HNode *n)
{
    ULONG rv;
    if ((rv = HL_Uninit(n)) == HL_OK)
	freemem (n);
    return rv;
} /* HL_Dispose */


/* **** create a new HNode entry */

struct HNode *HL_New (const UBYTE *name, struct HNode *parent, UWORD flags)
{
    struct HNode *n;
    ULONG	  rv;
    ULONG	  len = sizeof (struct HNode);

    if (flags & HLF_TREE)
	len += sizeof (struct HLATree);
    else if (flags & HLF_SUBCARRIER)
	len += sizeof (struct HLASubs);

    if (flags & HLF_VALUE)
	len += sizeof(struct HLAValue);

    if (n = allocmem (len)) {
	bzero (n, len);
	if ((rv = HL_Init(n, name, parent, flags)) != HL_OK) {
	    freemem (n);
	    return NULL;
	} /* if */
    } /* if */
    return n;
} /* HL_New */


/* **** create a new toplevel list with systemattribute */

struct HNode * HL_SystemList (const UBYTE *name)
{
    if (!HL_Root)
	HL_Root = HL_New("*Root*", NULL, HLF_SUBCARRIER);
    if (!HL_Root)
	return NULL;

    return HL_New (name, HL_Root, HLF_SUBCARRIER|HLF_SYSNODE);
} /* HL_SystemList */


/* **** lock, scan & unlock a list */

void HL_Scan (struct HNode *n, void (*scan)(struct HNode*, void*, int), void *userdata)
{
    struct DList *l;
    if ((l = HL_LockSubs (n))) {
	DLL_Scan     (l, (SCAN)scan, userdata);
	HL_UnlockSubs(n);
    //} else if ((t = HL_InstTree (n)))
    //	  AVL_ScanTree (&t->Subs, (SCAN)scan, AVL_SCAN_POSTFIX, userdata);
    } /* if */
} /* HL_Scan */

struct HNode *HL_Find (struct HNode *n, int (*find)(struct HNode*, void*, int), void *userdata)
{
    struct HLASubs *s;
    struct HLATree *t;

    if ((s = HL_InstSubs (n)))
	return (struct HNode *)DLL_Find (&s->Subs, (FIND)find, userdata);
    else if ((t = HL_InstTree (n)))
	return (struct HNode *)AVL_Find (&t->Subs, (FIND)find, userdata);
    else
	return NULL;
} /* HL_Find */




#ifndef freemem
void _STD_10000_HL_Exitus (void) {
    HL_Dispose(HL_Root);
} /* HL_Exitus */
#endif

/******************************************************************************
*****  END HL.c
******************************************************************************/

#if 0

static ULONG HL__ClassSize (struct HClass *c) {
    ULONG s = 0;
    while (c) {
	s += c->Size;
	c = c->Superclass;
    } /* while */
    return s;
} /* HL__ClassSize */

static ULONG HL__Dispatcher (struct HNode *n, struct HClass *class, ULONG *msg)
{
    switch (*msg) {
    case OM_DISPOSE:
	HL_Uninit(n);
	free (n);
	break;
    case OM_NEW:
	if ((n = malloc(HL__ClassSize((struct HClass *)n)))) {
	    if (HL_Init(n, (UBYTE *)*(msg+1), (struct HNode *)*(msg+2), *(msg+3)) == HL_OK)
		return (ULONG)n;
	    free (n);
	} /* if */
	return NULL;
    } /* switch */
    return 0;
} /* HL__Dispatcher */


ULONG HL__Coerce (struct HNode *n, struct HClass *c, ULONG *msg)
{
    return c->Dispatcher(n, c, msg);
} /* HL__Coerce */

ULONG HL__Method (struct HNode *n, ULONG *msg)
{
    return HL__Coerce(n, n->Class, msg);
} /* HL_Method */

struct HClass HRoot = {
    HL__Dispatcher,
    sizeof (struct HNode) + sizeof (struct HLAValue) + sizeof (struct HLASubs),
    NULL
};

struct HClass HLSubCarrier = {
    HL__Dispatcher,
    sizeof (struct HLASubs),
    &HRoot
};

struct HClass HLValCarrier = {
    HL__Dispatcher,
    sizeof (struct HLAValue),
    &HLSubCarrier
};

struct HNode *HL__New (struct HClass *c, UBYTE *name, struct HNode *parent, WORD flags) {
    ULONG new[] = { OM_NEW, 0,0,0,0 };
    new[1] = (ULONG)name;
    new[2] = (ULONG)parent;
    new[3] = flags;
    return (struct HNode *)HL__Coerce ((struct HNode *)c, c, new);
}

ULONG HL__SetN (struct HNode *n, ...)
{
    ULONG set[] = { OM_SET, NULL };
    set[1] = (ULONG)((&n)+1);
    return HL__Method(n, set);
} /* HL__SetN */

ULONG  HL__SetS (struct HNode *n, ULONG attr, ULONG value)
{
    return HL__SetN (n, attr, value, TAG_DONE);
} /* HL__SetS */

void HL__GetS (struct HNode *n, ULONG attr, ULONG *value)
{
    ULONG get[] = { OM_GET, 0, 0 };
    get[1] = attr;
    get[2] = (ULONG)value;
    HL__Method(n, get);
} /* HL__GetS */

void HL__Dispose (struct HNode *n)
{
    ULONG dispose = OM_DISPOSE;
    HL__Method (n, &dispose);
} /* HL_Dispose */

#endif
