// vim: foldmethod=marker
//
// BEGIN FLOCK GPL
// 
// Copyright Flock Inc. 2005-2006
// http://flock.com
// 
// This file may be used under the terms of of the
// GNU General Public License Version 2 or later (the "GPL"),
// http://www.gnu.org/licenses/gpl.html
// 
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
// for the specific language governing rights and limitations under the
// License.
// 
// END FLOCK GPL
//

const DEBUG = false;                // set to false to suppress debug messages
const FLOCK_SCRAPBOOK_CID           = Components.ID('{4aaa5053-3cac-4378-bb35-295fdf901603}');
const FLOCK_SCRAPBOOK_FNAME         = "flock_shelf.rdf";

const nsISupports                   = Components.interfaces.nsISupports;
const nsIClassInfo                  = Components.interfaces.nsIClassInfo;
const nsIFactory                    = Components.interfaces.nsIFactory;
const nsIProperties                 = Components.interfaces.nsIProperties;
const nsILocalFile                  = Components.interfaces.nsILocalFile;
const nsIFile                       = Components.interfaces.nsIFile;
const nsIIOService                  = Components.interfaces.nsIIOService;
const nsIFileProtocolHandler        = Components.interfaces.nsIFileProtocolHandler;
const nsIRDFRemoteDataSource        = Components.interfaces.nsIRDFRemoteDataSource;
const nsIRDFDataSource              = Components.interfaces.nsIRDFDataSource;
const flockIScrapbookService        = Components.interfaces.flockIScrapbookService;
const flockIPhotoAPI                = Components.interfaces.flockIPhotoAPI;

const FLOCK_SCRAPBOOK_CONTRACTID    = '@mozilla.org/rdf/datasource;1?name=flock-shelf';
const DIRECTORY_SERVICE_CONTRACTID  = '@mozilla.org/file/directory_service;1';
const LOCAL_FILE_CONTRACTID         = '@mozilla.org/file/local;1';
const PREFERENCES_CONTRACTID        = '@mozilla.org/preferences-service;1';
const IO_SERVICE_CONTRACTID         = '@mozilla.org/network/io-service;1';

const FLOCK_NS                      = 'http://flock.com/rdf#';
const FLOCK_RDF_SHELF_ROOT          = 'urn:flock:shelf:objects';
const FLOCK_RDF_SHELF_HISTORY_ROOT  = 'urn:flock:shelf:history';
const SHELF_NS                  = 'http://www.flock.com/rdf/shelf#';

function flockScrapbookService() { /*{{{*/
    debug ('CONSTRUCTOR: flockScrapbookService\n');

    var RDFService = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService().QueryInterface(Components.interfaces.nsIRDFService);
	var dirService = Components.classes['@mozilla.org/file/directory_service;1'].getService(Components.interfaces.nsIProperties);
	var profileDir = dirService.get('ProfD', Components.interfaces.nsILocalFile);
  
	//Create file object in the user's profile path.
	var file = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
	file.initWithPath(profileDir.path);
	file.append(FLOCK_SCRAPBOOK_FNAME);
	//If it doesn't exist, write it.
    if(!file.exists()) file.createUnique(0,0600);

    var ios = Components.classes['@mozilla.org/network/io-service;1']
        .getService(Components.interfaces.nsIIOService);

    var fileHandler = ios.getProtocolHandler('file')
       .QueryInterface(Components.interfaces.nsIFileProtocolHandler);

    var spec = fileHandler.getURLSpecFromFile(file);

    var ds = new RDFDataSource(spec);
    var root = ds.getNode(FLOCK_RDF_SHELF_ROOT);
    // var historyRoot = ds.getNode(FLOCK_RDF_SHELF_HISTORY_ROOT);
    root.makeSeq();
    // historyRoot.makeSeq();
  
    this.ds = ds;
    this.root = root;
    // this.historyRoot = historyRoot;
    this.dataSource = ds.getRawDataSource().QueryInterface (nsIRDFRemoteDataSource);
    this.dataSource.Flush();
}/*}}}*/

loadLibraryFromSpec('chrome://browser/content/flock/contrib/rdfds.js');

function loadLibraryFromSpec(aSpec) {
    var loader = Components.classes['@mozilla.org/moz/jssubscript-loader;1']
        .getService(Components.interfaces.mozIJSSubScriptLoader);

    loader.loadSubScript(aSpec);
}
/* XXX End */

flockScrapbookService.prototype.URI = 'rdf:flock-scrapbook';
flockScrapbookService.prototype.GetSource = function (aProperty, aTarget, aTruthValue) { /*{{{*/
    return this.dataSource.GetSource (aProperty, aTarget, aTruthValue); 
}/*}}}*/
flockScrapbookService.prototype.GetSources = function (aProperty, aTarget, aTruthValue) { /*{{{*/
    return this.dataSource.GetSources (aProperty, aTarget, aTruthValue); 
}/*}}}*/
flockScrapbookService.prototype.GetTarget = function (aSource, aProperty, aTruthValue) { /*{{{*/
    return this.dataSource.GetTarget (aSource, aProperty, aTruthValue); 
}/*}}}*/
flockScrapbookService.prototype.GetTargets = function (aSource, aProperty, aTruthValue) { /*{{{*/
    return this.dataSource.GetTargets (aSource, aProperty, aTruthValue); 
}/*}}}*/
flockScrapbookService.prototype.Assert = function (aSource, aProperty, aTarget, aTruthValue) { /*{{{*/
    this.dataSource.Assert (aSource, aProperty, aTarget, aTruthValue); 
}/*}}}*/
flockScrapbookService.prototype.Unassert = function (aSource, aProperty, aTarget) { /*{{{*/
    this.dataSource.Unassert (aSource, aProperty, aTarget); 
}/*}}}*/
flockScrapbookService.prototype.Change = function (aSource, aProperty, aOldTarget, aNewTarget) { /*{{{*/
    this.dataSource.Change (aSource, aProperty, aOldTarget, aNewTarget); 
}/*}}}*/
flockScrapbookService.prototype.Move = function (aOldSource, aNewSource, aProperty, aTarget) { /*{{{*/
    this.dataSource.Move (aOldSource, aNewSource, aProperty, aTarget); 
}/*}}}*/
flockScrapbookService.prototype.HasAssertion = function (aSource, aProperty, aTarget, aTruthValue) { /*{{{*/
    return this.dataSource.HasAssertion (aSource, aProperty, aTarget, aTruthValue); 
}/*}}}*/
flockScrapbookService.prototype.AddObserver = function (aObserver) { /*{{{*/
    this.dataSource.AddObserver (aObserver); 
}/*}}}*/
flockScrapbookService.prototype.RemoveObserver = function (aObserver) { /*{{{*/
    this.dataSource.RemoveObserver (aObserver); 
}/*}}}*/
flockScrapbookService.prototype.ArcLabelsIn = function (aNode) { /*{{{*/
    return this.dataSource.ArcLabelsIn (aNode); 
}/*}}}*/
flockScrapbookService.prototype.ArcLabelsOut = function (aNode) { /*{{{*/
    return this.dataSource.ArcLabelsOut (aNode); 
}/*}}}*/
flockScrapbookService.prototype.GetAllResources = function () { /*{{{*/
    return this.dataSource.GetAllResources (); 
}/*}}}*/
flockScrapbookService.prototype.IsCommandEnabled = function (aSources, aCommand, aArguments) { /*{{{*/
    return this.dataSource.IsCommandEnabled (aSources, aCommand, aArguments); 
}/*}}}*/
flockScrapbookService.prototype.DoCommand = function (aSources, aCommand, aArguments) { /*{{{*/
    this.dataSource.DoCommand (aSources, aCommand, aArguments); 
}/*}}}*/
flockScrapbookService.prototype.GetAllCmds = function (aSource) { /*{{{*/
    return this.dataSource.GetAllCmds (aSource); 
}/*}}}*/
flockScrapbookService.prototype.hasArcIn = function (aNode, aArc) { /*{{{*/
    return this.dataSource.hasArcIn (aNode, aArc); 
}/*}}}*/
flockScrapbookService.prototype.hasArcOut = function (aNode, aArc) { /*{{{*/
    return this.dataSource.hasArcOut (aNode, aArc); 
}/*}}}*/
flockScrapbookService.prototype.beginUpdateBatch = function () { /*{{{*/
    this.dataSource.beginUpdateBatch (); 
}/*}}}*/
flockScrapbookService.prototype.endUpdateBatch = function () { /*{{{*/
    this.dataSource.endUpdateBatch (); 
}/*}}}*/
flockScrapbookService.prototype.Flush = function () { /*{{{*/
    this.dataSource.Flush (); 
}/*}}}*/
flockScrapbookService.prototype.FlushTo = function (aURI) { /*{{{*/
    this.dataSource.FlushTo (aURI); 
}/*}}}*/
flockScrapbookService.prototype.Init = function (aURI) { /*{{{*/
    this.dataSource.Init (aURI); 
}/*}}}*/
flockScrapbookService.prototype.Refresh = function (aBlocking) { /*{{{*/
    this.dataSource.Refresh (aBlocking); 
}/*}}}*/

// Private methods
flockScrapbookService.prototype.updateSrc = function (anId) { /*{{{*/
   // FIXME: this is hacky, src should not be hardcoded here but
   // should be a method of nsIRichObject
   // ...add a "source template" attribute could do the trick
   // Ex: <a href="$URL$">$TITLE$</a> so we can recalculate the source
   // by replacing $URL$ and $TITLE$
   var type = this.getTargetOf(anId, 'type');
   if (type == 'note') return;

   var title = this.getTargetOf(anId, 'title');
   var type = this.getTargetOf(anId, 'type');
   var url = this.getTargetOf(anId, 'url');
   var content = this.getTargetOf(anId, 'content');
   var webPageUrl = this.getTargetOf(anId, 'webPageUrl');
   var webPageTitle = this.getTargetOf(anId, 'webPageTitle');

   var source;

   switch (type) {
     case 'image': src = '<a href="' + webPageUrl + '" title="' + this.title + '"><img border="0" alt="'
                         + title + '" src="' + url + '"/>';break;
     case 'link': src = '<a href="' + webPageUrl + '"/>'+title+'</a>';break;
     case 'document': src = '<blockquote cite="' + webPageTitle + '">'
               + content + "</blockquote>"
               + '<p class="citation"><cite cite="' + webPageUrl + '">'
               + '<a href="' + webPageUrl + '">' + webPageTitle + '</a>'
               + "</cite></p><br/>";
               break; 
     default: src = this.getTargetOf(anId, 'source');
  }

  var node = this.ds.getNode(anId);
  node.addTargetOnce(SHELF_NS + 'source', src + "");
   
  this.Flush();
}/*}}}*/

// Scrapbook Interface Implementation
flockScrapbookService.prototype.elementsCount = function () { /*{{{*/
    // Just using getChildCount() does not work for some reason. Too bad for now.
    var cnt=0;
    var children = this.root.getChildren();
    while(children.hasMoreElements()){
      children.getNext();
      cnt++;
    }
    // debug("Number of elts in the scrapbook "+cnt+"\n");

    return cnt;
}/*}}}*/

flockScrapbookService.prototype.insert = function (aReference, aPos) { /*{{{*/
    var date=new Date();
    var objid='obj_' + date.getTime();
    var id = FLOCK_RDF_SHELF_ROOT + ":" + objid;
    // Create the new node
    var node = this.ds.getNode(id);

    // Insert each attribute into the shelf
    node.addTargetOnce(SHELF_NS + 'objid', objid + "");
    node.addTargetOnce(SHELF_NS + 'type', aReference.type + "");
    node.addTargetOnce(SHELF_NS + 'date', date.toString());
    node.addTargetOnce(SHELF_NS + 'title', aReference.title + "");
    node.addTargetOnce(SHELF_NS + 'content', aReference.title + "");
    node.addTargetOnce(SHELF_NS + 'url', aReference.url + "");
    node.addTargetOnce(SHELF_NS + 'webPageTitle', aReference.webPageTitle + "");
    node.addTargetOnce(SHELF_NS + 'webPageUrl', aReference.webPageUrl + "");
    node.addTargetOnce(SHELF_NS + 'source', aReference.toHtml() + "");

    if (aPos == -1) {
        var pos = this.root.getChildCount() + 1;
       //this.root.addChildAt(node, pos);
    } else {
        var pos = aPos;
    }
    debug('node pos ' + pos + '\n');
    this.root.addChildAt(node, pos);
    this.Flush();

    return id;
}/*}}}*/

flockScrapbookService.prototype.insertOld = function (aType, aTitle, aContent, anUrl, aSrc, aPos) { /*{{{*/
    var date=new Date();
    var id='obj_' + date.getTime();

    debug("flockScrapbookService.insert("+aType+", "+aTitle+", "+anUrl+", "+aSrc+")\n");

    var node = this.ds.getNode(FLOCK_RDF_SHELF_ROOT + ":" + id);

    node.addTargetOnce(SHELF_NS + 'objid', id + "");
    node.addTargetOnce(SHELF_NS + 'type', aType + "");
    node.addTargetOnce(SHELF_NS + 'title', aTitle + "");
    node.addTargetOnce(SHELF_NS + 'content', aContent + "");
    node.addTargetOnce(SHELF_NS + 'date', date.toString());
    node.addTargetOnce(SHELF_NS + 'url', anUrl + "");
    node.addTargetOnce(SHELF_NS + 'source', aSrc + "");
    
    if (aPos == -1) {
       this.root.addChild(node);
    } else {
       this.root.addChildAt(node, aPos);
    }
    this.Flush();

    return id;
}/*}}}*/

flockScrapbookService.prototype.update = function (anId, aType, aTitle, aContent, anUrl, aSrc){/*{{{*/

    // FIXME: this is a hack to make sure that the templates are updated
    var position = this.getPositionOf(anId);

    var content = this.getTargetOf(anId, 'content');
    var webPageUrl = this.getTargetOf(anId, 'webPageUrl');
    var webPageTitle = this.getTargetOf(anId, 'webPageTitle');

    this.remove(anId);
    
    var node = this.ds.getNode(anId);

    node.addTargetOnce(SHELF_NS + 'type', aType + "");
    node.addTargetOnce(SHELF_NS + 'title', aTitle + "");
    node.addTargetOnce(SHELF_NS + 'url', anUrl + "");
    node.addTargetOnce(SHELF_NS + 'source', aSrc + "");
    node.addTargetOnce(SHELF_NS + 'content', aContent + "");
    node.addTargetOnce(SHELF_NS + 'webPageUrl', webPageUrl + "");
    node.addTargetOnce(SHELF_NS + 'webPageTitle', webPageTitle + "");

    this.root.addChildAt(node, position);

    this.Flush();

    this.updateSrc(anId);
}/*}}}*/

flockScrapbookService.prototype.getPositionOf = function (anId){/*{{{*/
    // XXX("Looking for the position of ", anId);
    var node = this.ds.getNode(anId);
    return this.root.getChildIndex(node); 
}/*}}}*/

flockScrapbookService.prototype.getTargetOf = function (anId, aTarget) { /*{{{*/
    var node = this.ds.getNode(anId);
    var target = node.getTarget(SHELF_NS + aTarget);
    if (target)
        return target.getValue();
    else
        return "";
}/*}}}*/

flockScrapbookService.prototype.remove = function (anId) { /*{{{*/
    debug("flockScrapbookService.remove("+anId+")\n");

    var nodeToRemove = this.ds.getNode(anId);

    this.root.removeChildForReal(nodeToRemove);
    // this.historyRoot.addChild(nodeToRemove);
    this.ds.deleteRecursive(nodeToRemove);

    this.Flush();
}/*}}}*/

flockScrapbookService.prototype.clear = function () { /*{{{*/
    this.ds.deleteRecursive(FLOCK_RDF_SHELF_ROOT);
    this.root = this.ds.getNode("urn:flock:shelf:objects");
    this.root.makeSeq();

    this.Flush();
}/*}}}*/

flockScrapbookService.prototype.flags = nsIClassInfo.Singleton;
flockScrapbookService.prototype.classDescription = "Flock Scrapbook Service";
flockScrapbookService.prototype.getInterfaces = function (count) {
    var interfaceList = [flockIScrapbookService, nsIClassInfo];
    count.value = interfaceList.length;
    return interfaceList;
}
flockScrapbookService.prototype.getHelperForLanguage = function (count) {return null;}/*}}}*/

// the nsISupports implementation/*{{{*/
flockScrapbookService.prototype.QueryInterface =
function (iid) {
    if (!iid.equals(flockIScrapbookService) && 
        !iid.equals(nsIRDFDataSource) &&
        !iid.equals(nsIClassInfo) &&
        !iid.equals(nsISupports))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    if (iid.equals(nsIRDFDataSource) && !this.dataSourceSetup) {
    }
    return this;
}/*}}}*/

// Module implementation/*{{{*/
var ScrapbookModule = new Object();

ScrapbookModule.registerSelf =
function (compMgr, fileSpec, location, type)
{
    compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);

    compMgr.registerFactoryLocation(FLOCK_SCRAPBOOK_CID, 
                                    "Flock Scrapbook JS Component",
                                    FLOCK_SCRAPBOOK_CONTRACTID, 
                                    fileSpec, 
                                    location,
                                    type);
}

ScrapbookModule.getClassObject =
function (compMgr, cid, iid) {
    if (!cid.equals(FLOCK_SCRAPBOOK_CID))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    
    if (!iid.equals(Components.interfaces.nsIFactory))
        throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
    
    return ScrapbookServiceFactory;
}

ScrapbookModule.canUnload =
function(compMgr)
{
    debug("Unloading shelf component.\n");
    //gPhotoService.dataSource.Flush();
    return true;
}/*}}}*/
    
/* factory object *//*{{{*/
var ScrapbookServiceFactory = new Object();

ScrapbookServiceFactory.createInstance =
function (outer, iid) {
    debug("CI: " + iid + "\n");
    if (outer != null)
        throw Components.results.NS_ERROR_NO_AGGREGATION;

    return (new flockScrapbookService()).QueryInterface(iid);
}/*}}}*/

/* entrypoint *//*{{{*/
function NSGetModule(compMgr, fileSpec) {
    return ScrapbookModule;
}/*}}}*/

