/* 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/. */

const {Cu} = require("chrome");
const promise = require("promise");
const {AddonManager} = Cu.import("resource://gre/modules/AddonManager.jsm");
const Services = require("Services");
const {getJSON} = require("devtools/client/shared/getjson");
const EventEmitter = require("devtools/shared/event-emitter");

const ADDONS_URL = "devtools.webide.addonsURL";

var SIMULATOR_LINK = Services.prefs.getCharPref("devtools.webide.simulatorAddonsURL");
var ADB_LINK = Services.prefs.getCharPref("devtools.webide.adbAddonURL");
var ADAPTERS_LINK = Services.prefs.getCharPref("devtools.webide.adaptersAddonURL");
var SIMULATOR_ADDON_ID = Services.prefs.getCharPref("devtools.webide.simulatorAddonID");
var ADB_ADDON_ID = Services.prefs.getCharPref("devtools.webide.adbAddonID");
var ADAPTERS_ADDON_ID = Services.prefs.getCharPref("devtools.webide.adaptersAddonID");

var platform = Services.appShell.hiddenDOMWindow.navigator.platform;
var OS = "";
if (platform.indexOf("Win") != -1) {
  OS = "win32";
} else if (platform.indexOf("Mac") != -1) {
  OS = "mac64";
} else if (platform.indexOf("Linux") != -1) {
  if (platform.indexOf("x86_64") != -1) {
    OS = "linux64";
  } else {
    OS = "linux32";
  }
}

var addonsListener = {};
addonsListener.onEnabled =
addonsListener.onDisabled =
addonsListener.onInstalled =
addonsListener.onUninstalled = (updatedAddon) => {
  GetAvailableAddons().then(addons => {
    for (let a of [...addons.simulators, addons.adb, addons.adapters]) {
      if (a.addonID == updatedAddon.id) {
        a.updateInstallStatus();
      }
    }
  });
}
AddonManager.addAddonListener(addonsListener);

var GetAvailableAddons_promise = null;
var GetAvailableAddons = exports.GetAvailableAddons = function() {
  if (!GetAvailableAddons_promise) {
    let deferred = promise.defer();
    GetAvailableAddons_promise = deferred.promise;
    let addons = {
      simulators: [],
      adb: null
    }
    getJSON(ADDONS_URL, true).then(json => {
      for (let stability in json) {
        for (let version of json[stability]) {
          addons.simulators.push(new SimulatorAddon(stability, version));
        }
      }
      addons.adb = new ADBAddon();
      addons.adapters = new AdaptersAddon();
      deferred.resolve(addons);
    }, e => {
      GetAvailableAddons_promise = null;
      deferred.reject(e);
    });
  }
  return GetAvailableAddons_promise;
}

exports.ForgetAddonsList = function() {
  GetAvailableAddons_promise = null;
}

function Addon() {}
Addon.prototype = {
  _status: "unknown",
  set status(value) {
    if (this._status != value) {
      this._status = value;
      this.emit("update");
    }
  },
  get status() {
    return this._status;
  },

  updateInstallStatus: function() {
    AddonManager.getAddonByID(this.addonID, (addon) => {
      if (addon && !addon.userDisabled) {
        this.status = "installed";
      } else {
        this.status = "uninstalled";
      }
    });
  },

  install: function() {
    AddonManager.getAddonByID(this.addonID, (addon) => {
      if (addon && !addon.userDisabled) {
        this.status = "installed";
        return;
      }
      this.status = "preparing";
      if (addon && addon.userDisabled) {
        addon.userDisabled = false;
      } else {
        AddonManager.getInstallForURL(this.xpiLink, (install) => {
          install.addListener(this);
          install.install();
        }, "application/x-xpinstall");
      }
    });
  },

  uninstall: function() {
    AddonManager.getAddonByID(this.addonID, (addon) => {
      addon.uninstall();
    });
  },

  installFailureHandler: function(install, message) {
    this.status = "uninstalled";
    this.emit("failure", message);
  },

  onDownloadStarted: function() {
    this.status = "downloading";
  },

  onInstallStarted: function() {
    this.status = "installing";
  },

  onDownloadProgress: function(install) {
    if (install.maxProgress == -1) {
      this.emit("progress", -1);
    } else {
      this.emit("progress", install.progress / install.maxProgress);
    }
  },

  onInstallEnded: function({addon}) {
    addon.userDisabled = false;
  },

  onDownloadCancelled: function(install) {
    this.installFailureHandler(install, "Download cancelled");
  },
  onDownloadFailed: function(install) {
    this.installFailureHandler(install, "Download failed");
  },
  onInstallCancelled: function(install) {
    this.installFailureHandler(install, "Install cancelled");
  },
  onInstallFailed: function(install) {
    this.installFailureHandler(install, "Install failed");
  },
}

function SimulatorAddon(stability, version) {
  EventEmitter.decorate(this);
  this.stability = stability;
  this.version = version;
  // This addon uses the string "linux" for "linux32"
  let fixedOS = OS == "linux32" ? "linux" : OS;
  this.xpiLink = SIMULATOR_LINK.replace(/#OS#/g, fixedOS)
                               .replace(/#VERSION#/g, version)
                               .replace(/#SLASHED_VERSION#/g, version.replace(/\./g, "_"));
  this.addonID = SIMULATOR_ADDON_ID.replace(/#SLASHED_VERSION#/g, version.replace(/\./g, "_"));
  this.updateInstallStatus();
}
SimulatorAddon.prototype = Object.create(Addon.prototype);

function ADBAddon() {
  EventEmitter.decorate(this);
  // This addon uses the string "linux" for "linux32"
  let fixedOS = OS == "linux32" ? "linux" : OS;
  this.xpiLink = ADB_LINK.replace(/#OS#/g, fixedOS);
  this.addonID = ADB_ADDON_ID;
  this.updateInstallStatus();
}
ADBAddon.prototype = Object.create(Addon.prototype);

function AdaptersAddon() {
  EventEmitter.decorate(this);
  this.xpiLink = ADAPTERS_LINK.replace(/#OS#/g, OS);
  this.addonID = ADAPTERS_ADDON_ID;
  this.updateInstallStatus();
}
AdaptersAddon.prototype = Object.create(Addon.prototype);
