//
// 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_BLOG_CID                = Components.ID('{3ebdb169-dbf3-441e-a461-5567bc82cc0c}');
const FLOCK_BLOG_FNAME              = "flock_blog.rdf";
const BLOG_RECENT_COUNT             = 5;

const IBLOG_WEB_SERVICE = Components.interfaces['flockIBlogWebService'];
const BLOG_ATOM = '@flock.com/blog/service/atom;1';
const BLOG_MOVABLETYPE = '@flock.com/blog/service/movabletype;1';
const BLOG_BLOGGER = '@flock.com/blog/service/blogger;1';

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 flockIBlogService             = Components.interfaces.flockIBlogService;

const FLOCK_BLOG_CONTRACTID         = '@mozilla.org/rdf/datasource;1?name=flock-blog';
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://www.flock.com/rdf#';
const FLOCK_BLOG_RDF_RECENT_ROOT    = 'urn:flock:blog:recent';
const FLOCK_BLOG_RDF_SETTINGS       = 'urn:flock:blog:settings';
const FLOCK_BLOG_RDF_PREF_ROOT = "urn:flock:blog:preferences";
const FLOCK_BLOG_RDF_NOTIFICATION_ROOT = FLOCK_BLOG_RDF_PREF_ROOT + ":notifications";

function flockBlogService() { /*{{{*/
    debug ('CONSTRUCTOR: flockBlogService\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_BLOG_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);
 
    // Create the accounts sequence... 
    var settings = ds.getNode(FLOCK_BLOG_RDF_SETTINGS);
    settings.makeSeq();

    this.ds = ds;
    //this.root = root;
    this.dataSource = ds.getRawDataSource().QueryInterface (nsIRDFRemoteDataSource);
    this.dataSource.Flush();
    
    // Remove the good old "Drafts" account
    //var drafts = this.getAccount(FLOCK_BLOG_RDF_SETTINGS+":Drafts");
    //if (drafts.url == " ") // Don't delete a legitimate account called "Drafts"
    //    this.deleteAccount(FLOCK_BLOG_RDF_SETTINGS+":Drafts");

}/*}}}*/

// nsISimpleEnumerator implementation {{{
function simpleEnumerator (aArray) {
    aArray.hasMoreElements = function () { 
        return this.length != 0; 
    }
    aArray.getNext = function () { 
        return this.shift (); 
    }
    return aArray;
} // }}}

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 */

createAPIFromName = function(aName) {
    var B;
    // should link this to the dropdown better
    switch(aName) {
        case "livejournal":
        case "atom":
           debug("Atom...\n");
           B = Components.classes[BLOG_ATOM].getService(IBLOG_WEB_SERVICE);
        break;
        case "wordpress":
        case "typepad":
        case "typo":
        case "drupal":
        case "movabletype":
           debug("Movabletype...\n");
           B = Components.classes[BLOG_MOVABLETYPE].getService(IBLOG_WEB_SERVICE);
        break;
        case "blogger":
           debug("Blogger...\n");
           B = Components.classes[BLOG_BLOGGER].getService(IBLOG_WEB_SERVICE);
        break;
        case "metaweblog":
           debug("Metaweblog...\n");
            B = new MetaweblogAPI();
        break;
        default:
            //throw new Error("hmm.  why didn't we find an api?");
        break;
    }
    return B;
}

function BlogAccount(aTitle) {
    this.title = aTitle;
    this.uid = FLOCK_BLOG_RDF_SETTINGS + ":" + aTitle;
    this.url = " ";
    this.username = "";
    this.password = "";
    this.pwhost = "";
}

BlogAccount.prototype = {
    getInterfaces: function (count) {
        var interfaceList = [flockIBlogAccount, nsIClassInfo];
        count.value = interfaceList.length;
        return interfaceList;
    }
}

function getPassword(aHost, aUsername) {
    var psm = Components.classes["@mozilla.org/passwordmanager;1"].getService(Components.interfaces.nsIPasswordManager);
    var enum = psm.enumerator;
    while(enum.hasMoreElements()) {
        var pw = enum.getNext();
        pw = pw.QueryInterface(Components.interfaces.nsIPassword);
        if(pw.host==aHost && aUsername==pw.user) {
            return pw.password;
        }
    }
    return null;
}


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

// Ensure that the "recent" list as no more than BLOG_RECENT_COUNT elements
flockBlogService.prototype.cleanRecent = function () {
    var root = this.ds.getNode(FLOCK_BLOG_RDF_RECENT_ROOT);
    root.makeSeq();
    while (root.getChildCount() > BLOG_RECENT_COUNT) {
       debug(root.getChildCount()+" Let's remove an element.\n");
       root.removeChildAt(1);
    }
    this.ds.save();
}

// the flockIBlogService implementation/*{{{*/

flockBlogService.prototype.accountExists = function (aTitle) { 
    var root = this.ds.getNode(FLOCK_BLOG_RDF_SETTINGS);
    var node = this.ds.getNode(FLOCK_BLOG_RDF_SETTINGS + ":" + aTitle);

    return (root.getChildIndex(node) > -1);
}

flockBlogService.prototype.accountCount = function () {
    var ds = this.ds;
    var settings = ds.getNode(FLOCK_BLOG_RDF_SETTINGS);
    var accounts = settings.getChildren();
    var count = 0;
    while(accounts.hasMoreElements()) {
        var account = accounts.getNext();
        ++count;
    }
    return count;
}

flockBlogService.prototype.getAccountList = function () {
  debug("GET ACCOUNT LIST\n");
  var ds = this.ds;
  var settings = ds.getNode(FLOCK_BLOG_RDF_SETTINGS);
  var accounts = settings.getChildren();
  var result = "";
  while(accounts.hasMoreElements()) {
    var account = accounts.getNext();
    var title = FLOCK_BLOG_RDF_SETTINGS+":"+account.getTarget(FLOCK_NS+"title").getValue();
    if (result != "")
      result = result + "|";
    result = result + title;
  }
  return result;
}

flockBlogService.prototype.addRecent = function (aName, aFile) { 
    var date=new Date();
    var root = this.ds.getNode(FLOCK_BLOG_RDF_RECENT_ROOT);
    root.makeSeq();

    var entry = this.ds.getNode(unescape(aFile));
    entry.addTargetOnce(FLOCK_NS+"filename", aFile+"");
    entry.addTargetOnce(FLOCK_NS+"shortname", aName+"");
    entry.addTargetOnce(FLOCK_NS+"date", date.getTime()+"");

    root.addChild(entry, false);

    this.ds.save();
    this.cleanRecent();
}

flockBlogService.prototype.recentIsEmpty = function () { 
    var root = this.ds.getNode(FLOCK_BLOG_RDF_RECENT_ROOT);
    root.makeSeq();
    try {
        // XXX: why does this fail sometimes?
        return (root.getChildCount() < 1);
    } catch (e) {
        return true;
    }
}

flockBlogService.prototype.saveAccount = function (aBlogAccount) {
    var ds = this.ds;
    var settings = ds.getNode(FLOCK_BLOG_RDF_SETTINGS);
    var count = settings.getChildCount();

    var bloguri = aBlogAccount.uid;

    var profile = ds.getNode(bloguri);
    var api = this.ds.getNode(bloguri + ":" + "api");

    profile.addTargetOnce(FLOCK_NS+"url", aBlogAccount.url + "");
    var uri = Components.classes["@mozilla.org/network/standard-url;1"].createInstance(Components.interfaces.nsIURI);
    uri.spec = aBlogAccount.url;
    var pwhost = "flockinternal." + uri.host;
    profile.addTargetOnce(FLOCK_NS+"pwhost", pwhost);

    profile.addTargetOnce(FLOCK_NS+"title", aBlogAccount.title + "");
    profile.addTargetOnce(FLOCK_NS+"username", aBlogAccount.username + "");

    if(aBlogAccount.username && aBlogAccount.username.length) {
        var psm = Components.classes["@mozilla.org/passwordmanager;1"].getService(Components.interfaces.nsIPasswordManager);
        psm.addUser(pwhost, aBlogAccount.username, aBlogAccount.password);
    }
    profile.addTargetOnce(FLOCK_NS+"api", api);
    profile.addTargetOnce(FLOCK_NS+"init", false + "");
    settings.makeSeq();
    settings.addChild(profile);
    var categoriesNode = ds.getNode(bloguri + ":" + "categories");
    categoriesNode.makeSeq();
    ds.save();
}
 
flockBlogService.prototype.deleteAccount = function (aBlogID) {
    var ds = this.ds;
    var profile = ds.getNode(aBlogID);  
    try {
        var pwhost = profile.getTarget(FLOCK_NS+"pwhost").getValue();
        var user = profile.getTarget(FLOCK_NS+"username").getValue();
        var psm = Components.classes["@mozilla.org/passwordmanager;1"].getService(Components.interfaces.nsIPasswordManager);
        psm.removeUser(pwhost, user);
    } catch(e) {
        debug("Blog service: Error removing the login/password.\n"+pwhost+" - "+user+"\n");
    }
    ds.deleteRecursive(profile);
    var settings = ds.getNode(FLOCK_BLOG_RDF_SETTINGS);
    settings.removeChild(profile);

    var categories = ds.getNode(aBlogID+":categories");
    ds.deleteRecursive(categories);

    ds.save();
}


flockBlogService.prototype.getAccount = function (aBlogID) {
    if(!aBlogID) throw new Error("Error in BlogSettings.getAccount()");

    var rval = new BlogAccount(aBlogID);
    var account = this.ds.getNode(aBlogID);
    try {
        rval.title = account.getTarget(FLOCK_NS+"title").getValue();
        rval.username = account.getTarget(FLOCK_NS+"username").getValue();
        rval.pwhost = account.getTarget(FLOCK_NS+"pwhost").getValue();
        //rval.password = account.getTarget(FLOCK_NS+"password").getValue();
        rval.password = getPassword(rval.pwhost, rval.username);
        //psm.addUser(pwhost, aBlogAccount.username, aBlogAccount.password);
        rval.uid = aBlogID;
        rval.url = account.getTarget(FLOCK_NS+"url").getValue();
    } catch(e) {
        debug("Error in getAccount(): "+e+"\n");
    }
    return rval;
};
/*}}}*/


flockBlogService.prototype.getBlogAPI = function (aBlogID) {
  try {
    var ds = this.ds;
    var profile = ds.getNode(aBlogID);
    var u = profile.getTarget(FLOCK_NS+"username").getValue();
    var t = profile.getTarget(FLOCK_NS+"title").getValue();
    var pwhost = profile.getTarget(FLOCK_NS+"pwhost").getValue();
    var p = getPassword(pwhost, u);

    var api = profile.getTarget(FLOCK_NS+"api");
    var apiname = api.getTarget(FLOCK_NS+"apiname").getValue();
    var apiLink = api.getTarget(FLOCK_NS+"apiLink").getValue();
    var blogid = api.getTarget(FLOCK_NS+"blogid").getValue();

    var B = null;
    B = createAPIFromName(apiname);
    B.init(apiname, "", apiLink, blogid, "", "");
    B.setAuth(u,p);

    return B;
  }
  catch(e) {
    debug("Caught exception: "+e+" "+e.fileName+" "+e.lineNumber+"\n");
    return null;
  }
};

flockBlogService.prototype.setBlogAPI = function (aBlogID, aBlogAPI) {
  var ds = this.ds;
  var api = this.ds.getNode(aBlogID + ":" + "api");
  debug(aBlogID + ":" + "api\n");
  api.addTargetOnce(FLOCK_NS+"apiname", aBlogAPI.name + "");
  api.addTargetOnce(FLOCK_NS+"apiLink", aBlogAPI.apiLink + "");
  api.addTargetOnce(FLOCK_NS+"editLink", aBlogAPI.editLink + "");
  api.addTargetOnce(FLOCK_NS+"blogid", aBlogAPI.blogID + "");
  ds.save();
};

flockBlogService.prototype.addCategory = function (aBlogID, aCategoryId, aCategoryName){
  var ds = this.ds;
  var categoriesNode = ds.getNode(aBlogID + ":" + "categories");
  categoriesNode.makeSeq();
  var categoryUID =  aBlogID + ":categories:" + aCategoryId;
  var categoryNode = ds.getNode(categoryUID);
  categoryNode.addTargetOnce(FLOCK_NS+"categoryName", aCategoryName + "");
  categoryNode.addTargetOnce(FLOCK_NS+"categoryId", aCategoryId + "");
  categoriesNode.addChild(categoryNode);
  ds.save();
};

flockBlogService.prototype.addNotification = function (aURL){
  var ds = this.ds;
  var root = ds.getNode(FLOCK_BLOG_RDF_NOTIFICATION_ROOT);
  root.makeSeq();
  var node = ds.getNode(FLOCK_BLOG_RDF_NOTIFICATION_ROOT + ":" + aURL);
  node.addTargetOnce(FLOCK_NS + "url", aURL);
  root.addChild(node);
  ds.save();
};

flockBlogService.prototype.removeNotification = function (aURL){
  var ds = this.ds;
  var root = ds.getNode(FLOCK_BLOG_RDF_NOTIFICATION_ROOT);
  var node = ds.getNode(FLOCK_BLOG_RDF_NOTIFICATION_ROOT + ":" + aURL);
  ds.deleteRecursive(node);
  root.removeChild(node);
  ds.save();
};


flockBlogService.prototype.getNotifications = function (){
    var ds = this.ds;
    var notifs = "";
    var elements = ds.getNode(FLOCK_BLOG_RDF_NOTIFICATION_ROOT).getChildren();
    while (elements.hasMoreElements ()) {
        var notification = elements.getNext ().getTarget(FLOCK_NS + "url");
        if (notifs != "")
            notifs = notifs + "|";
        notifs = notifs + notification.getValue();
    }

    return notifs;
};

flockBlogService.prototype.flags = nsIClassInfo.SINGLETON;
flockBlogService.prototype.classDescription = "Flock Blog Service";
flockBlogService.prototype.getInterfaces = function (count) {
    var interfaceList = [flockIBlogService, nsIClassInfo];
    count.value = interfaceList.length;
    return interfaceList;
}
flockBlogService.prototype.getHelperForLanguage = function (count) {return null;}/*}}}*/

// the nsISupports implementation/*{{{*/
flockBlogService.prototype.QueryInterface =
function (iid) {
    if (!iid.equals(flockIBlogService) && 
        !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 BlogModule = new Object();

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

    compMgr.registerFactoryLocation(FLOCK_BLOG_CID, 
                                    "Flock Blog JS Component",
                                    FLOCK_BLOG_CONTRACTID, 
                                    fileSpec, 
                                    location,
                                    type);
}

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

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

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

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

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

