/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/* 
   This file implements Super Cookie support for the rdf data model.
   For more information on this file, contact rjc or guha 
   For more information on RDF, look at the RDF section of www.mozilla.org
*/

#include "rdf-int.h"
#include "scook.h"
#include "glue.h"
#include "remstore.h"


	/* externs */
extern char* profileDirURL;



char *
makeSCookPathname(char* name)
{
  char		*ans ;
  size_t		s;

  if ((ans = (char*) getMem(strlen(profileDirURL) + strlen(name) + 8)) != NULL) {
    s = strlen(profileDirURL);
    memcpy(ans, profileDirURL, s);
    if (ans[s-1] != '/') {
      ans[s++] = '/';
    }
    memcpy(&ans[s], "SCook/", 5);
    s = s + 5;

#ifdef	XP_WIN
    if (ans[9] == '|') ans[9] = ':';
#endif

    CallPRMkDirUsingFileURL(ans, 00700);  
    memcpy(&ans[s], name, strlen(name));
  }
  return(ans);
}



PRBool
SCookAssert1 (RDFT mcf,   RDF_Resource u, RDF_Resource s, void* v, 
		     RDF_ValueType type, PRBool tv)
{
	return 0;
}



PRBool
SCookAssert3 (RDFT mcf,   RDF_Resource u, RDF_Resource s, void* v, 
		     RDF_ValueType type, PRBool tv)
{
	return (SCookAssert(mcf, u, s, v, type, tv) != NULL);
}



PRBool
SCookAssert2 (RDFFile  file, RDFT mcf,  RDF_Resource u, RDF_Resource s, void* v, 
		     RDF_ValueType type, PRBool tv)
{
  Assertion as = SCookAssert(mcf , u, s, v, type, tv);
  if (as != NULL) {
    void addToAssertionList (RDFFile f, Assertion as) ;
    addToAssertionList(file, as);
    return 1;
  } else return 0;
}



Assertion
SCookAssert (RDFT mcf, RDF_Resource u, RDF_Resource s, void* v, 
			  RDF_ValueType type, PRBool tv)
{
  Assertion nextAs, prevAs, newAs; 
  nextAs = prevAs = getArg1(mcf, u);
  while (nextAs != null) {
    if (asEqual(nextAs, u, s, v, type)) return null;
    prevAs = nextAs;
    nextAs = nextAs->next;
  }
  newAs = makeNewAssertion(u, s, v, type, tv);
  if (prevAs == null) {
    setArg1(mcf, u, newAs);
  } else {
    prevAs->next = newAs;
  }
  if (type == RDF_RESOURCE_TYPE) {
    nextAs = prevAs = getArg2(mcf, (RDF_Resource)v);
    while (nextAs != null) {
      prevAs = nextAs;
      nextAs = nextAs->invNext;
    }
    if (prevAs == null) {
      setArg2(mcf,  ((RDF_Resource)v), newAs);
    } else {
      prevAs->invNext = newAs;
    }
  }
  /* XXX have to mark the entire subtree XXX */  
  sendNotifications2(mcf, RDF_ASSERT_NOTIFY, u, s, v, type, tv);
  return newAs;
}



PRBool
SCookUnassert (RDFT mcf, RDF_Resource u, RDF_Resource s, void* v, 
		       RDF_ValueType type)
{
  Assertion as = SCookRemove(mcf, u, s, v, type);
  freeMem(as);
  return (as != NULL);
}



Assertion
SCookRemove (RDFT mcf, RDF_Resource u, RDF_Resource s, 
			     void* v, RDF_ValueType type)
{
  Assertion nextAs, prevAs, ans;
  PRBool found = false;
  nextAs = prevAs = getArg1(mcf, u);
  while (nextAs != null) {
    if (asEqual(nextAs, u, s, v, type)) {
      if (prevAs == nextAs) {
	setArg1(mcf, u, nextAs->next);
      } else {
	prevAs->next = nextAs->next;
      }
      found = true;
      ans = nextAs;
      break;
    }
    prevAs = nextAs;
    nextAs = nextAs->next; 
  }
  if (found == false) return null;
  if (type == RDF_RESOURCE_TYPE) {
    nextAs = prevAs = getArg2(mcf, (RDF_Resource)v);
    while (nextAs != null) {
      if (nextAs == ans) {
	if (prevAs == nextAs) {
	  setArg2(mcf, ((RDF_Resource)v), nextAs->invNext);
	} else {
	  prevAs->invNext = nextAs->invNext;
	}
      }
      prevAs = nextAs;
      nextAs = nextAs->invNext;
    }
  }
  /* Need to make sure that if something is removed from the bookmark tree,
     the type is updated */
  sendNotifications2(mcf, RDF_DELETE_NOTIFY, u, s, v, type, ans->tv);
  return ans;
}



PRBool
SCookHasAssertion (RDFT mcf, RDF_Resource u, RDF_Resource s, void* v, RDF_ValueType type, PRBool tv)
{
  Assertion nextAs;
  nextAs = getArg1(mcf, u);
  while (nextAs != null) {
    if (asEqual(nextAs, u, s, v, type) && (nextAs->tv == tv)) return true;
    nextAs = nextAs->next;
  }
  possiblyAccessSCookFile(mcf, u, s, 0);
  return false;
}



void *
SCookGetSlotValue (RDFT mcf, RDF_Resource u, RDF_Resource s, RDF_ValueType type, PRBool inversep,  PRBool tv)
{
  Assertion nextAs;
  nextAs = (inversep ? getArg2(mcf, u) : getArg1(mcf, u));
  while (nextAs != null) {
    if ((nextAs->s == s) && (nextAs->tv == tv) && (nextAs->type == type)) {
      return (inversep ? nextAs->u : nextAs->value);
    }
    nextAs = (inversep ? nextAs->invNext : nextAs->next);
  }
   possiblyAccessSCookFile(mcf, u, s, inversep);
  return null;
}



RDF_Cursor
SCookGetSlotValues (RDFT mcf, RDF_Resource u, RDF_Resource s, RDF_ValueType type,  PRBool inversep, PRBool tv)
{
Assertion as = (inversep ? getArg2(mcf, u) : getArg1(mcf, u));
  RDF_Cursor c;
  if (as == null) {
    possiblyAccessSCookFile(mcf, u, s, inversep);
    as = (inversep ? getArg2(mcf, u) : getArg1(mcf, u));
    if (as == NULL) return null;
  }  	
  c = (RDF_Cursor)getMem(sizeof(struct RDF_CursorStruct));
  c->u = u;
  c->s = s;
  c->type = type;
  c->inversep = inversep;
  c->tv = tv;
  c->count = 0;
  c->pdata = as;
  return c;
}



void *
SCookNextValue (RDFT mcf, RDF_Cursor c)
{
  while (c->pdata != null) {
    Assertion as = (Assertion) c->pdata;
    if ((as->s == c->s) && (as->tv == c->tv) && (c->type == as->type)) {
      if (c->s == gCoreVocab->RDF_slotsHere) {
	c->value = as->s;
      } else { 
	c->value = (c->inversep ? as->u : as->value);
      }
      c->pdata = (c->inversep ? as->invNext : as->next);
      return c->value;
    }
    c->pdata = (c->inversep ? as->invNext : as->next);
  }
  return null;
}



RDF_Error
SCookDisposeCursor (RDFT mcf, RDF_Cursor c)
{
  freeMem(c);
  return noRDFErr;
}



Assertion
getArg1 (RDFT r, RDF_Resource u)
{
  return PL_HashTableLookup(((SCookDB)r->pdata)->lhash, u);
}



Assertion
getArg2 (RDFT r, RDF_Resource u)
{
  return PL_HashTableLookup(((SCookDB)r->pdata)->rhash, u);
}



void
setArg1 (RDFT r, RDF_Resource u, Assertion as)
{
  if (as == NULL) {
    PL_HashTableRemove(((SCookDB)r->pdata)->lhash, u);
  } else {
    PL_HashTableAdd(((SCookDB)r->pdata)->lhash, u, as);
  }
}



void
setArg2 (RDFT r, RDF_Resource u, Assertion as)

{
 if (as == NULL) {
    PL_HashTableRemove(((SCookDB)r->pdata)->rhash, u);
  } else {
    PL_HashTableAdd(((SCookDB)r->pdata)->rhash, u, as);
  }
}



void
gcSCookFile (RDFT rdf, RDFFile f)
{
  int16 n = 0;
  RDFFile f1;
  SCookDB sk = (SCookDB)rdf->pdata;
  f1 = sk->rf;
  
  if (f->locked) return;
  
  if (f == f1) {
    sk->rf = f->next;
  } else {
    RDFFile prev = f1;
    while (f1 != NULL) {
      if (f1 == f) {
	prev->next = f->next;
	break;
      }
      prev = f1;
      f1 = f1->next;
    }
  }
  
  while (n < f->assertionCount) {
    Assertion as = *(f->assertionList + n);
    SCookUnassert(rdf, as->u, as->s, as->value, as->type);
    freeAssertion(as);
    *(f->assertionList + n) = NULL;
    n++;
  }
  n = 0;
  while (n < f->resourceCount) {
    RDF_Resource u = *(f->resourceList + n);
    possiblyGCResource(u);
    n++;
  }
  freeMem(f->assertionList);
  freeMem(f->resourceList);	
}



void
disposeAllSCookFiles (RDFT rdf, RDFFile f)
{
  if (f != NULL) {
    disposeAllSCookFiles(rdf, f->next);
    gcSCookFile(rdf, f);
  }
}



void
SCookDisposeDB (RDFT rdf)
{
  SCookDB db = (SCookDB)rdf->pdata;
  disposeAllSCookFiles(rdf, db->rf);
  PL_HashTableDestroy(db->rhash);
  PL_HashTableDestroy(db->lhash);
  freeMem(db);
}



static PRBool
SCookFileReadp (RDFT rdf, RDF_Resource u)
{
  RDFFile f;
  SCookDB db = (SCookDB)rdf->pdata;
  uint n = 0;
  for (f = db->rf; (f != NULL) ; f = f->next) {
	  n++;
    if (urlEquals( resourceID(u), f->url)) {
      return true;
    }
  }
  return false;
}



void
possiblyAccessSCookFile (RDFT mcf, RDF_Resource u, RDF_Resource s, PRBool inversep)
{
  if ((s == gCoreVocab->RDF_parent) && (strstr(resourceID(u), ":/")) &&
      (((SCookDB)mcf->pdata)->rf != NULL) && (containerp(u)) && 
      (resourceType(u) == RDF_RT) && (!SCookFileReadp(mcf, u))) {
    RDFFile newFile = makeRDFFile( resourceID(u), u, 0);
    SCookDB db = (SCookDB)mcf->pdata;
    newFile->next = db->rf;
    newFile->fileType = RDF_XML;
    newFile->db   = mcf;
    db->rf = newFile;
    newFile->db = mcf;
    newFile->assert = SCookAssert2;
    beginReadingRDFFile(newFile);
  }
}



RDFT
MakeSCookDB (char* url)
{
  if (startsWith("rdf:scook:", url) || (startsWith("rdf:ht", url))) {
    RDFT ntr = (RDFT)getMem(sizeof(struct RDF_TranslatorStruct));
    SCookDB sk = (SCookDB)getMem(sizeof(SCookDBStruct));    
    ntr->pdata = sk;
    sk->db     = ntr;
    sk->lhash = PL_NewHashTable(500, idenHash, PL_CompareValues, PL_CompareValues,  NULL, NULL);
    sk->rhash = PL_NewHashTable(500, idenHash, PL_CompareValues, PL_CompareValues,  NULL, NULL);
    ntr->assert = (startsWith("rdf:scook", url) ? SCookAssert3 : SCookAssert1);
    ntr->unassert = SCookUnassert;
    ntr->getSlotValue = SCookGetSlotValue;
    ntr->getSlotValues = SCookGetSlotValues;
    ntr->hasAssertion = SCookHasAssertion;
    ntr->nextValue = SCookNextValue;
    ntr->disposeCursor = SCookDisposeCursor;
    if (startsWith("rdf:scook:", url)) {
        sk->rf = makeRDFFile(makeSCookPathname(&url[10]), NULL, 0);    
        sk->rf->db = (RDFT)ntr;
        sk->rf->assert = SCookAssert2;
	sk->rf->localp = 1;
        beginReadingRDFFile(sk->rf);
	}
    return ntr;
  } else return NULL;
}
