/***********************************************************************/
/* Open Visualization Data Explorer                                    */
/* (C) Copyright IBM Corp. 1989,1999                                   */
/* ALL RIGHTS RESERVED                                                 */
/* This code licensed under the                                        */
/*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
/***********************************************************************/

#include <dxconfig.h>


#include <time.h>
#include <dx/dx.h>
#include "cache.h"
#include "crc.h"
#include "d.h"
#include "config.h"
#include "graph.h"
#include "log.h"
#include "swap.h"
#include "utils.h"
#include "context.h"
#include "distp.h"

extern gfunc   *_dxd_exCurrentFunc;
EXDictionary _dxd_exCacheDict = (EXDictionary) NULL;
static gvar *EmptyGvar;
void _dxf_ExFlushPathTags();

static char *
int32tohex (char *buf, int32 l)
{
    static char *htab = "0123456789abcdef";

    buf[8] = '\0';
    buf[7] = htab[l & 0xf]; l >>= 4;
    buf[6] = htab[l & 0xf]; l >>= 4;
    buf[5] = htab[l & 0xf]; l >>= 4;
    buf[4] = htab[l & 0xf]; l >>= 4;
    buf[3] = htab[l & 0xf]; l >>= 4;
    buf[2] = htab[l & 0xf]; l >>= 4;
    buf[1] = htab[l & 0xf]; l >>= 4;
    buf[0] = htab[l & 0xf];

    return (buf + 8);
}


EXCRC _dxf_ExGenCacheTag(char *function, int key, int n, EXCRC *in)
{
    EXCRC result;

    /* Build a cache string representing the key */
    result = _dxf_ExCRCString(EX_INITIAL_CRC, function);
    result = _dxf_ExCRCInt   (result,	 key);
    result = _dxf_ExCRCIntV  (result, 	 (int32 *)in, n);

    return (EXTAG(result));
}

/* this is what the libdx init code calls, but it is too early for 
 * the exec to init the cache at this point, so it is just a dummy
 * routine.  later in the exec init process, when the exo object pool
 * is set up, then ExCacheInit can be called.
 */
Error _dxf_initcache (void)
{
    return OK;
}

Error _dxf_ExCacheInit (void)
{
    _dxd_exCacheDict = _dxf_ExDictionaryCreateSorted(2048, FALSE, TRUE);
    EmptyGvar = _dxf_ExCreateGvar(GV_UNRESOLVED);
    /* Never want this gvar to get deleted so add an extra reference */
    ExReference(EmptyGvar);
    return (_dxd_exCacheDict == NULL? ERROR: OK);
}


/*
 * Insert an entry into the cache using a reference to an executive-style
 * object.
 */

int _dxf_ExCacheInsert (gvar *obj)
{
    char	 stringKey[8+3];
    int		 ret;

    if ((obj->reccrc & 0x80000000) == 0)
	_dxf_ExDie("Bad executive cache tag 0x%08x", obj->reccrc);

    stringKey[0] = 'X';
    int32tohex(stringKey+1, obj->reccrc);

    ExDebug("2", "DXAdd %s to cache at %f", stringKey, obj->object.lastref);

    obj->obj = DXSetObjectTag(obj->obj, obj->reccrc);

    ExTimestamp((EXO_Object)obj);

    ret = _dxf_ExDictionaryInsert (_dxd_exCacheDict, stringKey, (EXObj)obj);
    if(ret > 0) {
        /* since we are pointing to shared memory for the data we can 
         * send a size of 0.
         */
        _dxf_ExDistributeMsg(DM_INSERTCACHE, (Pointer)&(obj->reccrc), 0, 
                             TOPEERS);
    }
    return(ret);
}

int _dxf_ExCacheInsertRemoteTag (int fd, int swap)
{
    EXCRC ct;
    gvar *cachedGvar;
    char stringKey[8+3];

    if(_dxf_ExReceiveBuffer(fd, &ct, 1, TYPE_INT, swap) < 0) 
        DXUIMessage("ERROR", "bad remote cache tag");

    if ((ct & 0x80000000) == 0)
        _dxf_ExDie("Bad executive cache tag 0x%08x", ct);

    stringKey[0] = 'X';
    int32tohex(stringKey+1, ct);

    /* DPSEND outputs may be already cached */
    if((cachedGvar = (gvar *)
        _dxf_ExDictionarySearch(_dxd_exCacheDict, stringKey)) != NULL) {
        ExDelete(cachedGvar);
        return(OK);
    }
    else {
        ExDebug("2", "DXAdd RemoteTag %s to cache", stringKey);
        return(_dxf_ExDictionaryInsert(_dxd_exCacheDict, stringKey, 
                                       (EXObj)EmptyGvar));
    }
}

/*
 * Delete an entry from the cache.
 */

Error _dxf_ExCacheDelete (EXCRC key)
{
    char stringKey[8+3];
    int	ret;	

    if ((key & 0x80000000) == 0)
	_dxf_ExDie("Bad executive cache tag 0x%08x", key);

    stringKey[0] = 'X';
    int32tohex(stringKey+1, key);

    DXDebug ("1","removing cache entry '%s' in _dxf_ExCacheDelete", stringKey);
    ret = _dxf_ExDictionaryDelete (_dxd_exCacheDict, stringKey);
    if(ret > 0) {
        _dxf_ExDistributeMsg(DM_EVICTCACHE, (Pointer)&key, 
                             sizeof(EXCRC), TOPEERS);
    }
    return(ret);

}

int _dxf_ExCacheDeleteRemoteTag (int fd, int swap)
{
    EXCRC ct;
    char stringKey[8+3];

    if(_dxf_ExReceiveBuffer(fd, &ct, 1, TYPE_INT, swap) < 0) {
        DXUIMessage("ERROR", "bad remote cache tag");
        return(ERROR);
    }

    if ((ct & 0x80000000) == 0)
        _dxf_ExDie("Bad executive cache tag 0x%08x", ct);

    stringKey[0] = 'X';
    int32tohex(stringKey+1, ct);

    DXDebug ("1","removing cache entry '%s' in _dxf_ExCacheDeleteRemoteTag", stringKey);
    return(_dxf_ExDictionaryDelete(_dxd_exCacheDict, stringKey));
}

int _dxf_ExCacheListDeleteRemoteTag(int fd, int swap)
{
    CacheTagList pkg;
    char stringKey[8+3];
    int i;
    
    if(_dxf_ExReceiveBuffer(fd, &(pkg.numtags), 1, TYPE_INT, swap) < 0) {
        DXUIMessage("ERROR", "bad remote cache tag list");
        return(ERROR);
    }

    if(_dxf_ExReceiveBuffer(fd, pkg.ct, pkg.numtags, TYPE_INT, swap) < 0) {
        DXUIMessage("ERROR", "bad remote cache tag");
        return(ERROR);
    }

    stringKey[0] = 'X';
    for(i = 0; i < pkg.numtags; i++) {
        int32tohex(stringKey+1, pkg.ct[i]);
        DXDebug ("1","removing cache entry '%s' in _dxf_ExCacheListDeleteRemoteTag", stringKey);
        if(_dxf_ExDictionaryDelete(_dxd_exCacheDict, stringKey) != OK)
            DXWarning("Cache Tag %s not deleted", stringKey);
    }
    return(OK);
}    

/*
 * Locate an entry in the cache.
 */
gvar *_dxf_ExCacheSearch (EXCRC key)
{
    char	 stringKey[8+3];
    gvar	 *entry;

    if ((key & 0x80000000) == 0)
	_dxf_ExDie("Bad executive cache tag 0x%08x", key);

    stringKey[0] = 'X';
    int32tohex(stringKey+1, key);


    entry =  ((gvar*)_dxf_ExDictionarySearch (_dxd_exCacheDict, stringKey));
    if (entry)
	ExTimestamp((EXO_Object)entry);
    
    return entry;
}

/*
 * Module interface to allow inserting arbitrary data into the cache.
 * The module provides a deletion routine which is called when the
 * entry is removed from the cache.  A cost of CACHE_PERMANENT will
 * insure that the entry is not removed from the cache until the
 * application exits
 */

Error DXCacheInsert (char *id, Pointer data, int (*delete)(), double cost)
{
    Object o;

    o = (Object)DXNewPrivate(data, delete);
    return (DXSetCacheEntry(o, cost, id, 0, 0));
}


/*
 * Module interface to allow deleting specific entries from the cache
 */

Error DXCacheDelete (char *id)
{
    return (DXSetCacheEntry(NULL, 0.0, id, 0, 0));
}

/*
 * Module interface to look up entries in the cache.  The pointer returned
 * is the pointer supplied to DXCacheInsert when the entry was inserted.
 */
Error
DXCacheSearch(char *id, Pointer *data)
{
    Object o;

    *data = NULL;

    o = DXGetCacheEntry(id, 0, 0);
    if (o)
	*data = DXGetPrivateData((Private)o);
    DXDelete (o);

    return(OK);
}


/*
 * Module interface to insert a libdx-style object into the cache.  This
 * routine takes care of referencing the object before inserting it into
 * the cache.  The libdx DXDelete routine is called when the entry is removed
 * from the cache.
 */
Error
DXCacheInsertObject(char *id, Object o, double cost)
{
    Object p;

    p = (Object)DXNewPrivate((Pointer)o, NULL);
    return (DXSetCacheEntry(p, cost, id, 0, 0));
}

/*
 * This routine is responsible for flushing all non-permanent entries from
 * the executive cache.  It walks the dictionary structure and throws
 * away anything that is not marked permanent.
 */

Error
_dxf_ExCacheFlush (int all)
{
    EXObj		obj;
    EXObj		curr;
    gvar		*gv;
    char		*key;
    extern int		*_dxd_exTerminating;
    int  		i, ilimit;
    char	 	stringKey[8+3];

    if (! *_dxd_exTerminating)
        DXDebug ("1", "flushing cache");

    if(!_dxd_exRemoteSlave)
        _dxf_ExDistributeMsg(DM_FLUSHCACHE, (Pointer)&all, sizeof(int), 
                             TOSLAVES);

    _dxf_ExReclaimDisable ();

    _dxf_ExDictionaryBeginIterate (_dxd_exCacheDict);
    while ((obj = _dxf_ExDictionaryIterate (_dxd_exCacheDict, &key)) != NULL)
    {
	curr = NULL;

	switch (EXO_GetClass (obj))
	{
	case EXO_CLASS_GVAR:
	    gv = (gvar *) obj;

	    /* The variable is not currently in use */
	    if (all || gv->cost < CACHE_PERMANENT)
		curr = obj;
	    break;
	
	default:
	    break;
	}

	if (curr)
	{
            DXDebug ("1", "removing cache entry '%s'", key);
	    _dxf_ExDictionaryDeleteNoLock (_dxd_exCacheDict, key);
	}
    }

    _dxf_ExDictionaryEndIterate (_dxd_exCacheDict);
    _dxf_ExFlushPathTags();
    _dxf_ExReclaimEnable ();

    return(OK);
}

Error DXSetCacheEntryV(Object out, double cost, char *function, 
		     int key, int n, Object *in)
{
    char	 tag[8+3];
    gvar	 *entry;
    int		 i;
    EXCRC   	inTags[100];
    EXCRC	outTag;

    
    if (_dxd_exCacheDict == NULL)
	return (OK);

/*---------------------------------------------------------------*/
/* See if user has turned caching off and respect his/her wishes */
/* unless the cost is CACHE_PERMANENT                            */
/*---------------------------------------------------------------*/
    ExDebug("*1","In DXSetCacheEntryV with cost = %g and cache attr = %d",
            cost,_dxd_exCurrentFunc ? _dxd_exCurrentFunc->excache: 1);
    
    if (cost < CACHE_PERMANENT)
      if (_dxd_exCurrentFunc && !_dxd_exCurrentFunc->excache) {
        DXWarning("#4860",function);
        return OK;     /* not an error to not cache .... */
    }

    for (i = 0; i < n && i < 100; ++i)
	inTags[i] = DXGetObjectTag(in[i]);
    outTag = _dxf_ExGenCacheTag(function, key, n, inTags);

    tag[0] = 'U';
    int32tohex(tag+1, outTag);

    if (out == NULL) {
	_dxf_ExDictionaryDelete(_dxd_exCacheDict, tag);
	return (OK);
    }
    else
    {
	entry = _dxf_ExCreateGvar(GV_CACHE);
	if (entry == NULL)
	    return (ERROR);

	_dxf_ExDefineGvar(entry, out);
	entry->cost   = cost;
/* 	entry->reccrc = outTag; */
	entry->procId = exJID;
	ExTimestamp(entry);
	/* DXSetObjectTag(out, outTag); */
	return (_dxf_ExDictionaryInsert (_dxd_exCacheDict, tag, (EXObj)entry));
    }
}

Error DXSetCacheEntry(Object out, double cost, char *function,
		    int key, int n, ...)
{
    Object in[100];
    int i;
    va_list arg;

    va_start(arg,n);
    for (i = 0; i < n && i < 100; ++i)
	in[i] = va_arg(arg, Object);
    va_end(arg);

    return (DXSetCacheEntryV(out, cost, function, key, n, in));
}

Object DXGetCacheEntryV(char *function, int key, int n, Object *in)
{
    char	 tag[8+3];
    gvar	 *entry;
    int		 i;
    EXCRC        inTags[100];
    EXCRC 	outTag;
    Object	out = NULL;

    if (_dxd_exCacheDict == NULL)
	return (NULL);

    for (i = 0; i < n && i < 100; ++i)
	inTags[i] = DXGetObjectTag(in[i]);
    outTag = _dxf_ExGenCacheTag(function, key, n, inTags);

    tag[0] = 'U';
    int32tohex(tag+1, outTag);

    entry = (gvar *)_dxf_ExDictionarySearch (_dxd_exCacheDict, tag);
    if (entry) {
	ExTimestamp(entry);
	out = DXReference (entry->obj);		/* Deleted by user */
	ExDelete(entry);
    }
	
    return (out);
}

Object DXGetCacheEntry(char *function, int key, int n, ...)
{
    Object in[100];
    int i;
    va_list arg;

    va_start(arg,n);
    for (i = 0; i < n && i < 100; ++i)
	in[i] = va_arg(arg, Object);
    va_end(arg);

    return (DXGetCacheEntryV(function, key, n, in));
}
