// vim: ts=2 sw=2 expandtab cindent
//
// BEGIN FLOCK GPL
//
// Copyright Flock Inc. 2005-2009
// 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/FlockXPCOMUtils.jsm");
FlockXPCOMUtils.debug = false;
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 XANGA_TITLE = "Flock Xanga Service"
const CLASS_NAME = "Xanga"
const CLASS_SHORT_NAME = "xanga"
const MODULE_NAME = "xanga"

const METADATA_OVERLAY = "chrome://flock/content/services/xanga/xangaOverlay.xul";
const EDITOR_URL = "http://www.xanga.com/private/editorx.aspx";
const BLOG_URL = "http://www.xanga.com/private/yourhome.aspx?user=%username%";

/*************************************************************************
 * General Helpers
/*************************************************************************/

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

/*************************************************************************
 * Component: flockXGService
/*************************************************************************/

function flockXGService()
{
  var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
               .getService(Ci.mozIJSSubScriptLoader);
  loader.loadSubScript("chrome://browser/content/utilityOverlay.js");
  this.mIsInitialized = false;
  this._profiler = Cc["@flock.com/profiler;1"].getService(Ci.flockIProfiler);

  this._accountClassCtor = flockXGAccount;

  FlockSvcUtils.getLogger(this);

  var ws = FlockSvcUtils.flockIWebService;
  ws.addDefaultMethod(this, "url");
  ws.addDefaultMethod(this, "isHidden");
  ws.addDefaultMethod(this, "getStringBundle");

  var lws = FlockSvcUtils.flockILoginWebService;
  lws.addDefaultMethod(this, "loginURL");
  lws.addDefaultMethod(this, "tooltip");
  lws.addDefaultMethod(this, "getAccount");
  lws.addDefaultMethod(this, "getAuthenticatedAccount");
  lws.addDefaultMethod(this, "getAccountUrns");
  lws.addDefaultMethod(this, "logout");
  lws.addDefaultMethod(this, "removeAccount");
  lws.addDefaultMethod(this, "ownsDocument");
  lws.addDefaultMethod(this, "ownsLoginForm");
  lws.addDefaultMethod(this, "getSessionValue");
  lws.addDefaultMethod(this, "getCredentialsFromForm");
  lws.addDefaultMethod(this, "getAccountIDFromDocument");
  lws.addDefaultMethod(this, "docRepresentsSuccessfulLogin");

  this.init();
}

/*************************************************************************
 * flockXGService: XPCOM Component Creation
 *************************************************************************/

flockXGService.prototype = new FlockXPCOMUtils.genericComponent(
  XANGA_TITLE,
  XANGA_CID,
  XANGA_CONTRACTID,
  flockXGService,
  Ci.nsIClassInfo.SINGLETON,
  [
    Ci.flockIWebService,
    Ci.flockILoginWebService,
    Ci.flockIBlogWebService,
    Ci.flockIMigratable
  ]
);

// FlockXPCOMUtils.genericModule() categories
flockXGService.prototype._xpcom_categories = [
  { category: "wsm-startup" },
  { category: "flockWebService", entry: CLASS_SHORT_NAME },
  { category: "flockMigratable", entry: CLASS_SHORT_NAME }
];

/*************************************************************************
 * flockXGService: flockIWebService implementation
 *************************************************************************/

// readonly attribute AString contractId;
// ALMOST duplicated by nsIClassInfo::contractID
// with different case: contractId != contractID
flockXGService.prototype.contractId = XANGA_CONTRACTID;

// readonly attribute AString icon;
flockXGService.prototype.icon = XANGA_FAVICON;

// readonly attribute boolean needPassword;
flockXGService.prototype.needPassword = false;

// readonly attribute AString shortName;
flockXGService.prototype.shortName = CLASS_SHORT_NAME;

// readonly attribute AString title;
flockXGService.prototype.title = CLASS_NAME;

// readonly attribute AString urn;
flockXGService.prototype.urn = "urn:" + CLASS_SHORT_NAME + ":service";

flockXGService.prototype.addAccount =
function flockXGService_addAccount(aUsername, aIsTransient, aFlockListener)
{
  this._logger.info("addAccount('" + aUsername + "')");
  var accountURN = this._acUtils.createAccount(this,
                                               aUsername,
                                               aUsername,
                                               null,
                                               aIsTransient);
  var account = this.getAccount(accountURN);
  account.setParam("service", this._coopObj);

  // add blog
  var c_blog = new this._coop.Blog(accountURN + ":uniqueblog", {
    name: aUsername,
    title: aUsername,
    blogid: aUsername,
    apiLink: "",
    URL: this._blogURL.replace("%username%", aUsername)
  });
  account.coopObj.children.addOnce(c_blog);

  if (aFlockListener) {
    aFlockListener.onSuccess(account, "addAccount");
  }
  return account;
}


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

  // getWD() also adds the "_acUtils" property.
  FlockSvcUtils.getWD(this);
  this.domains = "xanga.com";
  FlockSvcUtils.initService(this);

  // 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;
  }

  this.account_root = this._coop.accounts_root;

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

  this._coopObj.children.addOnce(xgHomepage);

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

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

  this._editorURL = this._WebDetective.getString("xanga",
                                                 "editorURL",
                                                 EDITOR_URL);
  this._blogURL = this._WebDetective.getString("xanga",
                                               "blogURL",
                                               BLOG_URL);
  this._postBody = this._WebDetective.getString("xanga", "postBody", null);
}

/*************************************************************************
 * flockXGService: flockIBlogService implementation
 *************************************************************************/

flockXGService.prototype.supportsCategories = Ci.flockIBlogWebService.CATEGORIES_NO;

flockXGService.prototype.supportsPostReplace = false;

flockXGService.prototype.metadataOverlay = METADATA_OVERLAY;

flockXGService.prototype.authtoken = null;

flockXGService.prototype.setPostCategories =
function flockXGService_setPostCategories(aPublishListener,
                                          aBlogId,
                                          aPostId,
                                          aCategories) {
  inst._logger.error("Xanga doesn't support categories!");
  throw(CR.NS_ERROR_NOT_IMPLEMENTED);
};

flockXGService.prototype.newPost =
function(aPublishListener, aBlogId, aPost, aPublish, aNotifications) {
  var notifsArray = new Array();
  while (aNotifications.hasMore()) {
    notifsArray.push(aNotifications.getNext());
  }

  var textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"]
                     .getService(Ci.nsITextToSubURI);
  var tags = "";
  while (aPost.tags.hasMore()) {
    tags += (aPost.tags.getNext() + "%2C");
  }
  var privacy = aPost.extra.getNext();
  var comments = aPost.extra.getNext();
  var title = textToSubURI.ConvertAndEscape("UTF-8", aPost.title);
  var body = textToSubURI.ConvertAndEscape("UTF-8",
                                           aPost.description
                                                .replace(/\n/g, "<br />"));
  var svc = this;
  function postForReal(aViewState, aEventValidation) {
    var postMessage =
      svc._postBody.replace("%token%", aViewState)
                   .replace("%validation%", aEventValidation)
                   .replace("%title%", title)
                   .replace("%privacy%", privacy)
                   .replace("%comments%", comments == "on" ? "&chkComments=on"
                                                           : "")
                   .replace("%body%", body)
                   .replace("%tags%", tags);

    var xhr2 = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
               .createInstance(Ci.nsIXMLHttpRequest);
    xhr2.mozBackgroundRequest = true;
    xhr2.open("POST", svc._editorURL, true);
    xhr2.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xhr2.setRequestHeader("Content-Length", postMessage.length );
    xhr2.setRequestHeader("Referer", svc._editorURL);

    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("");
      }
    }

    xhr2.send(postMessage);
  }

  function getViewState() {
    // Show the editor to get the "viewState" token and the validation
    var xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
    xhr.mozBackgroundRequest = true;
    xhr.open("GET", svc._editorURL, 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;
        }
        var results = Cc["@mozilla.org/hash-property-bag;1"]
                      .createInstance(Ci.nsIWritablePropertyBag2);
        if (svc._WebDetective.detectNoDOM("xanga",
                                          "editorForm",
                                          "",
                                          xhr.responseText,
                                          results))
        {
          var viewstate, eventvalidation;
          if (results.hasKey("viewstate") &&
              results.getPropertyAsAString("viewstate") != "")
          {
            viewstate = results.getPropertyAsAString("viewstate");
            viewstate = encodeURIComponent(viewstate);
          }
          if (results.hasKey("eventvalidation") &&
              results.getPropertyAsAString("eventvalidation") != "")
          {
            eventvalidation = results.getPropertyAsAString("eventvalidation");
            eventvalidation = encodeURIComponent(eventvalidation);
          }
        }
        postForReal(viewstate, eventvalidation);
      }
    }
    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) {
}

/*************************************************************************
 * flockXGService: flockILoginWebService implementation
 *************************************************************************/

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,
                                                        aAccount,
                                                        aFlockListener)
{
  if (aAccount) {
    // We're logged in to this account, but still need to grab the avatar URL
    this._acUtils.ensureOnlyAuthenticatedAccount(aAccount);
    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) {
        aAccount.setParam("avatar", avatarURL);
      }
    }
  } else if (this._WebDetective.detect("xanga", "loggedout", aDocument, null)) {
    // We're logged out (of all accounts)
    this._logger.debug("Xanga user logged OUT!");
    this._acUtils.markAllAccountsAsLoggedOut(this);
  }
}

/**************************************************************************
 * 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 accountUrns = this.getAccountUrns();
  while (accountUrns.hasMore()) {
    var coopAccount = this._coop.get(accountUrns.getNext());

    // 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._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) {
};


/**************************************************************************
 * Flock flockXGAccount
 **************************************************************************/

function flockXGAccount() {
  FlockSvcUtils.getLogger(this).init(CLASS_SHORT_NAME + ":Account");
  FlockSvcUtils.getCoop(this);
  FlockSvcUtils.getACUtils(this);

  this.service = Cc[XANGA_CONTRACTID].getService(Ci.flockIBlogWebService)

  var wsa = FlockSvcUtils.flockIWebServiceAccount;
  wsa.addDefaultMethod(this, "getService");
  wsa.addDefaultMethod(this, "keep");
  wsa.addDefaultMethod(this, "getParam");
  wsa.addDefaultMethod(this, "setParam");
  wsa.addDefaultMethod(this, "getCustomParam");
  wsa.addDefaultMethod(this, "getAllCustomParams");
  wsa.addDefaultMethod(this, "logout");
  wsa.addDefaultMethod(this, "setCustomParam");
  wsa.addDefaultMethod(this, "isAuthenticated");
}

/**************************************************************************
 * Flock BL Account: XPCOM Component Creation
 **************************************************************************/

flockXGAccount.prototype = new FlockXPCOMUtils.genericComponent(
  CLASS_NAME + " Account",
  "",
  "",
  flockXGAccount,
  0,
  [
    Ci.flockIWebServiceAccount,
    Ci.flockIBlogAccount
  ]
);

/*************************************************************************
 * XPCOM Support - Module Construction
 *************************************************************************/

// Create array of components.
var componentsArray = [flockXGService];

// Generate a module for XPCOM to find.
var NSGetModule = FlockXPCOMUtils.generateNSGetModule(MODULE_NAME,
                                                      componentsArray);
