// 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 flockIBlogAPI = Components.interfaces.flockIBlogAPI;
const flockIUserBlog = Components.interfaces.flockIUserBlog;
const nsISupports = Components.interfaces.nsISupports;
const FLOCK_MT_CID =  Components.ID('{05707ca4-3432-456d-874e-42991ccf7376}');
const FLOCK_MT_CONTRACTID = '@flock.com/blog/service/movabletype;1';
const FLOCK_MT_DESCRIPTION = 'MovableType';

const FLOCK_BLOG_RDF_SETTINGS       = 'urn:flock:blog:settings';

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

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

BlogAccount.prototype = {
    getInterfaces: function (count) {
        var interfaceList = [Components.interfaces['flockIBlogAccount'], nsIClassInfo];
        count.value = interfaceList.length;
        return interfaceList;
    },
    QueryInterface: function (iid) {
      if (!iid.equals(Components.interfaces.flockIBlogAccount))
          throw Components.results.NS_ERROR_NO_INTERFACE;
      return this;
    }

}



// CONSTRUCTOR
function movabletypeWebService () {
    debug ('CONSTRUCTOR: movabletypeWebService\n\n\n');
    //this.mQueue = null;

    // set attributes
    this.name = 'MovableType';
    this.shortName = 'movabletype';
    this.description = "The MovableType API"
    this.supportsCategories = true;
    this.appKey = "";
}

// the nsISupports implementation
movabletypeWebService.prototype.QueryInterface = function (iid) {
    if (iid.equals(flockIBlogAPI) || iid.equals(nsISupports)) {
        return this;
    }

    throw Components.results.NS_ERROR_NO_INTERFACE;
}

loadLibrary("xmlrpc.js.lib");
//loadLibraryFromSpec("chrome://browser/content/flock/common/xmlrpc.js");
function loadLibraryFromSpec(aSpec) {
    var loader = Components.classes['@mozilla.org/moz/jssubscript-loader;1']
        .getService(Components.interfaces.mozIJSSubScriptLoader);

    loader.loadSubScript(aSpec);
}

// PRIVATE METHODS

function unescapeXML(aString) {
    var result = aString;
 	result = result.replace(/&#0*34;/, '"');
 	result = result.replace(/&#0*38;/, '&');
 	result = result.replace(/&#0*39;/, "'");
 	result = result.replace(/&#0*60;/, "<");
 	result = result.replace(/&#0*62;/, ">");
 	result = result.replace(/&#160;/, " ");
 	result = result.replace(/&#173;/, "-");
    return result;
}

movabletypeWebService.prototype.xmlrpc = function() {
    var listener = arguments[0];   
    var url = arguments[1];   
    var call = arguments[2];   

    var params = new Array();

    for(var i=3;i<arguments.length;++i)
    {
        params[params.length] = arguments[i];
    }
    var rpc = new JSXMLRPC();
    
    rpc.call(listener, url, call, params);
}

// Listeners
function blogListener(aListener, aBool){
    this.listener = aListener;
    this.container = aBool
}

blogListener.prototype = 
{
    // aResult is an Array of struct objects
    onResult: function(aResult) {
        // Create an in-memory datasource
        var rdfService = Components.classes["@mozilla.org/rdf/rdf-service;1"]
            .getService(Components.interfaces.nsIRDFService);
        var rdf = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].createInstance(Components.interfaces.nsIRDFDataSource);
        var rdfContainerUtils = Components.classes["@mozilla.org/rdf/container-utils;1"]
            .createInstance(Components.interfaces.nsIRDFContainerUtils);
        // Fill it with the results
        if (this.container)
            var container = rdfContainerUtils.MakeSeq(rdf, rdfService.GetResource("urn:flock:blog:elements"));
        for (i=0; i<aResult.length; i++){
            debug("==> Insert\n");
            var id='obj_' + Math.round(10000*Math.random())
            var subject = rdfService.GetResource("http://www.flock.com/rdf/"+id);
            var entry = aResult[i];
            for (j in entry){
                debug(j+" "+entry[j]+"\n");
                var predicate = rdfService.GetResource("http://www.flock.com/rdf/"+j);
                if (typeof entry[j] == 'string')
                    entry[j] = unescapeXML(entry[j]);
                var name = rdfService.GetLiteral(entry[j]);
                
                rdf.Assert(subject, predicate, name, true);
            }
            if (this.container)
                container.AppendElement(subject);
        }
 
        // and return the data source through the listener
        this.listener.onResult(rdf);
    },
    onError: function(error) {
        this.listener.onError(error);
    }, 
    onFault: function(error) {
        this.listener.onFault(error);
    } 
}

function userBlogsListener(aListener){
    this.listener = aListener;
}

userBlogsListener.prototype = 
{
  // aResult is an Array of struct objects
  onResult: function(aResult) {
    var result = new Array();
    for (i=0; i<aResult.length; i++){
      var entry = aResult[i];
      var newAccount = new BlogAccount(entry.blogName);
      newAccount.blogid = entry.blogid;
      newAccount.apiLink = "";
      newAccount.url = entry.url;
      result.push(newAccount);
    }
    this.listener.onResult(simpleEnumerator(result));
  },
  onError: function(error) {
    this.listener.onError(error);
  }, 
  onFault: function(error) {
    this.listener.onFault(error);
  } 
}


function blogNewPostListener(aListener){
    this.listener = aListener;
}

blogNewPostListener.prototype = 
{
    onResult: function(aResult) {
        // Create an in-memory datasource
        var rdfService = Components.classes["@mozilla.org/rdf/rdf-service;1"]
            .getService(Components.interfaces.nsIRDFService);
        var rdf = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].createInstance(Components.interfaces.nsIRDFDataSource);
        var rdfContainerUtils = Components.classes["@mozilla.org/rdf/container-utils;1"]
            .createInstance(Components.interfaces.nsIRDFContainerUtils);
        // debug(aResult);
        // Fill it with the results
        var container = rdfContainerUtils.MakeSeq(rdf, rdfService.GetResource("urn:flock:blog:elements"));
        var subject = rdfService.GetResource("http://www.flock.com/rdf/newpost");
        var predicate = rdfService.GetResource("http://www.flock.com/rdf/postid");
        var target = rdfService.GetLiteral(aResult);
                
        rdf.Assert(subject, predicate, target, true);
 
        // and return the data source through the listener
        this.listener.onResult(rdf);
    },
    onError: function(error) {
        this.listener.onError(error);
    }, 
    onFault: function(error) {
        this.listener.onFault(error);
    } 
}


movabletypeWebService.prototype.detectAccount = function (aUrl, aLinkList){
  while(aLinkList.hasMoreElements()) {
    var link = aLinkList.getNext();
    link.QueryInterface(Components.interfaces['flockIBlogLink']);
    // dump("   "+link.href+"\n");
    if (link.rel=="pingback"){
      var account = {
        api : "movabletype",
        apiLink : link.href,
        blogid : "1"
      };
      return account; 
    }
  }
  return null;
}


movabletypeWebService.prototype.newPost = function(aListener, aAccount, aTitle, aDescription, aDateCreated, aPublish, aNotifications){
    var listener = new blogNewPostListener(aListener);
    var notifsArray = new Array();
    while (aNotifications.hasMore())
      notifsArray.push(aNotifications.getNext());
    var body = "<params>"+
		       "<param><value>"+aAccount.blogid.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+aAccount.username.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+aAccount.password.toXMLRPCParam()+"</value></param>"+
               "<param><value><struct>"+
                   "<member><name>title</name><value>"+aTitle.toXMLRPCParam()+"</value></member>"+
                   "<member><name>description</name><value>"+aDescription.toXMLRPCParam()+"</value></member>"+
                   "<member><name>mt_convert_breaks</name><value>"+"".toXMLRPCParam()+"</value></member>"+
                   "<member><name>mt_tb_ping_urls</name><value>"+notifsArray.toXMLRPCParam()+"</value></member>"+
               "</struct></value></param>"+
		       "<param><value>"+aPublish.toXMLRPCParam()+"</value></param>"+
               "</params>";
    debug(body+"\n");
    return this.xmlrpc(listener, aAccount.apiLink, "metaWeblog.newPost", body);
}

movabletypeWebService.prototype.editPost = function(aListener, aAccount, aPostid, aTitle, aDescription, aDateCreated, aPublish, atomid, editURI, aNotifications){
    var listener = new blogListener(aListener, false);
    var notifsArray = new Array();
    while (aNotifications.hasMore())
      notifsArray.push(aNotifications.getNext());
    var body = "<params>"+
		       "<param><value>"+aPostid.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+aAccount.username.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+aAccount.password.toXMLRPCParam()+"</value></param>"+
               "<param><value><struct>"+
                   "<member><name>title</name><value>"+aTitle.toXMLRPCParam()+"</value></member>"+
                   "<member><name>description</name><value>"+aDescription.toXMLRPCParam()+"</value></member>"+
                   "<member><name>dateCreated</name><value>"+(new Date(aDateCreated)).toXMLRPCParam()+"</value></member>"+
                   "<member><name>mt_convert_breaks</name><value>"+"".toXMLRPCParam()+"</value></member>"+
                   "<member><name>mt_tb_ping_urls</name><value>"+notifsArray.toXMLRPCParam()+"</value></member>"+
               "</struct></value></param>"+
		       "<param><value>"+aPublish.toXMLRPCParam()+"</value></param>"+
               "</params>";
    debug(body+"\n");
    return this.xmlrpc(listener, aAccount.apiLink, "metaWeblog.editPost", body);
}

movabletypeWebService.prototype.deletePost = function(aListener, aAccount, aPostid){
    return this.xmlrpc(aListener, this.apiLink, "blogger.deletePost", this.appKey,
            aPostid, this.username, this.password, true);
}

movabletypeWebService.prototype.setPostCategories = function(aListener, aAccount, aPostid, aCategories){
  var listener = new blogListener(aListener, false);
  var body = "<params>"+
    "<param><value>"+aPostid.toXMLRPCParam()+"</value></param>"+
    "<param><value>"+aAccount.username.toXMLRPCParam()+"</value></param>"+
    "<param><value>"+aAccount.password.toXMLRPCParam()+"</value></param>"+
    "<param><value><array><data>";
  while (aCategories.hasMore()) {
    var category = aCategories.getNext();
    body = body + "<struct>";
    body = body + "<member>";
    body = body + "<name>categoryId</name>";
    body = body + "<value>" + category.toXMLRPCParam() + "</value>";
    body = body + "</member>";
    body = body + "</struct>";
  }
  body = body + "</data></array></value></param></params>";
  debug(body);
  return this.xmlrpc(listener, aAccount.apiLink, "mt.setPostCategories", body); 
}

movabletypeWebService.prototype.getPost = function(aListener, aAccount, aPostid){
    var listener = new blogListener(aListener, false);
    var body = "<params>"+
		       "<param><value>"+aPostid.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+aAccount.username.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+aAccount.password.toXMLRPCParam()+"</value></param>"+
               "</params>";
    return this.xmlrpc(listener, aAccount.apiLink, "metaWeblog.getPost", body);
}

movabletypeWebService.prototype.getUsersBlogs = function(aListener, aAccount){
    var listener = new userBlogsListener(aListener, false);
    var body = "<params>"+
		       "<param><value>"+this.appKey.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+aAccount.username.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+aAccount.password.toXMLRPCParam()+"</value></param>"+
               "</params>";
    return this.xmlrpc(listener, aAccount.apiLink, "blogger.getUsersBlogs", body);
}

movabletypeWebService.prototype.getRecentPosts = function(aListener, aAccount, aNumber){
    var listener = new blogListener(aListener, true);
    var body = "<params>"+
               "<param><value>"+aAccount.blogid.toXMLRPCParam()+"</value></param>"+
               "<param><value>"+aAccount.username.toXMLRPCParam()+"</value></param>"+
               "<param><value>"+aAccount.password.toXMLRPCParam()+"</value></param>"+
               "<param><value>"+aNumber.toXMLRPCParam()+"</value></param>"+
               "</params>";
    debug(body+"\n");
    return this.xmlrpc(listener, aAccount.apiLink, "metaWeblog.getRecentPosts", body);
}

movabletypeWebService.prototype.getCategoryList = function(aListener, aAccount){
    var listener = new blogListener(aListener, false);
    var body = "<params>"+
		       "<param><value>"+aAccount.blogid.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+aAccount.username.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+aAccount.password.toXMLRPCParam()+"</value></param>"+
               "</params>";
    debug(body+"\n");
    return this.xmlrpc(listener, aAccount.apiLink, "mt.getCategoryList", body);
}

function loadLibrary(aFilename) { // {{{
    var file = Components.classes["@mozilla.org/file/directory_service;1"]
        .getService(Components.interfaces.nsIProperties)
        .get("ComsD", Components.interfaces.nsIFile);
    file.append(aFilename);

    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 scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
        .getService(Components.interfaces.mozIJSSubScriptLoader);

    scriptLoader.loadSubScript(spec);
}
// }}}


// Module implementation
var MovabletypeModule = new Object();

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

    compMgr.registerFactoryLocation(FLOCK_MT_CID, 
                                    "Flock MovableType JS Component",
                                    FLOCK_MT_CONTRACTID, 
                                    fileSpec, 
                                    location,
                                    type);
    //necessary category registration
    var catmgr = Components.classes["@mozilla.org/categorymanager;1"]
        .getService (Components.interfaces.nsICategoryManager);
    catmgr.addCategoryEntry('flockIBlogAPI', 'movabletype', FLOCK_MT_CONTRACTID, true, true);
}

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

MovabletypeModule.canUnload =
function(compMgr)
{
    debug("Unloading Movabletype component.\n");
    return true;
}
    
// factory object
var MovabletypeServiceFactory = new Object();

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

    return (new movabletypeWebService()).QueryInterface(iid);
}

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

debug ('LOADED: flockBlogWebServiceMovabletype.js\n');


