var EXPORTED_SYMBOLS = ['AppIntent'];

const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");

XPCOMUtils.defineLazyGetter(this, "Tracker", function() {
  var obj = {};
  Cu.import("resource://livemargins/track.jsm", obj);
  return obj.Tracker;
});

XPCOMUtils.defineLazyGetter(this, "utils", function() {
  var obj = { };
  Cu['import']('resource://livemargins/utils.jsm', obj);
  return obj.utils;
});

XPCOMUtils.defineLazyGetter(this, "AppCenter", function() {
  var obj = { };
  Cu['import']('resource://livemargins/appcenter.jsm', obj);
  return obj.AppCenter;
});

XPCOMUtils.defineLazyGetter(this, "AppCenterDAO", function() {
  var obj = { };
  Cu['import']('resource://livemargins/dao.jsm', obj);
  return obj.AppCenterDAO;
});

XPCOMUtils.defineLazyGetter(this, "window", function() {
  return Cc["@mozilla.org/appshell/appShellService;1"]
       .getService(Ci.nsIAppShellService)
       .hiddenDOMWindow;
});

XPCOMUtils.defineLazyGetter(this, "SNS", function() {
  var obj = { };
  try {
    Cu["import"]("resource://share_all_cn/sns.jsm", obj);
  } catch (e) {
    let defSNS = {
      name: "sina",
      icon: "chrome://livemargins/skin/images/icons/sina.ico",
      height: 380,
      width: 630,
      getUrl: function(title, url, pic, extended) {
        return "http://v.t.sina.com.cn/share/share.php?appkey=1203606049&source=firefox&title="
          + window.encodeURIComponent(extended + "  ")
          + window.encodeURIComponent(title)
          + "&url=" + window.encodeURIComponent(url)
          + "&pic=" + window.encodeURIComponent(pic);
      }
    };
    return {
      getSNSPrefName: function() {
        return null;
      },

      getCurrentSNS: function() {
        return defSNS;
      },

      getSNSByName: function(name) {
        return defSNS;
      },

      installed: false
    };
  }
  return obj.SNS;
});

XPCOMUtils.defineLazyGetter(this, "_logger", function() {
  return new utils.logger("LM4.AppIntentModule");
});

XPCOMUtils.defineLazyGetter(this, "_stringBundle", function() {
  var stringBundleService = Cc['@mozilla.org/intl/stringbundle;1'].getService(Ci.nsIStringBundleService);
  return stringBundleService.createBundle("chrome://livemargins/locale/livemargins.properties");
});

function _log(msg) {
  _logger.trace(msg + "\n");
}

var _handlers = { };
var _activityQueue = { };

// init app intents
var _intents = {};

function _refreshIntents() {
  _intents = {
    "image/*": AppCenterDAO.getIntentsByType('image/*'),

    "text/*": AppCenterDAO.getIntentsByType('text/*', 6),

    "text/html": AppCenterDAO.getIntentsByType('text/html'),

    "application/link": AppCenterDAO.getIntentsByType('application/link')
  };
}
_refreshIntents();

function _replaceIntent(type, action, newIntent) {
  let _is = [];
  let _textIs = _intents[type];
  for (let i = 0; i < _textIs.length; i++) {
    let intent = _textIs[i];
    if (intent.action != action) {
      _is.push(intent);
    } else if (newIntent) {
      _is.push(newIntent);
    }
  }

  AppCenterDAO.updateAppIntent(newIntent);

  _intents[type] = _is;
}

function _getCurrentEngine() {
  let currentEngine = Cc["@mozilla.org/browser/search-service;1"]
              .getService(Ci.nsIBrowserSearchService)
              .currentEngine;
  let _engineIntent = null;
  if (currentEngine) {
    _engineIntent = {
      "icon": currentEngine.iconURI.spec,
      "action": "$app-search-engine$",
      "message": "search",
      "type": "text/*",
      "label": "Search using current engine",
      "labelStr": "intent.searchengine.label"
    };
  }

  _replaceIntent("text/*", "$app-search-engine$", _engineIntent);
}
_getCurrentEngine();

// Listen to the change of default search engine.
new utils.prefListener("browser.search.selectedEngine", function(branch, name) {
  _getCurrentEngine();
}).register();

let _updCurShareEntryTimeout = null;
function _updateCurrentShareEntry() {
  if (_updCurShareEntryTimeout) {
    window.clearTimeout(_updCurShareEntryTimeout);
    _updCurShareEntryTimeout = null;
  }

  // Set an timeout to make sure the intents can be shown more quickly.
  _updCurShareEntryTimeout = window.setTimeout(function() {
    _updCurShareEntryTimeout = null;
    _replaceIntent("text/*", "$app-share-tool$", {
      "icon": SNS.getCurrentSNS().icon,
      "action": "$app-share-tool$",
      "message": "share",
      "type": "text/*",
      "label": "Share selection",
      "labelStr": "intent.share.label"
    });

    _replaceIntent("application/link", "$app-share-tool$", {
      "icon": SNS.getCurrentSNS().icon,
      "action": "$app-share-tool$",
      "message": "share",
      "type": "application/link",
      "label": "Share link",
      "labelStr": "intent.share.label"
    });
  }, 1000);
}
_updateCurrentShareEntry();

if (SNS.getSNSPrefName()) {
  // Listen to the change of default search engine.
  new utils.prefListener(SNS.getSNSPrefName(), function(branch, name) {
    _updateCurrentShareEntry();
  }).register();
}

function _getRecentWindow() {
  var windowMediator = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator);
  var wnd = windowMediator.getMostRecentWindow('navigator:browser');
  if (!wnd) {
    wnd = windowMediator.getMostRecentWindow('');
  }
  return wnd;
}

/**
 * Share text, image, or link with the current SNS site.
 */
function _startShareActivity(activity) {
  let href = null;
  let sns = SNS.getCurrentSNS();
  switch (activity.data.type) {
  case "text/*":
    href = sns.getUrl(activity.data.title, activity.data.href, "", activity.data.text);
    break;
  case "image/*":
    href = sns.getUrl("", "", activity.data.imgsrc, "");
    break;
  case "application/link":
    href = sns.getUrl(activity.data.title, activity.data.href, "", "");
    break;
  }

  function _openInCenter(pageURL, title, w, h) {
    var left = (window.screen.width / 2) - (w / 2);
    var top = (window.screen.height / 2)-(h / 2);
    return window.open(pageURL, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=yes, copyhistory=no, width=' + w +', height=' + h + ', top=' + top + ', left=' + left);
  }

  if (sns.name == "mail") {
    let extProtocolSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].getService(Ci.nsIExternalProtocolService);
    let ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
    let uri = ioService.newURI(href, null, null);
    extProtocolSvc.loadUrl(uri);
  } else {
    _openInCenter(href, sns.name, sns.width, sns.height);
  }
}

/**
 * Search text with default search engine.
 */
function _startSearchEngineActivity(activity) {
  // Only handle activity with type 'text/*'
  if (activity.data.type != "text/*") {
    return;
  }

  let currentEngine = Cc["@mozilla.org/browser/search-service;1"]
              .getService(Ci.nsIBrowserSearchService)
              .currentEngine;
  if (null == currentEngine) {
    return;
  }

  _getRecentWindow().openUILinkIn(currentEngine.getSubmission(activity.data.text, null).uri.spec, "tab");
}

/**
 * Replace intent label by localized string.
 */
function _localizeLabel(intent) {
  if (intent.labelStr) {
    intent.label = _stringBundle.GetStringFromName(intent.labelStr);
  }
  return intent;
}

var AppIntent = {
  refreshIntents: function() {
    _refreshIntents();
    _getCurrentEngine();
    _updateCurrentShareEntry();
  },

  isSNSInstalled: function() {
    return SNS.installed !== false;
  },

  getIntentsByText: function(text, html) {
    var _html_intents = {};
    if (html) {
      _intents["text/html"].forEach(function(intent) {
        _html_intents[intent.action] = intent;
      });
    }
    var intents = [];
    _intents["text/*"].forEach(function(intent) {
      intent = _html_intents[intent.action] || intent;
      intents.push(_localizeLabel(intent));
    });
    return intents;
  },

  getIntentsByLink: function(text, href) {
    var intents = [];
    _intents["application/link"].forEach(function(intent) {
      intents.push(_localizeLabel(intent));
    });
    return intents;
  },

  getIntentsByImage: function(src) {
    var intents = [];
    _intents["image/*"].forEach(function(intent) {
      intents.push(_localizeLabel(intent));
    });

    return intents;
  },

  makeObjAccessible: function(obj) {
    return utils.exposeReadOnly(JSON.parse(JSON.stringify(obj)));
  },

  /**
   * interface Activity {
   *   string action;
   *   string message;
   *   string type;
   *   object data;
   *   void postResult(in object data);
   *   void postException(in string type, in string message);
   * }
   *
   * @param activity
   *
   * @see https://github.com/mozilla/openwebapps/blob/master/docs/ACTIVITIES.md
   *
   */
  startActivity: function(activity) {
    var appUID = activity.action;
    if (!appUID) {
      return;
    }

    switch (appUID) {
    case "$app-share-tool$":
      _startShareActivity(activity);
      break;
    case "$app-search-engine$":
      // search text with default search engine.
      _startSearchEngineActivity(activity);
      break;
    default:
      let handler = _getHandler(activity.action);
      if (!!handler) {
        // focus app window
        AppCenter.installAndOpenApp(appUID);
        handler(this.makeObjAccessible(activity.data), activity.message);
      } else {
        // App window may not be initialized, push activity to queue temporarily.
        _activityQueue[activity.action] = activity;
        AppCenter.installAndOpenApp(appUID);
      }
      break;
    }
    Tracker.track(Tracker.intent.type, Tracker.intent.actions.OPEN_INENT,
                  appUID, activity.from);
  },

  registerHandler: function(action, handler) {
    // for simplicity, only one handler is allowed.
    _handlers[action] = handler;
    // check if temporary activity exists.
    if (_activityQueue[action]) {
      let activity = _activityQueue[action];
      handler(this.makeObjAccessible(activity.data), activity.message)
      delete _activityQueue[action];
    }
  },

  unregisterHandler: function(action, handler) {
    delete _handlers[action];
  },

  localize: _stringBundle.GetStringFromName
};

function _getHandler(action) {
  return _handlers[action];
}

