//= require web-console

var AJAXTransport = (function(WebConsole) {

  var inherits = WebConsole.inherits;
  var EventEmitter = WebConsole.EventEmitter;

  var FORM_MIME_TYPE = 'application/x-www-form-urlencoded; charset=utf-8';

  var AJAXTransport = function(options) {
    EventEmitter.call(this);
    options || (options = {});

    this.url = (typeof options.url === 'string') ? {
      input: options.url,
      pendingOutput: options.url,
      configuration: options.url
    } : options.url;

    this.pendingInput  = '';

    this.initializeEventHandlers();
  };

  inherits(AJAXTransport, EventEmitter);

  // Initializes the default event handlers.
  AJAXTransport.prototype.initializeEventHandlers = function() {
    this.on('input', this.sendInput);
    this.on('configuration', this.sendConfiguration);
    this.once('initialization', function(cols, rows) {
      this.emit('configuration', cols, rows);
      this.pollForPendingOutput();
    });
  };

  // Shorthand for creating XHR requests.
  AJAXTransport.prototype.createRequest = function(method, url, options) {
    options || (options = {});

    var request = new XMLHttpRequest;
    request.open(method, url);

    if (typeof options.form === 'object') {
      var content = [], form = options.form;

      for (var key in form) {
        var value = form[key];
        content.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
      }

      request.setRequestHeader('Content-Type', FORM_MIME_TYPE);
      request.data = content.join('&');
    }

    return request;
  };

  AJAXTransport.prototype.pollForPendingOutput = function() {
    var request = this.createRequest('GET', this.url.pendingOutput);

    var self = this;
    request.onreadystatechange = function() {
      if (request.readyState === XMLHttpRequest.DONE) {
        if (request.status === 200) {
          self.emit('pendingOutput', request.responseText);
          self.pollForPendingOutput();
        } else {
          self.emit('disconnect', request);
        }
      }
    };

    request.send(null);
  };

  // Send the input to the server.
  //
  // Each key press is encoded to an intermediate format, before it is sent to
  // the server.
  //
  // WebConsole#keysPressed is an alias for WebConsole#sendInput.
  AJAXTransport.prototype.sendInput = function(input) {
    input || (input = '');

    if (this.disconnected) return;
    if (this.sendingInput) return this.pendingInput += input;

    // Indicate that we are starting to send input.
    this.sendingInput = true;

    var request = this.createRequest('PUT', this.url.input, {
      form: { input: this.pendingInput + input }
    });

    // Clear the pending input.
    this.pendingInput = '';

    var self = this;
    request.onreadystatechange = function() {
      if (request.readyState === XMLHttpRequest.DONE) {
        self.sendingInput = false;
        if (self.pendingInput) self.sendInput();
      }
    };

    request.send(request.data);
  };

  // Send the terminal configuration to the server.
  //
  // Right now by configuration, we understand the terminal widht and terminal
  // height.
  //
  // WebConsole#resized is an alias for WebConsole#sendconfiguration.
  AJAXTransport.prototype.sendConfiguration = function(cols, rows) {
    if (this.disconnected) return;

    var request = this.createRequest('PUT', this.url.configuration, {
      form: { width: cols, height: rows }
    });

    // Just send the configuration and don't care about any output.
    request.send(request.data);
  };

  return AJAXTransport;

}).call(this, WebConsole);

window.addEventListener('load', function() {
  var geometry = calculateFitScreenGeometry();
  config.terminal.cols = geometry[0];
  config.terminal.rows = geometry[1];

  var terminal = window.terminal = new WebConsole.Terminal(config.terminal);

  terminal.on('data', function(data) {
    transport.emit('input', data);
  });

  var transport = new AJAXTransport(config.transport);

  transport.on('pendingOutput', function(response) {
    var json = JSON.parse(response);
    if (json.output) terminal.write(json.output);
  });

  transport.on('disconnect', function() {
    terminal.destroy();
  });

  transport.emit('initialization', terminal.cols, terminal.rows);

  // Utilities
  // ---------

  function calculateFitScreenGeometry() {
    // Currently, resizing term.js is broken. E.g. opening vi causes it to go
    // back to 80x24 and fail with off-by-one error. Other stuff, like chip8
    // are rendered incorrectly and so on.
    //
    // To work around it, create a temporary terminal, just so we can get the
    // best dimensions for the screen.
    var temporary = new WebConsole.Terminal;
    try {
      return temporary.fitScreen();
    } finally {
      temporary.destroy();
    }
  }
});