/**
 * GMail web app script
 */
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;

var browser;
var poll;
var chromeWindow;
var glue;

function d(s) {
  dump("----------------------> " + s + "\n");
}

function GetXPathNode(xpath, context) {
  var doc = context.ownerDocument;
  var nsResolver = doc.createNSResolver(doc.documentElement);
  var result = doc.evaluate(xpath,
                            context,
                            nsResolver,
                            Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE,
                            null);
  return result.singleNodeValue;
}


function startup() {
}

function shutdown() {
  GoogleMail.shutdown();
}

function dropFiles(uris) {
  GoogleMail.drop(uris);
}

function load() {

  if (!browser) {
    var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
               .getService(Ci.nsIWindowMediator);
    chromeWindow = wm.getMostRecentWindow(null);
    browser = host.getBrowser();
    poll = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
    poll.initWithCallback({
      notify: function(timer) {
        var gm = browser.contentWindow.wrappedJSObject.gmonkey;
        if (!gm) {
          return;
        }
        poll.cancel();
        poll = null;
        gm.load("1.0", function(gmail) { GoogleMail.startup(gmail); });
      }
    }, 500, Ci.nsITimer.TYPE_REPEATING_SLACK);
  }

}

var GoogleMail = {
  _timer: null,
  _lastInboxUnread: 0,
  _lastLabelsUnread: null,
  _lastFocusInboxUnread: 0,
  _gmail: null,
  _compose: null,
  _dropUris: null,
  _chatManager: null,
  _pendingChats: 0,

  notify: function(timer) {

try {
    var icon = window.platform.icon();
    icon.badgeText = "hello";
}
catch(e) { d(e); }
    var list = [];
    var inboxUnread = this._getInboxUnread();
    var inboxNew = Math.max(inboxUnread - this._lastInboxUnread, 0);
    this._lastInboxUnread = inboxUnread;

    if (inboxNew > 0) {
      list.push(["Inbox", inboxNew]);
    }

    var labelsUnread = this._getLabelsUnread();

    // Sweep lastLabelsUnread for removed labels
    for (var label in this._lastLabelsUnread) {
      if (!(label in labelsUnread)) {
        delete this._lastLabelsUnread[label];
      }
    }

    for (var label in labelsUnread) {
      var lastLabelUnread = this._lastLabelsUnread[label] || 0;
      var labelNew = Math.max(labelsUnread[label] - lastLabelUnread, 0);
      this._lastLabelsUnread[label] = labelsUnread[label];

      if (labelNew > 0) {
        list.push([label, labelNew]);
      }
    }

    if (list.length > 0) {
      var message = "";
      for (var i = 0; i < list.length; i++) {
        message += list[i][1] + " new in " + list[i][0];
        if (i + 1 < list.length) {
          message += ", ";
        }
      }

      host.showAlert(host.getResource("mail28.png"),
                     "GMail",
                     "You have new email: " + message);
    }

    // If we are not focused, alert on new inbox messages
    if (!chromeWindow.document.hasFocus() &&
        inboxUnread > this._lastFocusInboxUnread) {
      d("get attention");
      host.getAttention();
    }

  },

  _onChat: function(from, text) {
    if (!chromeWindow.document.hasFocus()) {
      host.showAlert(host.getResource("mail28.png"), from, text);
      this._pendingChats++;
    }
  },

  _getInboxUnread: function() {
    var a = this._gmail.getSystemLabelsElement().getElementsByTagName("a");
    for (var i = 0; i < a.length; i++) {
      var href = a[i].getAttribute("href");
      if (href.indexOf("#inbox")) {
        var matches = a[i].getAttribute("title").match(/\((\d+)\)/);
        return matches ? matches[1] : 0;
      }
    }
    return 0;
  },

  _getLabelsUnread: function() {
    var o = {};
    var a = this._gmail.getNavPaneElement().getElementsByTagName("a");
    for (var i = 0; i < a.length; i++) {
      var href = a[i].getAttribute("href");
      var pos = href.indexOf("#label");
      if (pos >= 0) {
        var title = a[i].getAttribute("title");
        if (title.indexOf("(") >= 0) {
          var matches = title.match(/(.*)\s+\((\d+)\)/);
          if (matches) {
            o[matches[1]] = matches[2];
          }
        }
        else {
          o[title] = 0;
        }
      }
    }
    return o;
  },

  _getComposeMail: function() {
    var a = this._gmail.getNavPaneElement().getElementsByTagName("span");
    for (var i = 0; i < a.length; i++) {
      var role = a[i].getAttribute("role");
      if (role == "link") {
        return a[i];
      }
    }
    return null;
  },

  viewChanged: function() {

    var viewType = this._gmail.getActiveViewType();
    if (viewType == "co" && this._dropUris) {

      var view = this._gmail.getActiveViewElement();

      // Count how many blank file inputs are open so we don't add more
      // than we need
      var a = view.getElementsByTagName("input");
      var blankInputs = 0;
      for (var i = 0; i < a.length; i++) {
        if (a[i].type == "file" && a[i].value == "") {
          blankInputs++;
        }
      }

      var count = Math.max(this._dropUris.length - blankInputs, 0);

      var that = this;

      function doAdd(first) {

        if (!first) {
          count--;
        }

        if (count == 0) {

          // Add the files
          var a = view.getElementsByTagName("input");
          var uriNum = 0;
          for (var i = 0; i < a.length; i++) {
            if (a[i].type == "file" && a[i].value == "") {
              var f = that._dropUris[uriNum].QueryInterface(Ci.nsIFileURL);
              a[i].value = f.file.path;
              uriNum++;
            }
          }
          that._dropUris = null;
          return;
        }

        // Find the attach link
        var a = view.getElementsByTagName("span");
        for (var i = 0; i < a.length; i++) {
          var text = a[i].textContent;
          if (text.indexOf("Attach") >= 0) {

            // Dispatch a click to the attach span
            var event = browser.contentDocument.createEvent("Events");
            event.initEvent("click", true, true);
            a[i].dispatchEvent(event);

            // Call back to this same function so we can continue to add
            // attachments
            var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
            timer.initWithCallback({
              notify: function(timer) {
                doAdd(false);
              }
            }, 0, Ci.nsITimer.TYPE_ONE_SHOT);

            return;
          }
        }
      }

      doAdd(true);
    }
  },

  drop: function(uris) {
    if (this._dropUris) {
      Cu.reportError("Can't drop, drop in progress");
    }

    if (!(uris[0] instanceof Ci.nsIFileURL)) {
      return;
    }

    this._dropUris = uris;

    // If we're already composing, just add the file, otherwise switch first
    var viewType = this._gmail.getActiveViewType();
    if (viewType == "co") {
      this.viewChanged();
    }
    else {
      var event = browser.contentDocument.createEvent("Events");
      event.initEvent("click", true, true);
      this._compose.dispatchEvent(event);
    }
  },

  _focus: function() {
    this._lastFocusInboxUnread = this._getInboxUnread();
    this._pendingChats = 0;
    // clear message
  },

  _setUnreadMessage: function(unread) {

  },

  startup: function(gmail) {
    this._gmail = gmail;

    this._lastInboxUnread = 0;
    this._lastLabelsUnread = {};
    this._lastFocusInboxUnread = 0;
    this._compose = this._getComposeMail();
    this._gmail.registerViewChangeCallback(function() {
      GoogleMail.viewChanged();
    });

    // Send the first notification immediately
    this.notify();

    // kick off a polling timer to check for new mail
    this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
    this._timer.initWithCallback(this, 5000, Ci.nsITimer.TYPE_REPEATING_SLACK);

    chromeWindow.addEventListener("focus", function(e) {
      GoogleMail._focus();
    }, true);

    this._chatManager =
      new ChatManager(browser.contentDocument, function(from, text) {
        GoogleMail._onChat(from, text);
      });
  },

  shutdown: function() {
    if (this._timer) {
      this._timer.cancel();
      this._timer = null;
    }

    if (this._chatManager) {
      this._chatManager.shutdown();
    }

  }
};

function ChatManager(doc, callback) {
  this._doc = doc;
  this._callback = callback;
  this._div = this._getChatDiv();
  this._chats = [];

  this._div.addEventListener("DOMNodeInserted", this, true);
  this._div.addEventListener("DOMNodeRemoved", this, true);
}

ChatManager.prototype = {

  _getChatDiv: function() {
    return GetXPathNode("//DIV[@style='float: right;']",
                        this._doc.documentElement);
  },

  handleEvent: function(e) {
    var t = e.target;

    if (e.type == "DOMNodeInserted" && t.parentNode == this._div) {
      this._chats.push(new Chat(t, this._callback));
      return;
    }

    if (e.type == "DOMNodeRemoved" && t.parentNode == this._div) {
      for (var i = 0; i < this._chats.length; i++) {
        if (this._chats[i].div == t) {
          this._chats[i].close();
          this._chats.splice(i, 1);
          return;
        }
      }
    }

  },

  shutdown: function() {
    this._div.removeEventListener("DOMNodeInserted", this, true);
    this._div.removeEventListener("DOMNodeRemoved", this, true);
  }
}

function Chat(div, callback) {
  this.div = div;
  this._callback = callback;
  this._doc = this.div.ownerDocument;
  this._lastMessageDiv = null;
  this.div.addEventListener("DOMNodeInserted", this, true);
}

Chat.prototype = {
  handleEvent: function(e) {
    var t = e.target;
    if (t instanceof Ci.nsIDOMElement && t.tagName == "DIV") {

      var messageDiv = GetXPathNode(".//DIV[@chat-dir][position() = last()]/DIV[position() = last()]",
                                    this.div);
      if (!messageDiv || messageDiv == this._lastMessageDiv) {
        return;
      }

      var container = messageDiv.parentNode;
      if (container.getAttribute("chat-dir") == "t") {

        var firstMessageDiv = GetXPathNode("./DIV[1]", container);
        var from = GetXPathNode("./SPAN[1]", firstMessageDiv).textContent;
        from = from.substr(0, from.length - 2);

        var text;
        if (messageDiv == firstMessageDiv) {
          text = GetXPathNode("./SPAN[2]", firstMessageDiv).textContent;
        }
        else {
          text = messageDiv.textContent;
        }

        this._callback.apply(null, [from, text]);
        this._lastMessageDiv = messageDiv;
      }
    }
  },

  close: function(e) {
    this.div.removeEventListener("DOMNodeInserted", this, true);
  }

}
