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

/* eslint-env browser */

"use strict";

const { createClass, DOM: dom, PropTypes } =
  require("devtools/client/shared/vendor/react");
const { debugWorker } = require("../../modules/worker");
const Services = require("Services");

loader.lazyRequireGetter(this, "DebuggerClient",
  "devtools/shared/client/main", true);

const Strings = Services.strings.createBundle(
  "chrome://devtools/locale/aboutdebugging.properties");

module.exports = createClass({
  displayName: "ServiceWorkerTarget",

  propTypes: {
    client: PropTypes.instanceOf(DebuggerClient).isRequired,
    debugDisabled: PropTypes.bool,
    target: PropTypes.shape({
      active: PropTypes.bool,
      icon: PropTypes.string,
      name: PropTypes.string.isRequired,
      url: PropTypes.string,
      scope: PropTypes.string.isRequired,
      // registrationActor can be missing in e10s.
      registrationActor: PropTypes.string,
      workerActor: PropTypes.string
    }).isRequired
  },

  getInitialState() {
    return {
      pushSubscription: null
    };
  },

  componentDidMount() {
    let { client } = this.props;
    client.addListener("push-subscription-modified", this.onPushSubscriptionModified);
    this.updatePushSubscription();
  },

  componentDidUpdate(oldProps, oldState) {
    let wasActive = oldProps.target.active;
    if (!wasActive && this.isActive()) {
      // While the service worker isn't active, any calls to `updatePushSubscription`
      // won't succeed. If we just became active, make sure we didn't miss a push
      // subscription change by updating it now.
      this.updatePushSubscription();
    }
  },

  componentWillUnmount() {
    let { client } = this.props;
    client.removeListener("push-subscription-modified", this.onPushSubscriptionModified);
  },

  debug() {
    if (!this.isRunning()) {
      // If the worker is not running, we can't debug it.
      return;
    }

    let { client, target } = this.props;
    debugWorker(client, target.workerActor);
  },

  push() {
    if (!this.isActive() || !this.isRunning()) {
      // If the worker is not running, we can't push to it.
      // If the worker is not active, the registration might be unavailable and the
      // push will not succeed.
      return;
    }

    let { client, target } = this.props;
    client.request({
      to: target.workerActor,
      type: "push"
    });
  },

  start() {
    if (!this.isActive() || this.isRunning()) {
      // If the worker is not active or if it is already running, we can't start it.
      return;
    }

    let { client, target } = this.props;
    client.request({
      to: target.registrationActor,
      type: "start"
    });
  },

  unregister() {
    let { client, target } = this.props;
    client.request({
      to: target.registrationActor,
      type: "unregister"
    });
  },

  onPushSubscriptionModified(type, data) {
    let { target } = this.props;
    if (data.from === target.registrationActor) {
      this.updatePushSubscription();
    }
  },

  updatePushSubscription() {
    if (!this.props.target.registrationActor) {
      // A valid registrationActor is needed to retrieve the push subscription.
      return;
    }

    let { client, target } = this.props;
    client.request({
      to: target.registrationActor,
      type: "getPushSubscription"
    }, ({ subscription }) => {
      this.setState({ pushSubscription: subscription });
    });
  },

  isRunning() {
    // We know the target is running if it has a worker actor.
    return !!this.props.target.workerActor;
  },

  isActive() {
    return this.props.target.active;
  },

  getServiceWorkerStatus() {
    if (this.isActive() && this.isRunning()) {
      return "running";
    } else if (this.isActive()) {
      return "stopped";
    }
    // We cannot get service worker registrations unless the registration is in
    // ACTIVE state. Unable to know the actual state ("installing", "waiting"), we
    // display a custom state "registering" for now. See Bug 1153292.
    return "registering";
  },

  renderButtons() {
    let pushButton = dom.button({
      className: "push-button",
      onClick: this.push
    }, Strings.GetStringFromName("push"));

    let debugButton = dom.button({
      className: "debug-button",
      onClick: this.debug,
      disabled: this.props.debugDisabled
    }, Strings.GetStringFromName("debug"));

    let startButton = dom.button({
      className: "start-button",
      onClick: this.start,
    }, Strings.GetStringFromName("start"));

    if (this.isRunning()) {
      if (this.isActive()) {
        return [pushButton, debugButton];
      }
      // Only debug button is available if the service worker is not active.
      return debugButton;
    }
    return startButton;
  },

  renderUnregisterLink() {
    if (!this.isActive()) {
      // If not active, there might be no registrationActor available.
      return null;
    }

    return dom.a({
      onClick: this.unregister,
      className: "unregister-link"
    }, Strings.GetStringFromName("unregister"));
  },

  render() {
    let { target } = this.props;
    let { pushSubscription } = this.state;
    let status = this.getServiceWorkerStatus();

    return dom.div({ className: "target-container" },
      dom.img({
        className: "target-icon",
        role: "presentation",
        src: target.icon
      }),
      dom.span({ className: `target-status target-status-${status}` },
        Strings.GetStringFromName(status)),
      dom.div({ className: "target" },
        dom.div({ className: "target-name", title: target.name }, target.name),
        dom.ul({ className: "target-details" },
          (pushSubscription ?
            dom.li({ className: "target-detail" },
              dom.strong(null, Strings.GetStringFromName("pushService")),
              dom.span({
                className: "service-worker-push-url",
                title: pushSubscription.endpoint
              }, pushSubscription.endpoint)) :
            null
          ),
          dom.li({ className: "target-detail" },
            dom.strong(null, Strings.GetStringFromName("scope")),
            dom.span({
              className: "service-worker-scope",
              title: target.scope
            }, target.scope),
            this.renderUnregisterLink()
          )
        )
      ),
      this.renderButtons()
    );
  }
});
