// Generated by CoffeeScript 2.6.1
(function() {
  // telnetInput.coffee
  // Copyright 2017 Patrick Meade.

  // This program is free software: you can redistribute it and/or modify
  // it under the terms of the GNU Affero General Public License as
  // published by the Free Software Foundation, either version 3 of the
  // License, or (at your option) any later version.

  // This program is distributed in the hope that it will be useful,
  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  // GNU Affero General Public License for more details.

  // You should have received a copy of the GNU Affero General Public License
  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
  //----------------------------------------------------------------------------
  var DEFAULT_SUBNEGOTIATION_BUFFER_SIZE, DEFAULT_SUBNEGOTIATION_ERROR_POLICY, TELNET_COMMAND, TELNET_DATA, TELNET_DO, TELNET_DONT, TELNET_IAC, TELNET_OPTION, TELNET_SUBNEG, TELNET_SUBNEG_COMMAND, TELNET_SUB_BEGIN, TELNET_SUB_END, TELNET_WILL, TELNET_WONT, TelnetInput, Transform;

  DEFAULT_SUBNEGOTIATION_BUFFER_SIZE = 8192;

  DEFAULT_SUBNEGOTIATION_ERROR_POLICY = "keepBoth";

  TELNET_COMMAND = "TELNET_COMMAND";

  TELNET_DATA = "TELNET_DATA";

  TELNET_OPTION = "TELNET_OPTION";

  TELNET_SUBNEG = "TELNET_SUBNEG";

  TELNET_SUBNEG_COMMAND = "TELNET_SUBNEG_COMMAND";

  TELNET_DO = 253;

  TELNET_DONT = 254;

  TELNET_IAC = 255;

  TELNET_SUB_BEGIN = 250;

  TELNET_SUB_END = 240;

  TELNET_WILL = 251;

  TELNET_WONT = 252;

  ({Transform} = require("stream"));

  TelnetInput = class TelnetInput extends Transform {
    constructor(opt) {
      var options;
      options = opt || {};
      super(options);
      this.state = TELNET_DATA;
      this.subBufSize = options.bufferSize || DEFAULT_SUBNEGOTIATION_BUFFER_SIZE;
      this.subBuf = Buffer.alloc(this.subBufSize);
      this.errorPolicy = options.errorPolicy || DEFAULT_SUBNEGOTIATION_ERROR_POLICY;
    }

    _transform(chunk, encoding, callback) {
      var byte, i, len;
      this.dataBuf = Buffer.alloc(chunk.length * 2);
      this.dataBufIndex = 0;
      for (i = 0, len = chunk.length; i < len; i++) {
        byte = chunk[i];
        this._handle(byte);
      }
      if (this.dataBufIndex > 0) {
        this.push(this.dataBuf.slice(0, this.dataBufIndex));
      }
      return callback();
    }

    _handle(chunkData) {
      switch (this.state) {
        case TELNET_DATA:
          switch (chunkData) {
            case TELNET_IAC:
              return this.state = TELNET_COMMAND;
            default:
              this.dataBuf[this.dataBufIndex] = chunkData;
              return this.dataBufIndex++;
          }
          break;
        case TELNET_COMMAND:
          switch (chunkData) {
            case TELNET_IAC:
              this.state = TELNET_DATA;
              this.dataBuf[this.dataBufIndex] = TELNET_IAC;
              return this.dataBufIndex++;
            case TELNET_DO:
            case TELNET_DONT:
            case TELNET_WILL:
            case TELNET_WONT:
            case TELNET_SUB_BEGIN:
              this.state = TELNET_OPTION;
              return this.command = chunkData;
            default:
              this.state = TELNET_DATA;
              return this.emit("command", chunkData);
          }
          break;
        case TELNET_OPTION:
          switch (this.command) {
            case TELNET_DO:
              this.state = TELNET_DATA;
              return this.emit("do", chunkData);
            case TELNET_DONT:
              this.state = TELNET_DATA;
              return this.emit("dont", chunkData);
            case TELNET_WILL:
              this.state = TELNET_DATA;
              return this.emit("will", chunkData);
            case TELNET_WONT:
              this.state = TELNET_DATA;
              return this.emit("wont", chunkData);
            case TELNET_SUB_BEGIN:
              this.state = TELNET_SUBNEG;
              this.option = chunkData;
              this.subBufIndex = 0;
              return this.subOverflowEmit = false;
          }
          break;
        case TELNET_SUBNEG:
          switch (chunkData) {
            case TELNET_IAC:
              return this.state = TELNET_SUBNEG_COMMAND;
            default:
              return this._handleSub(chunkData);
          }
          break;
        case TELNET_SUBNEG_COMMAND:
          switch (chunkData) {
            case TELNET_IAC:
              this.state = TELNET_SUBNEG;
              return this._handleSub(TELNET_IAC);
            case TELNET_SUB_END:
              this.state = TELNET_DATA;
              return this.emit("sub", this.option, this.subBuf.slice(0, this.subBufIndex));
            default:
              this.state = TELNET_SUBNEG;
              this.emit("error", new Error("expected IAC or SE"));
              switch (this.errorPolicy) {
                case "discardBoth":
                  break;
                case "keepData":
                  return this._handleSub(chunkData);
                default:
                  // "keepBoth"
                  this._handleSub(TELNET_IAC);
                  return this._handleSub(chunkData);
              }
          }
      }
    }

    _handleSub(subByte) {
      if (this.subBufIndex >= this.subBufSize) {
        if (!this.subOverflowEmit) {
          this.subOverflowEmit = true;
          this.emit("error", new Error("subnegotiation buffer overflow"));
        }
        return;
      }
      this.subBuf[this.subBufIndex] = subByte;
      return this.subBufIndex++;
    }

  };

  exports.TelnetInput = TelnetInput;

  //----------------------------------------------------------------------------
// end of telnetInput.coffee

}).call(this);
