// vim: ts=2 sw=2 expandtab cindent
//
// BEGIN FLOCK GPL
//
// Copyright Flock Inc. 2005-2008
// http://flock.com
//
// This file may be used under the terms 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 Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;

Cu.import("resource:///modules/FlockSvcUtils.jsm");
Cu.import("resource:///modules/FlockStringBundleHelpers.jsm");

const XANGA_CID = Components.ID("{4a67cc66-aafa-4df6-a836-c63bc92651e7}");
const XANGA_CONTRACTID = "@flock.com/people/xanga;1";
const XANGA_FAVICON = "chrome://flock/content/services/xanga/favicon.png";
const SERVICE_ENABLED_PREF = "flock.service.xanga.enabled";
const CATEGORY_COMPONENT_NAME = "Xanga JS Component"
const CATEGORY_ENTRY_NAME = "xanga"


// ====================================================
// ========== BEGIN General helper functions ==========
// ====================================================

var gCompTK;
function getCompTK() {
  if (!gCompTK) {
    gCompTK = Components.classes["@flock.com/singleton;1"]
                        .getService(Components.interfaces.flockISingleton)
                        .getSingleton("chrome://flock/content/services/common/load-compTK.js")
                        .wrappedJSObject;
  }
  return gCompTK;
}


function loadSubScript(spec) {
  var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
  var context = {};
  loader.loadSubScript(spec, context);
  return context;
}


// ================================================
// ========== BEGIN flockXGService class ==========
// ================================================

function flockXGService()
{
  var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
               .getService(Ci.mozIJSSubScriptLoader);
  loader.loadSubScript("chrome://browser/content/utilityOverlay.js");
  var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
  obs.addObserver(this, "xpcom-shutdown", false);
  this.mIsInitialized = false;
  this._ctk = {
    interfaces: [
      "nsISupports",
      "nsIClassInfo",
      "nsISupportsCString",
      "nsIObserver",
      "flockIWebService",
      "flockILoginWebService",
      "flockIBlogWebService",
      "flockIMigratable"
    ],
    shortName: "xanga",
    fullName: "Xanga",
    description: "Flock Xanga Service",
    favicon: XANGA_FAVICON,
    CID: XANGA_CID,
    contractID: XANGA_CONTRACTID,
    accountClass: flockXGAccount
  };
  this._profiler = Cc["@flock.com/profiler;1"].getService(Ci.flockIProfiler);
  this.init();

  FlockSvcUtils.flockIWebService.addDefaultMethod(this, "url");
  FlockSvcUtils.flockIWebService.addDefaultMethod(this, "getStringBundle");
  FlockSvcUtils.flockILoginWebService.addDefaultMethod(this, "loginURL");
}


// BEGIN nsIObserver
flockXGService.prototype.observe =
function flockXGService_observe(subject, topic, state)
{
  switch (topic) {
    case "xpcom-shutdown":
      var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
      obs.removeObserver(this, "xpcom-shutdown");
      return;
  }
}
// END nsIObserver


// BEGIN flockIWebService
flockXGService.prototype.addAccount =
function flockXGService_addAccount(aUsername, aIsTransient, aFlockListener)
{
  this.logger.info("addAccount('"+aUsername+"')");
  var accountURN = this.acUtils.createAccount(this,
                                              aUsername.toLowerCase(),
                                              aUsername,
                                              null,
                                              aIsTransient);
  var c_account = this.faves_coop.get(accountURN);

  // add blog
  var c_blog = new this.faves_coop.Blog(accountURN+":uniqueblog", {
    name: aUsername,
    title: aUsername,
    blogid: aUsername,
    apiLink: "",
    URL: "http://www.xanga.com/private/yourhome.aspx?user="+aUsername,
  });
  c_account.children.addOnce(c_blog);

  var acct = this.getAccount(accountURN);
  if (aFlockListener) {
    aFlockListener.onSuccess(acct, "addAccount");
  }
  return acct;
}


flockXGService.prototype.init =
function flockXGService_init()
{
  FlockSvcUtils.getLogger(this).debug(".init()");

  // Prevent re-entry
  if (this.mIsInitialized) return;
  this.mIsInitialized = true;

  var evtID = this._profiler.profileEventStart("xanga-init");

  this.prefService = Components.classes["@mozilla.org/preferences-service;1"]
                               .getService(Components.interfaces.nsIPrefBranch);
  if ( this.prefService.getPrefType(SERVICE_ENABLED_PREF) &&
       !this.prefService.getBoolPref(SERVICE_ENABLED_PREF) )
  {
    this._logger.info("Pref " + SERVICE_ENABLED_PREF
                              + " set to FALSE... not initializing.");
    var catMgr = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
    catMgr.deleteCategoryEntry("wsm-startup", CATEGORY_COMPONENT_NAME, true);
    catMgr.deleteCategoryEntry("flockWebService", CATEGORY_ENTRY_NAME, true);
    catMgr.deleteCategoryEntry("flockMigratable", CATEGORY_ENTRY_NAME, true);
    return;
  }

  // Logger
  this.logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
  this.logger.init("xanga");

  // Attributes of flockIBlogWebService
  this.supportsCategories = 0;
  this.supportsPostReplace = false;
  this.metadataOverlay = "chrome://flock/content/services/xanga/xangaOverlay.xul";

  this.acUtils = Cc["@flock.com/account-utils;1"].getService(Ci.flockIAccountUtils);

  this.faves_coop = Cc["@flock.com/singleton;1"]
                    .getService(Ci.flockISingleton)
                    .getSingleton("chrome://flock/content/common/load-faves-coop.js")
                    .wrappedJSObject;

  this.account_root = this.faves_coop.accounts_root;

  this.xgService = new this.faves_coop.Service('urn:xanga:service');
  this.xgService.name = 'xanga';
  this.xgService.desc = 'The Xanga.com Service';
  this.xgService.domains = "xanga.com";
  this.xgService.serviceId = XANGA_CONTRACTID;

  var xgHomepage = new this.faves_coop.Favorite('urn:xanga:actions:homepage');
  xgHomepage.name = 'Xanga.com';
  xgHomepage.URL = 'http://www.xanga.com';

  this.xgService.children.addOnce(xgHomepage);
  this.urn = this.xgService.id();

  this.webDetective = this.acUtils.useWebDetective("xanga.xml");

  // Initialize member that specifies path for localized string bundle
  this._stringBundlePath = "";

  this._profiler.profileEventEnd(evtID, "");
}


flockXGService.prototype.refresh =
function flockXGService_refresh(aURN, aPollingListener)
{
	debug("flockXGService refresh with aURN of "+aURN+"\n");

	// Introspection against what we're syncing - Identity or Account or Item?
	var refreshItem = this.faves_coop.get(aURN);

  if (refreshItem instanceof this.faves_coop.Account) {
	}
  else if (refreshItem instanceof this.faves_coop.Favorite) {
	}
  else {
		throw Components.results.NS_ERROR_ABORT;
	}
}

flockXGService.prototype.refreshAccount =
function flockXGService_refreshAccount(aURN, aPollingListener)
{
  debug("XGService - refreshAccount with aURN of "+aURN);
}


// BEGIN flockIBlogWebService interface

flockXGService.prototype.newPost =
function(aPublishListener, aBlogId, aPost, aPublish, aNotifications) {
  dump("==== newPost in Xanga\n");
/*  var authenticated = false;
  var enum = this.faves_coop.accounts_root.children.enumerate();
  while (enum.hasMoreElements()) {
    var account = enum.getNext();
    if (!account) continue; // getNext() can return NULL when hasMoreElements() is TRUE.
    if ((account.serviceId == XANGA_CONTRACTID) && (account.isAuthenticated))
      authenticated = true;
  }

  if (!authenticated) {
    var error = Components.classes['@flock.com/error;1']
                          .createInstance(Components.interfaces.flockIError);
    error.errorCode = error.BLOG_LOGIN_REQUIRED;
    aPublishListener.onError(error);
    return;
  }*/

  var notifsArray = new Array();
  // var html = aPost.description + addTechnoratiTags(aPost.tags);
  while (aNotifications.hasMore())
    notifsArray.push(aNotifications.getNext());
  //var comments = aPost.extra.getNext();
  //var privacy = aPost.extra.getNext();
  //var mood = aPost.extra.getNext().split('||');

  var textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"].getService(Components.interfaces.nsITextToSubURI);
  var tags = "";
  while (aPost.tags.hasMore())
    tags += (aPost.tags.getNext() + '%2C');
  var privacy = aPost.extra.getNext();
  var comments = aPost.extra.getNext();

  function postForReal(aToken) {
    var postMessage = '__EVENTTARGET='+
                      '&__EVENTARGUMENT='+
                      '&__VIEWSTATE=' + aToken +
                      '&txtTitle=' + textToSubURI.ConvertAndEscape("UTF-8", aPost.title) +
                      '&spellcheckbtn=false' +
                      '&RadEContentTextareawerichtext=' +
                      '&werichtext=++++++++++++++++++++++++++++++++++++++++++++++++' +
                      '&tnames=' +
                      '&ptag=tagname' +
                      '&proftitle0=' +
                      '&proftitle1=' +
                      '&proftitle3=' +
                      '&xztitle1=' +
                      '&xztitle2=' +
                      '&xzasin1=' +
                      '&xzformat=' +
                      '&xzextravalue=' +
                      '&weprivacy=' + privacy
                      '&txtRating=';
    if (comments == "on")
      postMessage += '&chkComments=on';
    postMessage += '&btnSave=Save+Changes' +
                      '&epropcurrstate=0' +
                      '&commentcnt=0' +
                      '&weuniqueid=0' +
                      '&webgcolor=' +
                      '&webdcolor=' +
                      '&tprefid=29385413' +
                      '&tpref2=' +
                      '&tpref3=' +
                      '&tpref4=' +
                      '&tpref5=' +
                      '&cncel=' +
                      '&postvalues=' +
                      '&photovalues=' +
                      '&videovalues=' +
                      '&audiovalues=' +
                      '&bodyvalue=' + textToSubURI.ConvertAndEscape("UTF-8", aPost.description.replace(/\n/g, "<br/>")) +
                      '&mediaargs=' +
                      '&tagsoriginal=' +
                      '&tagstodelete=%2C' +
                      '&tagstoadd=%2C' + tags;

    var xhr2 = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance (Ci.nsIXMLHttpRequest);
    xhr2.mozBackgroundRequest = true;
    xhr2.open ('POST', 'http://www.xanga.com/private/editorx.aspx', true);
    xhr2.setRequestHeader ('Content-Type', 'application/x-www-form-urlencoded');
    xhr2.setRequestHeader ('Content-Length', postMessage.length );
    xhr2.setRequestHeader ('Referer', 'http://www.xanga.com/private/editorx.aspx');

    xhr2.onreadystatechange = function (aEvent) {
      debug("readyState: "+xhr2.readyState);
      if (xhr2.readyState == 4) {
        debug("status: "+xhr2.status);
        if ((xhr2.status != 200) && (xhr2.status != 302)) {
          aPublishListener.onError(null, xhr2.statusText);
          return;
        }
        debug(xhr2.responseText);
        aPublishListener.onResult("");
      }
    }

    dump(" *** Post FOR REAL!!...\n");

    //dump(postMessage);
    xhr2.send(postMessage);
  }

  function getViewState() {
    // Show the editor to get the "viewState" token
    var xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
    xhr.mozBackgroundRequest = true;
    xhr.open('GET', 'http://www.xanga.com/private/editorx.aspx', true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

    xhr.onreadystatechange = function (aEvent) {
      if (xhr.readyState == 4) {
        if (xhr.status != 200) {
          aPublishListener.onError(null, xhr.statusText);
          return;
        }
        //dump(xhr.responseText);

        if (xhr.responseText.match("PLEASE SIGN IN")) {
          var error = Components.classes['@flock.com/error;1']
                          .createInstance(Ci.flockIError);
          error.errorCode = error.BLOG_LOGIN_REQUIRED;
          aPublishListener.onError(error);
          return;
        }
        // Get the token and post for real
        var theHash = xhr.responseText.match(/(?:id\=\"\__VIEWSTATE\"\svalue\=\")(.+)(?=\")/);
        debug("theHash: "+theHash+"\n");
        debug("theHash[1]: "+theHash[1]+"\n");

        if (theHash != null){
          var token = theHash[1];
          debug("*****\n*****\n TOKEN: "+token+"\n");
          postForReal(token);
        }
      }
    }
    xhr.send(null);
  }

  getViewState();
}

flockXGService.prototype.editPost =
function flockXGService_editPost(aPublishListener,
                                 aBlogId,
                                 aPost, 
                                 aPublish,
                                 atomid,
                                 editURI,
                                 aNotifications)
{
}

flockXGService.prototype.deletePost = function(aListener, aBlogId, aPostid){
}

flockXGService.prototype.getUsersBlogs =
function flockXGService_getUsersBlogs(aBlogListener,
                                      aAPILink,
                                      aUsername,
                                      aPassword)
{
}

flockXGService.prototype.getRecentPosts =
function flockXGService_getRecentPosts(aBlogListener, aBlogId, aNumber) {
}

flockXGService.prototype.getCategoryList =
function flockXGService_getCategoryList(aBlogListener, aBlogId) {
}
// END flockIBlogWebService interface


// BEGIN flockILoginWebService interface
flockXGService.prototype.getAccountIDFromDocument =
function flockXGService_getAccountIDFromDocument(aDocument) {
  this.logger.debug(".getAccountIDFromDocument(...)");
  var results = FlockSvcUtils.newResults();
  if (this.webDetective.detect("xanga", "accountinfo", aDocument, results)) {
    return results.getPropertyAsAString("accountid").toLowerCase();
  }
  return null;
};

flockXGService.prototype.updateAccountStatusFromDocument =
function flockXGService_updateAccountStatusFromDocument(aDocument,
                                                        aAcctURN,
                                                        aFlockListener)
{
  this.logger.debug("updateAccountStatusFromDocument('" + aAcctURN + "')");
  if (aAcctURN) {
    // We're logged in to this account, but still need to grab the avatar URL
    this.acUtils.ensureOnlyAuthenticatedAccount(aAcctURN);
    var results = Cc["@mozilla.org/hash-property-bag;1"]
                  .createInstance(Ci.nsIWritablePropertyBag2);
    if (this.webDetective
            .detect("xanga", "avatarURLDetect", aDocument, results))
    {
      var avatarURL = null;
      try {
        avatarURL = results.getPropertyAsAString("avatarURL");
      } catch(e) {
        this.logger.debug("No avatar found");
      }
      if (avatarURL) {
        var c_acct = this.faves_coop.get(aAcctURN);
        c_acct.avatar = avatarURL;
      }
    }
  } else if (this.webDetective.detect("xanga", "loggedout", aDocument, null)) {
    // We're logged out (of all accounts)
    this.logger.debug("WOOT WOOT Xanga user logged OUT!");
    this.acUtils.markAllAccountsAsLoggedOut(XANGA_CONTRACTID);
  }
}
// END flockILoginWebService interface

/**************************************************************************
 * Flock flockXGService Service: flockIMigratable Implementation
 **************************************************************************/
flockXGService.prototype.__defineGetter__("migrationName",
function flockXGService_getter_migrationName() {
  return flockGetString("common/migrationNames", "migration.name.xanga");
});

// boolean needsMigration(in string oldVersion);
flockXGService.prototype.needsMigration =
function flockXGService_needsMigration(aOldVersion) {
  var versionChecker = Cc["@mozilla.org/xpcom/version-comparator;1"]
                       .getService(Ci.nsIVersionComparator);
  return (versionChecker.compare(aOldVersion, "2.0b4") < 0);
}

// nsISupports startMigration(in string aOldVersion,
//                            in flockIMigrationProgressListener aListener);
flockXGService.prototype.startMigration =
function flockXGService_startMigration(aOldVersion,
                                     aFlockMigrationProgressListener) {
  var ctxt = {
    oldVersion: aOldVersion,
    listener: aFlockMigrationProgressListener
  };

  return { wrappedJSObject: ctxt };
};

// boolean doMigrationWork(in nsISupports aMigrationContext);
flockXGService.prototype.doMigrationWork =
function flockXGService_doMigrationWork(aMigrationContext) {
  this.logger.debug("doMigrationWork");
  var accountsEnum = this.getAccounts();
  while (accountsEnum.hasMoreElements()) {
    var account = accountsEnum.getNext()
                              .QueryInterface(Ci.flockIWebServiceAccount);
    var coopAccount = this.faves_coop.get(account.urn);

    // Lowercase the accountId for those accounts with a mixed case accountId.
    if (coopAccount.accountId != coopAccount.accountId.toLowerCase()) {
      coopAccount.accountId = coopAccount.accountId.toLowerCase();
    }

    var coopAccountId = coopAccount.id();
    // Lowercase the urn for those accounts with a mixed case urn.
    if (coopAccountId != coopAccountId.toLowerCase()) {
      coopAccount.changeId(coopAccountId.toLowerCase());
      // Lowercase the account urns for blogs associated with the account.
      var blogsEnum = coopAccount.children.enumerate();
      while (blogsEnum.hasMoreElements()) {
        var blog = blogsEnum.getNext();
        if ((blog.isInstanceOf(this.faves_coop.Blog)) &&
            (blog.id() != blog.id().toLowerCase()))
        {
          blog.changeId(blog.id().toLowerCase());
        }
      }
    }
  }
  return false;
};

// void finishMigration(in nsISupports aMigrationContext);
flockXGService.prototype.finishMigration =
function flockXGService_finishMigration(aMigrationContext) {
};

// ================================================
// ========== BEGIN XPCOM Module support ==========
// ================================================

function createModule(aParams) {
  return {
    registerSelf: function (aCompMgr, aFileSpec, aLocation, aType) {
      var aCompMgr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
      aCompMgr.registerFactoryLocation( aParams.CID, aParams.componentName,
                                        aParams.contractID, aFileSpec,
                                        aLocation, aType );
      var catMgr = Cc["@mozilla.org/categorymanager;1"]
        .getService(Ci.nsICategoryManager);
      if (!aParams.categories) { aParams.categories = []; }
      for (var i = 0; i < aParams.categories.length; i++) {
        var cat = aParams.categories[i];
        catMgr.addCategoryEntry( cat.category, cat.entry,
                                 cat.value, true, true );
      }
    },
    getClassObject: function (aCompMgr, aCID, aIID) {
      if (!aCID.equals(aParams.CID)) { throw Cr.NS_ERROR_NO_INTERFACE; }
      if (!aIID.equals(Ci.nsIFactory)) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; }
      return { // Factory
        createInstance: function (aOuter, aIID) {
          if (aOuter != null) { throw Cr.NS_ERROR_NO_AGGREGATION; }
          var comp = new aParams.componentClass();
          if (aParams.implementationFunc) { aParams.implementationFunc(comp); }
          return comp.QueryInterface(aIID);
        }
      };
    },
    canUnload: function (aCompMgr) { return true; }
  };
}

// NS Module entrypoint
function NSGetModule(aCompMgr, aFileSpec) {
  return createModule({
    componentClass: flockXGService,
    CID: XANGA_CID,
    contractID: XANGA_CONTRACTID,
    componentName: CATEGORY_COMPONENT_NAME,
    implementationFunc: function (aComp) { getCompTK().addAllInterfaces(aComp); },
    categories: [
      { category: "wsm-startup", entry: CATEGORY_COMPONENT_NAME, value: XANGA_CONTRACTID },
      { category: "flockWebService", entry: CATEGORY_ENTRY_NAME, value: XANGA_CONTRACTID },
      { category: "flockMigratable", entry: CATEGORY_ENTRY_NAME, value: XANGA_CONTRACTID }
    ]
  });
}

// ========== END XPCOM Module support ==========



// ================================================
// ========== BEGIN flockXGAccount class ==========
// ================================================


function flockXGAccount()
{
  this.logger = Cc["@flock.com/logger;1"].createInstance(Ci.flockILogger);
  this.logger.init("xangaAccount");
  this.faves_coop = Cc["@flock.com/singleton;1"]
                    .getService(Ci.flockISingleton)
                    .getSingleton("chrome://flock/content/common/load-faves-coop.js")
                    .wrappedJSObject;
  this.acUtils = Cc["@flock.com/account-utils;1"].getService(Ci.flockIAccountUtils);
  this._ctk = {
    interfaces: [
      "nsISupports",
      "flockIWebServiceAccount",
      "flockIBlogAccount"
    ]
  };
  getCompTK().addAllInterfaces(this);
}


// flockIBlogAccount implementation
flockXGAccount.prototype.getBlogs = function() {
  this.logger.info("{flockIBlogAccount}.getBlogs()");
  var blogsEnum = {
    QueryInterface : function(iid) {
      if (!iid.equals(Ci.nsISupports) &&
          !iid.equals(Ci.nsISimpleEnumerator))
      {
        throw Components.results.NS_ERROR_NO_INTERFACE;
      }
      return this;
    },
    hasMoreElements : function() {
      return false;
    },
    getNext : function() {
    }
  };
  return blogsEnum;
}
