/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

const { classes: Cc, interfaces: Ci, utils: Cu } = Components;

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

this.EXPORTED_SYMBOLS = ["Notifications"];

function log(msg) {
  // Services.console.logStringMessage(msg);
}

var _notificationsMap = {};
var _handlersMap = {};

function Notification(aId, aOptions) {
  this._id = aId;
  this._when = (new Date()).getTime();
  this.fillWithOptions(aOptions);
}

Notification.prototype = {
  fillWithOptions: function(aOptions) {
    if ("icon" in aOptions && aOptions.icon != null)
      this._icon = aOptions.icon;
    else
      throw "Notification icon is mandatory";

    if ("title" in aOptions && aOptions.title != null)
      this._title = aOptions.title;
    else
      throw "Notification title is mandatory";

    if ("message" in aOptions && aOptions.message != null)
      this._message = aOptions.message;
    else
      this._message = null;

    if ("priority" in aOptions && aOptions.priority != null)
      this._priority = aOptions.priority;

    if ("buttons" in aOptions && aOptions.buttons != null) {
      if (aOptions.buttons.length > 3)
        throw "Too many buttons provided. The max number is 3";

      this._buttons = {};
      for (let i = 0; i < aOptions.buttons.length; i++) {
        let button_id = aOptions.buttons[i].buttonId;
        this._buttons[button_id] = aOptions.buttons[i];
      }
    } else {
      this._buttons = null;
    }

    if ("ongoing" in aOptions && aOptions.ongoing != null)
      this._ongoing = aOptions.ongoing;
    else
      this._ongoing = false;

    if ("progress" in aOptions && aOptions.progress != null)
      this._progress = aOptions.progress;
    else
      this._progress = null;

    if ("onCancel" in aOptions && aOptions.onCancel != null)
      this._onCancel = aOptions.onCancel;
    else
      this._onCancel = null;

    if ("onClick" in aOptions && aOptions.onClick != null)
      this._onClick = aOptions.onClick;
    else
      this._onClick = null;

    if ("cookie" in aOptions && aOptions.cookie != null)
      this._cookie = aOptions.cookie;
    else
      this._cookie = null;

    if ("handlerKey" in aOptions && aOptions.handlerKey != null)
      this._handlerKey = aOptions.handlerKey;

    if ("persistent" in aOptions && aOptions.persistent != null)
      this._persistent = aOptions.persistent;
    else
      this._persistent = false;
  },

  show: function() {
    let msg = {
        type: "Notification:Show",
        id: this._id,
        title: this._title,
        smallIcon: this._icon,
        ongoing: this._ongoing,
        when: this._when,
        persistent: this._persistent,
    };

    if (this._message)
      msg.text = this._message;

    if (this._progress) {
      msg.progress_value = this._progress;
      msg.progress_max = 100;
      msg.progress_indeterminate = false;
    } else if (Number.isNaN(this._progress)) {
      msg.progress_value = 0;
      msg.progress_max = 0;
      msg.progress_indeterminate = true;
    }

    if (this._cookie)
      msg.cookie = JSON.stringify(this._cookie);

    if (this._priority)
      msg.priority = this._priority;

    if (this._buttons) {
      msg.actions = [];
      let buttonName;
      for (buttonName in this._buttons) {
        let button = this._buttons[buttonName];
        let obj = {
          buttonId: button.buttonId,
          title : button.title,
          icon : button.icon
        };
        msg.actions.push(obj);
      }
    }

    if (this._light)
      msg.light = this._light;

    if (this._handlerKey)
      msg.handlerKey = this._handlerKey;

    Services.androidBridge.handleGeckoMessage(msg);
    return this;
  },

  cancel: function() {
    let msg = {
      type: "Notification:Hide",
      id: this._id,
      handlerKey: this._handlerKey,
      cookie: JSON.stringify(this._cookie),
    };
    Services.androidBridge.handleGeckoMessage(msg);
  }
}

var Notifications = {
  get idService() {
    delete this.idService;
    return this.idService = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
  },

  registerHandler: function(key, handler) {
    if (!_handlersMap[key]) {
      _handlersMap[key] = [];
    }
    _handlersMap[key].push(handler);
  },

  unregisterHandler: function(key, handler) {
    let h = _handlersMap[key];
    if (!h) {
      return;
    }
    let i = h.indexOf(handler);
    if (i > -1) {
      h.splice(i, 1);
    }
  },

  create: function notif_notify(aOptions) {
    let id = this.idService.generateUUID().toString();

    let notification = new Notification(id, aOptions);
    _notificationsMap[id] = notification;
    notification.show();

    return id;
  },

  update: function notif_update(aId, aOptions) {
    let notification = _notificationsMap[aId];
    if (!notification)
      throw "Unknown notification id";
    notification.fillWithOptions(aOptions);
    notification.show();
  },

  cancel: function notif_cancel(aId) {
    let notification = _notificationsMap[aId];
    if (notification)
      notification.cancel();
  },

  observe: function notif_observe(aSubject, aTopic, aData) {
    Services.console.logStringMessage(aTopic + " " + aData);

    let data = JSON.parse(aData);
    let id = data.id;
    let handlerKey = data.handlerKey;
    let cookie = data.cookie ? JSON.parse(data.cookie) : undefined;
    let notification = _notificationsMap[id];

    switch (data.eventType) {
      case "notification-clicked":
        if (notification && notification._onClick)
          notification._onClick(id, notification._cookie);

        if (handlerKey) {
          _handlersMap[handlerKey].forEach(function(handler) {
            handler.onClick(cookie);
          });
        }

        break;
      case "notification-button-clicked":
        if (handlerKey) {
          _handlersMap[handlerKey].forEach(function(handler) {
            handler.onButtonClick(data.buttonId, cookie);
          });
        }

        break;
      case "notification-cleared":
      case "notification-closed":
        if (handlerKey) {
          _handlersMap[handlerKey].forEach(function(handler) {
            handler.onCancel(cookie);
          });
        }

        if (notification && notification._onCancel)
          notification._onCancel(id, notification._cookie);
        delete _notificationsMap[id]; // since the notification was dismissed, we no longer need to hold a reference.
        break;
    }
  },

  QueryInterface: function (aIID) {
    if (!aIID.equals(Ci.nsISupports) &&
        !aIID.equals(Ci.nsIObserver) &&
        !aIID.equals(Ci.nsISupportsWeakReference))
      throw Components.results.NS_ERROR_NO_INTERFACE;
    return this;
  }
};

Services.obs.addObserver(Notifications, "Notification:Event", false);