"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _jsbi = _interopRequireDefault(require("jsbi"));

var _readableStream = require("readable-stream");

var _token = require("./token");

var _colmetadataTokenParser = _interopRequireDefault(require("./colmetadata-token-parser"));

var _doneTokenParser = require("./done-token-parser");

var _envChangeTokenParser = _interopRequireDefault(require("./env-change-token-parser"));

var _infoerrorTokenParser = require("./infoerror-token-parser");

var _fedauthInfoParser = _interopRequireDefault(require("./fedauth-info-parser"));

var _featureExtAckParser = _interopRequireDefault(require("./feature-ext-ack-parser"));

var _loginackTokenParser = _interopRequireDefault(require("./loginack-token-parser"));

var _orderTokenParser = _interopRequireDefault(require("./order-token-parser"));

var _returnstatusTokenParser = _interopRequireDefault(require("./returnstatus-token-parser"));

var _returnvalueTokenParser = _interopRequireDefault(require("./returnvalue-token-parser"));

var _rowTokenParser = _interopRequireDefault(require("./row-token-parser"));

var _nbcrowTokenParser = _interopRequireDefault(require("./nbcrow-token-parser"));

var _sspiTokenParser = _interopRequireDefault(require("./sspi-token-parser"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const tokenParsers = {
  [_token.TYPE.COLMETADATA]: _colmetadataTokenParser.default,
  [_token.TYPE.DONE]: _doneTokenParser.doneParser,
  [_token.TYPE.DONEINPROC]: _doneTokenParser.doneInProcParser,
  [_token.TYPE.DONEPROC]: _doneTokenParser.doneProcParser,
  [_token.TYPE.ENVCHANGE]: _envChangeTokenParser.default,
  [_token.TYPE.ERROR]: _infoerrorTokenParser.errorParser,
  [_token.TYPE.FEDAUTHINFO]: _fedauthInfoParser.default,
  [_token.TYPE.FEATUREEXTACK]: _featureExtAckParser.default,
  [_token.TYPE.INFO]: _infoerrorTokenParser.infoParser,
  [_token.TYPE.LOGINACK]: _loginackTokenParser.default,
  [_token.TYPE.ORDER]: _orderTokenParser.default,
  [_token.TYPE.RETURNSTATUS]: _returnstatusTokenParser.default,
  [_token.TYPE.RETURNVALUE]: _returnvalueTokenParser.default,
  [_token.TYPE.ROW]: _rowTokenParser.default,
  [_token.TYPE.NBCROW]: _nbcrowTokenParser.default,
  [_token.TYPE.SSPI]: _sspiTokenParser.default
};

class EndOfMessageMarker {}

class Parser extends _readableStream.Transform {
  constructor(debug, colMetadata, options) {
    super({
      objectMode: true
    });
    this.debug = void 0;
    this.colMetadata = void 0;
    this.options = void 0;
    this.endOfMessageMarker = void 0;
    this.buffer = void 0;
    this.position = void 0;
    this.suspended = void 0;
    this.next = void 0;
    this.debug = debug;
    this.colMetadata = colMetadata;
    this.options = options;
    this.endOfMessageMarker = new EndOfMessageMarker();
    this.buffer = Buffer.alloc(0);
    this.position = 0;
    this.suspended = false;
    this.next = undefined;
  }

  _transform(input, _encoding, done) {
    if (input instanceof EndOfMessageMarker) {
      return done(undefined, new _token.EndOfMessageToken());
    }

    if (this.position === this.buffer.length) {
      this.buffer = input;
    } else {
      this.buffer = Buffer.concat([this.buffer.slice(this.position), input]);
    }

    this.position = 0;

    if (this.suspended) {
      // Unsuspend and continue from where ever we left off.
      this.suspended = false;
      const next = this.next;
      next();
    } // If we're no longer suspended, parse new tokens


    if (!this.suspended) {
      // Start the parser
      this.parseTokens();
    }

    done();
  }

  parseTokens() {
    const doneParsing = token => {
      if (token) {
        if (token instanceof _token.ColMetadataToken) {
          this.colMetadata = token.columns;
        }

        this.push(token);
      }
    };

    while (!this.suspended && this.position + 1 <= this.buffer.length) {
      const type = this.buffer.readUInt8(this.position);
      this.position += 1;

      if (tokenParsers[type]) {
        tokenParsers[type](this, this.colMetadata, this.options, doneParsing);
      } else {
        this.emit('error', new Error('Unknown type: ' + type));
      }
    }
  }

  suspend(next) {
    this.suspended = true;
    this.next = next;
  }

  awaitData(length, callback) {
    if (this.position + length <= this.buffer.length) {
      callback();
    } else {
      this.suspend(() => {
        this.awaitData(length, callback);
      });
    }
  }

  readInt8(callback) {
    this.awaitData(1, () => {
      const data = this.buffer.readInt8(this.position);
      this.position += 1;
      callback(data);
    });
  }

  readUInt8(callback) {
    this.awaitData(1, () => {
      const data = this.buffer.readUInt8(this.position);
      this.position += 1;
      callback(data);
    });
  }

  readInt16LE(callback) {
    this.awaitData(2, () => {
      const data = this.buffer.readInt16LE(this.position);
      this.position += 2;
      callback(data);
    });
  }

  readInt16BE(callback) {
    this.awaitData(2, () => {
      const data = this.buffer.readInt16BE(this.position);
      this.position += 2;
      callback(data);
    });
  }

  readUInt16LE(callback) {
    this.awaitData(2, () => {
      const data = this.buffer.readUInt16LE(this.position);
      this.position += 2;
      callback(data);
    });
  }

  readUInt16BE(callback) {
    this.awaitData(2, () => {
      const data = this.buffer.readUInt16BE(this.position);
      this.position += 2;
      callback(data);
    });
  }

  readInt32LE(callback) {
    this.awaitData(4, () => {
      const data = this.buffer.readInt32LE(this.position);
      this.position += 4;
      callback(data);
    });
  }

  readInt32BE(callback) {
    this.awaitData(4, () => {
      const data = this.buffer.readInt32BE(this.position);
      this.position += 4;
      callback(data);
    });
  }

  readUInt32LE(callback) {
    this.awaitData(4, () => {
      const data = this.buffer.readUInt32LE(this.position);
      this.position += 4;
      callback(data);
    });
  }

  readUInt32BE(callback) {
    this.awaitData(4, () => {
      const data = this.buffer.readUInt32BE(this.position);
      this.position += 4;
      callback(data);
    });
  }

  readBigInt64LE(callback) {
    this.awaitData(8, () => {
      const result = _jsbi.default.add(_jsbi.default.leftShift(_jsbi.default.BigInt(this.buffer[this.position + 4] + this.buffer[this.position + 5] * Math.pow(2, 8) + this.buffer[this.position + 6] * Math.pow(2, 16) + (this.buffer[this.position + 7] << 24) // Overflow
      ), _jsbi.default.BigInt(32)), _jsbi.default.BigInt(this.buffer[this.position] + this.buffer[this.position + 1] * Math.pow(2, 8) + this.buffer[this.position + 2] * Math.pow(2, 16) + this.buffer[this.position + 3] * Math.pow(2, 24)));

      this.position += 8;
      callback(result);
    });
  }

  readInt64LE(callback) {
    this.awaitData(8, () => {
      const data = Math.pow(2, 32) * this.buffer.readInt32LE(this.position + 4) + ((this.buffer[this.position + 4] & 0x80) === 0x80 ? 1 : -1) * this.buffer.readUInt32LE(this.position);
      this.position += 8;
      callback(data);
    });
  }

  readInt64BE(callback) {
    this.awaitData(8, () => {
      const data = Math.pow(2, 32) * this.buffer.readInt32BE(this.position) + ((this.buffer[this.position] & 0x80) === 0x80 ? 1 : -1) * this.buffer.readUInt32BE(this.position + 4);
      this.position += 8;
      callback(data);
    });
  }

  readBigUInt64LE(callback) {
    this.awaitData(8, () => {
      const low = _jsbi.default.BigInt(this.buffer.readUInt32LE(this.position));

      const high = _jsbi.default.BigInt(this.buffer.readUInt32LE(this.position + 4));

      this.position += 8;
      callback(_jsbi.default.add(low, _jsbi.default.leftShift(high, _jsbi.default.BigInt(32))));
    });
  }

  readUInt64LE(callback) {
    this.awaitData(8, () => {
      const data = Math.pow(2, 32) * this.buffer.readUInt32LE(this.position + 4) + this.buffer.readUInt32LE(this.position);
      this.position += 8;
      callback(data);
    });
  }

  readUInt64BE(callback) {
    this.awaitData(8, () => {
      const data = Math.pow(2, 32) * this.buffer.readUInt32BE(this.position) + this.buffer.readUInt32BE(this.position + 4);
      this.position += 8;
      callback(data);
    });
  }

  readFloatLE(callback) {
    this.awaitData(4, () => {
      const data = this.buffer.readFloatLE(this.position);
      this.position += 4;
      callback(data);
    });
  }

  readFloatBE(callback) {
    this.awaitData(4, () => {
      const data = this.buffer.readFloatBE(this.position);
      this.position += 4;
      callback(data);
    });
  }

  readDoubleLE(callback) {
    this.awaitData(8, () => {
      const data = this.buffer.readDoubleLE(this.position);
      this.position += 8;
      callback(data);
    });
  }

  readDoubleBE(callback) {
    this.awaitData(8, () => {
      const data = this.buffer.readDoubleBE(this.position);
      this.position += 8;
      callback(data);
    });
  }

  readUInt24LE(callback) {
    this.awaitData(3, () => {
      const low = this.buffer.readUInt16LE(this.position);
      const high = this.buffer.readUInt8(this.position + 2);
      this.position += 3;
      callback(low | high << 16);
    });
  }

  readUInt40LE(callback) {
    this.awaitData(5, () => {
      const low = this.buffer.readUInt32LE(this.position);
      const high = this.buffer.readUInt8(this.position + 4);
      this.position += 5;
      callback(0x100000000 * high + low);
    });
  }

  readUNumeric64LE(callback) {
    this.awaitData(8, () => {
      const low = this.buffer.readUInt32LE(this.position);
      const high = this.buffer.readUInt32LE(this.position + 4);
      this.position += 8;
      callback(0x100000000 * high + low);
    });
  }

  readUNumeric96LE(callback) {
    this.awaitData(12, () => {
      const dword1 = this.buffer.readUInt32LE(this.position);
      const dword2 = this.buffer.readUInt32LE(this.position + 4);
      const dword3 = this.buffer.readUInt32LE(this.position + 8);
      this.position += 12;
      callback(dword1 + 0x100000000 * dword2 + 0x100000000 * 0x100000000 * dword3);
    });
  }

  readUNumeric128LE(callback) {
    this.awaitData(16, () => {
      const dword1 = this.buffer.readUInt32LE(this.position);
      const dword2 = this.buffer.readUInt32LE(this.position + 4);
      const dword3 = this.buffer.readUInt32LE(this.position + 8);
      const dword4 = this.buffer.readUInt32LE(this.position + 12);
      this.position += 16;
      callback(dword1 + 0x100000000 * dword2 + 0x100000000 * 0x100000000 * dword3 + 0x100000000 * 0x100000000 * 0x100000000 * dword4);
    });
  } // Variable length data


  readBuffer(length, callback) {
    this.awaitData(length, () => {
      const data = this.buffer.slice(this.position, this.position + length);
      this.position += length;
      callback(data);
    });
  } // Read a Unicode String (BVARCHAR)


  readBVarChar(callback) {
    this.readUInt8(length => {
      this.readBuffer(length * 2, data => {
        callback(data.toString('ucs2'));
      });
    });
  } // Read a Unicode String (USVARCHAR)


  readUsVarChar(callback) {
    this.readUInt16LE(length => {
      this.readBuffer(length * 2, data => {
        callback(data.toString('ucs2'));
      });
    });
  } // Read binary data (BVARBYTE)


  readBVarByte(callback) {
    this.readUInt8(length => {
      this.readBuffer(length, callback);
    });
  } // Read binary data (USVARBYTE)


  readUsVarByte(callback) {
    this.readUInt16LE(length => {
      this.readBuffer(length, callback);
    });
  }

}

var _default = Parser;
exports.default = _default;
module.exports = Parser;