var EventEmitter, Runtime, WebSocketServer, WebSocketTransport, common, coordinator, debug, http, protocol, querystring, saveGraphFile, send, transport;

protocol = require('./protocol');

coordinator = require('./coordinator');

common = require('./common');

querystring = require('querystring');

transport = require('msgflo-nodejs').transport;

debug = require('debug')('msgflo:runtime');

http = require('http');

EventEmitter = require('events').EventEmitter;

WebSocketServer = require('websocket').server;

send = function(connection, msg) {
  return connection.sendUTF(JSON.stringify(msg));
};

WebSocketTransport = class WebSocketTransport extends EventEmitter {
  constructor(server) {
    var handleMessage, ws;
    super();
    this.server = server;
    this.connections = [];
    ws = new WebSocketServer({
      httpServer: this.server
    });
    handleMessage = (message, connection) => {
      var e, msg;
      if (message.type !== 'utf8') {
        return;
      }
      try {
        msg = JSON.parse(message.utf8Data);
      } catch (error) {
        e = error;
        return null;
      }
      return this.emitMessage(msg, connection);
    };
    ws.on('request', (request) => {
      var connection, subProtocol;
      subProtocol = request.requestedProtocols.indexOf("noflo") !== -1 ? "noflo" : null;
      connection = request.accept(subProtocol, request.origin);
      this.connections.push(connection);
      connection.on('message', function(message) {
        return handleMessage(message, connection);
      });
      return connection.on('close', () => {
        var connIndex;
        connIndex = this.connections.indexOf(connection);
        if (connIndex === -1) {
          return;
        }
        return this.connections.splice(connIndex, 1);
      });
    });
  }

  send(protocol, command, payload, ctx) {
    var connection, msg;
    if (command === 'error') {
      console.error(`${protocol}:error`, payload.message);
    }
    connection = ctx;
    msg = {
      protocol: protocol,
      command: command,
      payload: payload
    };
    debug('SEND', msg);
    return send(connection, msg);
  }

  sendAll(protocol, command, payload) {
    var connection, i, len, msg, ref, results;
    if (command === 'error') {
      console.error(`${protocol}:error`, payload.message);
    }
    msg = {
      protocol: protocol,
      command: command,
      payload: payload
    };
    debug('SENDALL', this.connections.length, msg);
    ref = this.connections;
    results = [];
    for (i = 0, len = ref.length; i < len; i++) {
      connection = ref[i];
      results.push(send(connection, msg));
    }
    return results;
  }

  emitMessage(msg, ctx) {
    return this.emit('message', msg.protocol, msg.command, msg.payload, ctx);
  }

};

// atomic
saveGraphFile = function(graph, filepath, callback) {
  var fs, json, temppath;
  fs = require('fs');
  temppath = filepath + `.msgflo-autosave-${Date.now()}`;
  json = JSON.stringify(graph, null, 2);
  return fs.open(temppath, 'wx', function(err, fd) {
    if (err) {
      return callback(err);
    }
    return fs.write(fd, json, function(err) {
      if (err) {
        return callback(err);
      }
      return fs.fsync(fd, function(err) {
        if (err) {
          return callback(err);
        }
        return fs.rename(temppath, filepath, function(err) {
          return fs.unlink(temppath, function(e) {
            return callback(err);
          });
        });
      });
    });
  });
};

Runtime = class Runtime {
  constructor(options) {
    this.options = options;
    this.server = null;
    this.transport = null;
    this.protocol = null;
    this.broker = transport.getBroker(this.options.broker);
    this.coordinator = new coordinator.Coordinator(this.broker, this.options);
    this.saveGraph = common.debounce(() => {
      var graph;
      debug('saving graph changes', this.options.graph);
      graph = this.coordinator.serializeGraph('main');
      return saveGraphFile(graph, this.options.graph, function(err) {
        if (err) {
          return console.log("ERROR: Failed to save graph file", err);
        }
      });
    }, 500);
    if (this.options.graph && this.options.autoSave) {
      debug('enabling autosave');
      this.coordinator.on('graph-changed', () => {
        return this.saveGraph();
      });
    }
  }

  start(callback) {
    this.server = http.createServer();
    this.transport = new WebSocketTransport(this.server);
    this.protocol = new protocol.Protocol(this.transport, this.coordinator);
    this.server.on('request', (request, response) => {
      return this.serveFrontpage(request, response);
    });
    return this.server.listen(this.options.port, (err) => {
      if (err) {
        return callback(err);
      }
      return this.coordinator.start((err) => {
        var onLoaded;
        if (err) {
          return callback(err);
        }
        onLoaded = (err) => {
          return callback(err, this.address(), this.liveUrl());
        };
        if (this.options.graph) {
          return this.coordinator.loadGraphFile(this.options.graph, this.options, onLoaded);
        } else {
          return onLoaded(null);
        }
      });
    });
  }

  stop(callback) {
    return this.coordinator.stop((stopErr) => {
      return this.server.close(function(closeErr) {
        if (stopErr) {
          return callback(stopErr);
        }
        if (closeErr) {
          return callback(closeErr);
        }
        return callback(null);
      });
    });
  }

  address() {
    var address, scheme;
    scheme = 'ws://';
    return address = scheme + this.options.host + ':' + this.options.port;
  }

  liveUrl() {
    var params, url;
    params = querystring.escape(`protocol=websocket&address=${this.address()}&id=${this.options.runtimeId}`);
    return url = `${this.options.ide}#runtime/endpoint?${params}`;
  }

  serveFrontpage(req, res) {
    var html;
    html = `<a id="flowhub_url">Open in Flowhub</a>\n<script>\n  var addr = window.location.origin.replace("http://", "ws://");\n  addr = addr.replace("https://", "ws://");\n  var ide = "${this.options.ide}";\n  var url = ide+"/#runtime/endpoint?protocol=websocket&address="+encodeURIComponent(addr);\n  var a = document.getElementById("flowhub_url");\n  a.href = url;\n</script>`;
    res.statusCode = 200;
    res.setHeader("Content-Type", "text/html");
    res.write(html);
    return res.end();
  }

};

exports.Runtime = Runtime;
