text: 'use strict';
  
  Object.defineProperty(exports, "__esModule", {
    value: true
  });
  
  var _promise = require('babel-runtime/core-js/promise');
  
  var _promise2 = _interopRequireDefault(_promise);
  
  var _defineProperty = require('babel-runtime/core-js/object/define-property');
  
  var _defineProperty2 = _interopRequireDefault(_defineProperty);
  
  var _setPrototypeOf = require('babel-runtime/core-js/object/set-prototype-of');
  
  var _setPrototypeOf2 = _interopRequireDefault(_setPrototypeOf);
  
  var _create = require('babel-runtime/core-js/object/create');
  
  var _create2 = _interopRequireDefault(_create);
  
  var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
  
  var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
  
  var _assign = require('babel-runtime/core-js/object/assign');
  
  var _assign2 = _interopRequireDefault(_assign);
  
  var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; (0, _defineProperty2.default)(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  
  exports.parseStream = parseStream;
  exports.parseFile = parseFile;
  exports.parseStringSync = parseStringSync;
  exports.formatSync = formatSync;
  exports.formatStream = formatStream;
  exports.formatFile = formatFile;
  
  var _parse = require('./parse');
  
  var _parse2 = _interopRequireDefault(_parse);
  
  var _util = require('./util');
  
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  
  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  
  function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  
  function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = (0, _create2.default)(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) _setPrototypeOf2.default ? (0, _setPrototypeOf2.default)(subClass, superClass) : subClass.__proto__ = superClass; }
  
  var _require = require('stream'),
      Transform = _require.Transform;
  
  var Decoder = require('string_decoder').StringDecoder;
  
  // don't load fs native module if running in webpacked code
  var fs = typeof __webpack_require__ !== 'function' ? require('fs') : null;
  
  // call a callback on the next process tick if running in
  // an environment that supports it
  function _callback(callback) {
    if (process && process.nextTick) process.nextTick(callback);else callback();
  }
  
  // shared arg processing for the parse routines
  function _processParseOptions(options) {
    var additionalDefaults = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  
    var out = (0, _assign2.default)({
      parseFeatures: true,
      parseDirectives: false,
      parseSequences: true,
      parseComments: false
    }, additionalDefaults, options);
  
    if (options.parseAll) {
      out.parseFeatures = true;
      out.parseDirectives = true;
      out.parseComments = true;
      out.parseSequences = true;
    }
  
    return out;
  }
  
  var GFFTransform = function (_Transform) {
    _inherits(GFFTransform, _Transform);
  
    function GFFTransform() {
      var inputOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  
      _classCallCheck(this, GFFTransform);
  
      var options = _processParseOptions(inputOptions);
  
      var _this = _possibleConstructorReturn(this, (GFFTransform.__proto__ || (0, _getPrototypeOf2.default)(GFFTransform)).call(this, { objectMode: true }));
  
      _this.encoding = inputOptions.encoding || 'utf8';
  
      _this.decoder = new Decoder();
      _this.textBuffer = '';
  
      var push = _this.push.bind(_this);
      _this.parser = new _parse2.default({
        featureCallback: options.parseFeatures ? push : null,
        directiveCallback: options.parseDirectives ? push : null,
        commentCallback: options.parseComments ? push : null,
        sequenceCallback: options.parseSequences ? push : null,
        errorCallback: function errorCallback(err) {
          return _this.emit('error', err);
        },
        bufferSize: options.bufferSize
      });
      return _this;
    }
  
    _createClass(GFFTransform, [{
      key: '_addLine',
      value: function _addLine(data) {
        var line = data.toString('utf8');
        if (line) {
          this.parser.addLine(line);
        }
      }
    }, {
      key: '_nextText',
      value: function _nextText(buffer) {
        var _this2 = this;
  
        var pieces = (this.textBuffer + buffer).split(/\r?\n/);
        this.textBuffer = pieces.pop();
  
        if (this.maxLineLength && this.textBuffer.length > this.maxLineLength) {
          this.emit('error', new Error('maximum line size exceeded'));
          return;
        }
  
        pieces.forEach(function (piece) {
          return _this2._addLine(piece);
        });
      }
    }, {
      key: '_transform',
      value: function _transform(chunk, encoding, callback) {
        this._nextText(this.decoder.write(chunk));
        _callback(callback);
      }
    }, {
      key: '_flush',
      value: function _flush(callback) {
        if (this.decoder.end) this._nextText(this.decoder.end());
        if (this.textBuffer != null) this._addLine(this.textBuffer);
        this.parser.finish();
        _callback(callback);
      }
    }]);
  
    return GFFTransform;
  }(Transform);
  
  /**
   * Parse a stream of text data into a stream of feature,
   * directive, and comment objects.
   *
   * @param {Object} options optional options object
   * @param {string} options.encoding text encoding of the input GFF3. default 'utf8'
   * @param {boolean} options.parseAll default false.  if true, will parse all items. overrides other flags
   * @param {boolean} options.parseFeatures default true
   * @param {boolean} options.parseDirectives default false
   * @param {boolean} options.parseComments default false
   * @param {boolean} options.parseSequences default true
   * @param {Number} options.bufferSize maximum number of GFF3 lines to buffer. defaults to 1000
   * @returns {ReadableStream} stream (in objectMode) of parsed items
   */
  
  
  function parseStream() {
    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  
    var newOptions = (0, _assign2.default)({ bufferSize: 1000 }, options);
    return new GFFTransform(newOptions);
  }
  
  /**
   * Read and parse a GFF3 file from the filesystem.
   *
   * @param {string} filename the filename of the file to parse
   * @param {Object} options optional options object
   * @param {string} options.encoding the file's string encoding, defaults to 'utf8'
   * @param {boolean} options.parseAll default false.  if true, will parse all items. overrides other flags
   * @param {boolean} options.parseFeatures default true
   * @param {boolean} options.parseDirectives default false
   * @param {boolean} options.parseComments default false
   * @param {boolean} options.parseSequences default true
   * @param {Number} options.bufferSize maximum number of GFF3 lines to buffer. defaults to 1000
   * @returns {ReadableStream} stream (in objectMode) of parsed items
   */
  function parseFile(filename, options) {
    return fs.createReadStream(filename).pipe(parseStream(options));
  }
  
  /**
   * Synchronously parse a string containing GFF3 and return
   * an arrayref of the parsed items.
   *
   * @param {string} str
   * @param {Object} inputOptions optional options object
   * @param {boolean} inputOptions.parseAll default false.  if true, will parse all items. overrides other flags
   * @param {boolean} inputOptions.parseFeatures default true
   * @param {boolean} inputOptions.parseDirectives default false
   * @param {boolean} inputOptions.parseComments default false
   * @param {boolean} inputOptions.parseSequences default true
   * @returns {Array} array of parsed features, directives, and/or comments
   */
  function parseStringSync(str) {
    var inputOptions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  
    if (!str) return [];
  
    var options = _processParseOptions(inputOptions);
  
    var items = [];
    var push = items.push.bind(items);
  
    var parser = new _parse2.default({
      featureCallback: options.parseFeatures ? push : null,
      directiveCallback: options.parseDirectives ? push : null,
      commentCallback: options.parseComments ? push : null,
      sequenceCallback: options.parseSequences ? push : null,
      bufferSize: Infinity,
      errorCallback: function errorCallback(err) {
        throw err;
      }
    });
  
    str.split(/\r?\n/).forEach(parser.addLine.bind(parser));
    parser.finish();
  
    return items;
  }
  
  /**
   * Format an array of GFF3 items (features,directives,comments) into string of GFF3.
   * Does not insert synchronization (###) marks.
   *
   * @param {Array[Object]} items
   * @returns {String} the formatted GFF3
   */
  function formatSync(items) {
    // sort items into seq and other
    var other = [];
    var sequences = [];
    items.forEach(function (i) {
      if (i.sequence) sequences.push(i);else other.push(i);
    });
    var str = other.map(_util.formatItem).join('');
    if (sequences.length) {
      str += '##FASTA\n';
      str += sequences.map(_util.formatSequence).join('');
    }
    return str;
  }
  
  var FormattingTransform = function (_Transform2) {
    _inherits(FormattingTransform, _Transform2);
  
    function FormattingTransform() {
      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  
      _classCallCheck(this, FormattingTransform);
  
      var _this3 = _possibleConstructorReturn(this, (FormattingTransform.__proto__ || (0, _getPrototypeOf2.default)(FormattingTransform)).call(this, (0, _assign2.default)(options, { objectMode: true })));
  
      _this3.linesSinceLastSyncMark = 0;
      _this3.minLinesBetweenSyncMarks = options.minSyncLines || 100;
      _this3.insertVersionDirective = options.insertVersionDirective || false;
      _this3.haveWeEmittedData = false;
      _this3.fastaMode = false;
      return _this3;
    }
  
    _createClass(FormattingTransform, [{
      key: '_transform',
      value: function _transform(chunk, encoding, callback) {
        // if we have not emitted anything yet, and this first
        // chunk is not a gff-version directive, emit one
        var str = void 0;
        if (!this.haveWeEmittedData && this.insertVersionDirective && (chunk[0] || chunk).directive !== 'gff-version') this.push('##gff-version 3\n');
  
        // if it's a sequence chunk coming down, emit a FASTA directive and
        // change to FASTA mode
        if (chunk.sequence && !this.fastaMode) {
          this.push('##FASTA\n');
          this.fastaMode = true;
        }
  
        if (Array.isArray(chunk)) str = chunk.map(_util.formatItem).join('');else str = (0, _util.formatItem)(chunk);
  
        this.push(str);
  
        if (this.linesSinceLastSyncMark >= this.minLinesBetweenSyncMarks) {
          this.push('###\n');
          this.linesSinceLastSyncMark = 0;
        } else {
          // count the number of newlines in this chunk
          var count = 0;
          for (var i = 0; i < str.length; i += 1) {
            if (str[i] === '\n') count += 1;
          }
          this.linesSinceLastSyncMark += count;
        }
  
        this.haveWeEmittedData = true;
        _callback(callback);
      }
    }]);
  
    return FormattingTransform;
  }(Transform);
  
  /**
   * Format a stream of items (of the type produced
   * by this script) into a stream of GFF3 text.
   *
   * Inserts synchronization (###) marks automatically.
   *
   * @param {Object} options
   * @param {Object} options.minSyncLines minimum number of lines between ### marks. default 100
   * @param {Boolean} options.insertVersionDirective
   *  if the first item in the stream is not a ##gff-version directive, insert one.
   *  default false
   */
  
  
  function formatStream(options) {
    return new FormattingTransform(options);
  }
  
  /**
   * Format a stream of items (of the type produced
   * by this script) into a GFF3 file and write it to the filesystem.
  
   * Inserts synchronization (###) marks and a ##gff-version
   * directive automatically (if one is not already present).
   *
   * @param {ReadableStream} stream the stream to write to the file
   * @param {String} filename the file path to write to
   * @param {Object} options
   * @param {String} options.encoding default 'utf8'. encoding for the written file
   * @param {Number} options.minSyncLines
   *  minimum number of lines between sync (###) marks. default 100
   * @param {Boolean} options.insertVersionDirective
   *  if the first item in the stream is not a ##gff-version directive, insert one.
   *  default true
   * @returns {Promise} promise for the written filename
   */
  function formatFile(stream, filename) {
    var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  
    var newOptions = (0, _assign2.default)({
      insertVersionDirective: true
    }, options);
  
    return new _promise2.default(function (resolve, reject) {
      stream.pipe(new FormattingTransform(newOptions)).on('end', function () {
        return resolve(filename);
      }).on('error', reject).pipe(fs.createWriteStream(filename, {
        encoding: newOptions.encoding || 'utf8'
      }));
    });
  }