/* 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";

module.metadata = {
  "stability": "experimental"
};

const { Cu } = require("chrome");
const { Class } = require("../sdk/core/heritage");
const { MessagePort, MessageChannel } = require("../sdk/messaging");
const { require: devtoolsRequire } = Cu.import("resource://devtools/shared/Loader.jsm", {});
const { DebuggerServer } = devtoolsRequire("devtools/server/main");

const outputs = new WeakMap();
const inputs = new WeakMap();
const targets = new WeakMap();
const transports = new WeakMap();

const inputFor = port => inputs.get(port);
const outputFor = port => outputs.get(port);
const transportFor = port => transports.get(port);

const fromTarget = target => {
  const debuggee = new Debuggee();
  const { port1, port2 } = new MessageChannel();
  inputs.set(debuggee, port1);
  outputs.set(debuggee, port2);
  targets.set(debuggee, target);

  return debuggee;
};
exports.fromTarget = fromTarget;

const Debuggee = Class({
  extends: MessagePort.prototype,
  close: function() {
    const server = transportFor(this);
    if (server) {
      transports.delete(this);
      server.close();
    }
    outputFor(this).close();
  },
  start: function() {
    const target = targets.get(this);
    if (target.isLocalTab) {
      // Since a remote protocol connection will be made, let's start the
      // DebuggerServer here, once and for all tools.
      if (!DebuggerServer.initialized) {
        DebuggerServer.init();
        DebuggerServer.addBrowserActors();
      }

      transports.set(this, DebuggerServer.connectPipe());
    }
    // TODO: Implement support for remote connections (See Bug 980421)
    else {
      throw Error("Remote targets are not yet supported");
    }

    // pipe messages send to the debuggee to an actual
    // server via remote debugging protocol transport.
    inputFor(this).addEventListener("message", ({data}) =>
      transportFor(this).send(data));

    // pipe messages received from the remote debugging
    // server transport onto the this debuggee.
    transportFor(this).hooks = {
      onPacket: packet => inputFor(this).postMessage(packet),
      onClosed: () => inputFor(this).close()
    };

    inputFor(this).start();
    outputFor(this).start();
  },
  postMessage: function(data) {
    return outputFor(this).postMessage(data);
  },
  get onmessage() {
    return outputFor(this).onmessage;
  },
  set onmessage(onmessage) {
    outputFor(this).onmessage = onmessage;
  },
  addEventListener: function(...args) {
    return outputFor(this).addEventListener(...args);
  },
  removeEventListener: function(...args) {
    return outputFor(this).removeEventListener(...args);
  }
});
exports.Debuggee = Debuggee;
