// vim: ts=4 sw=4 expandtab cindent 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 flockIBlogWebService = Components.interfaces.flockIBlogWebService;
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';

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

    // set attributes
    //this.configurationDialog = null;
    //this.name = 'MovableType';
    //this.description = "MovableType API (Wordpress...)"
    //this.id = 'movabletype';
    this.supportsCategories = true;

    // Logger
    this.logger = Components.classes['@flock.com/logger;1']
        .getService(Components.interfaces.flockILogger);
    this.logger.init("blog");
}/*}}}*/

// the nsISupports implementation/*{{{*/
movabletypeWebService.prototype.QueryInterface = function (iid) {
    if (iid.equals(flockIBlogWebService) || 
            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();

    var logMessage = ">>>>>>>>>> MovableType API: FLOCK TO SERVER\n";
    logMessage += "  URL: " + url + "\n";
    logMessage += "  CALL: " + call + "\n";
    for (i in params)
        logMessage += "  params["+i+"] = "+params[i]+"\n";
    this.logger.write(logMessage); 
    rpc.call(listener, url, call, params);
}

// Listeners
function blogListener(aListener, aBool){
    this.listener = aListener;
    this.container = aBool
    this.logger = Components.classes['@flock.com/logger;1']
        .getService(Components.interfaces.flockILogger);
    this.logger.init("blog");
}

blogListener.prototype = 
{
    // aResult is an Array of struct objects
    onResult: function(aResult) {
        this.logger.write("<<<<<<<<<< MovableType API: SERVER TO FLOCK\n");
        for (i=0; i<aResult.length; i++){
            this.logger.write("Entry\n");
            var entry = aResult[i];
            for (j in entry)
                this.logger.write("  -> "+j+": "+entry[j]+"\n");
        }
        // 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"]
            .getService(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++){
            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){
                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.logger.write("<<<<<<<<<< MovableType API: SERVER TO FLOCK\n");
        this.logger.write("ERROR "+error+"\n");
        this.listener.onError(error);
    }, 
    onFault: function(error) {
        this.logger.write("<<<<<<<<<< MovableType API: SERVER TO FLOCK\n");
        this.logger.write("FAULT "+error+"\n");
        this.listener.onFault(error);
    } 
}

function blogNewPostListener(aListener){
    this.listener = aListener;
    this.logger = Components.classes['@flock.com/logger;1']
        .getService(Components.interfaces.flockILogger);
    this.logger.init("blog");
}

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"]
            .getService(Components.interfaces.nsIRDFContainerUtils);
        this.logger.write("<<<<<<<<<< MovableType API: SERVER TO FLOCK\n");
        this.logger.write(aResult+"\n");
        // 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.logger.write("<<<<<<<<<< MovableType API: SERVER TO FLOCK\n");
        this.logger.write("ERROR "+error+"\n");
        this.listener.onError(error);
    }, 
    onFault: function(error) {
        this.logger.write("<<<<<<<<<< MovableType API: SERVER TO FLOCK\n");
        this.logger.write("FAULT "+error+"\n");
        this.listener.onFault(error);
    } 
}/*}}}*/


// IMPLEMENTATION
movabletypeWebService.prototype.init = function(aName, aAppKey, aAPILink, aBlogID, aUsername, aPassword){
    this.name = aName;
    this.appKey = aAppKey;
    this.editLink = "";
    this.apiLink = aAPILink;
    this.blogID = aBlogID;
    this.username = aUsername;
    this.password = aPassword;
}

movabletypeWebService.prototype.setAuth = function(aUsername, aPassword){
    this.username = aUsername;
    this.password = aPassword;
}

movabletypeWebService.prototype.newPost = function(aListener, aTitle, aDescription, aDateCreated, aPublish, aNotifications){
    var listener = new blogNewPostListener(aListener);
    var notifsArray = aNotifications.split('|');
    var body = "<params>"+
		       "<param><value>"+this.blogID.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+this.username.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+this.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>";
    return this.xmlrpc(listener, this.apiLink, "metaWeblog.newPost", body);
}

movabletypeWebService.prototype.editPost = function(aListener, aPostid, aTitle, aDescription, aDateCreated, aPublish, atomid, editURI, aNotifications){
    var listener = new blogListener(aListener, false);
    var body = "<params>"+
		       "<param><value>"+aPostid.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+this.username.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+this.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>"+aNotifications.toXMLRPCParam()+"</value></member>"+
               "</struct></value></param>"+
		       "<param><value>"+aPublish.toXMLRPCParam()+"</value></param>"+
               "</params>";
    debug(body+"\n");
    return this.xmlrpc(listener, this.apiLink, "metaWeblog.editPost", body);
}

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

movabletypeWebService.prototype.setPostCategories = function(aListener, aPostid, aCategories){
    var listener = new blogListener(aListener, false);
    var body = "<params>"+
		       "<param><value>"+aPostid.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+this.username.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+this.password.toXMLRPCParam()+"</value></param>"+
               "<param><value><array><data>";
    debug("\n\n"+aCategories+"\n\n");
    var catArray = aCategories.split("|");
    for (i=0; i<catArray.length; i++) {
        var category = catArray[i] * 1; // * 1 to convert to number
        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, this.apiLink, "mt.setPostCategories", body); 
}

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

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

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

movabletypeWebService.prototype.getCategoryList = function(aListener){
    var listener = new blogListener(aListener, false);
    var body = "<params>"+
		       "<param><value>"+this.blogID.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+this.username.toXMLRPCParam()+"</value></param>"+
		       "<param><value>"+this.password.toXMLRPCParam()+"</value></param>"+
               "</params>";
    debug(body+"\n");
    return this.xmlrpc(listener, this.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);
}

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');


