const fs = require('fs');
const path = require('path');
const stream = require('stream');
const split2 = require('split2');
const pumpify = require('pumpify');

const cprotoParser = require('./reader-cproto-parser.js');
const docParser = require('./reader-doc-parser.js');

const DOC_FILE = path.resolve(__dirname, '..', 'cephes', 'cephes.txt');

const INTERNAL_CEPHES_FUNCTIONS = new Set([
  'hyp2f0', 'onef2', 'threef0'
]);

class MergeDocumentation extends stream.Transform {
  constructor() {
    super({ objectMode: true });

    this._prototypes = new Map();

    this._documentationEnded = false;
    this._documentation = [];

    this._documentationStream = fs.createReadStream(DOC_FILE)
      .pipe(docParser())
      .on('data', (doc) => this._documentation.push(doc))
      .once('end', (err) => this._documentationEnded = true)
      .on('error', (err) => this.emit('error', err));
  }

  _transform(proto, encoding, done) {
    this._prototypes.set(proto.functionName, proto);
    done(null);
  }

  _finish(done) {
    // Validate that no documentation is missing
    const documentationParts = new Set(
      this._documentation.map((doc) => doc.functionName)
    );
    for (const protoFunctionName of this._prototypes.keys()) {
      if (INTERNAL_CEPHES_FUNCTIONS.has(protoFunctionName)) continue;

      if (!documentationParts.has(protoFunctionName)) {
        throw new Error(`documentation for ${protoFunctionName} is missing`);
      }
    }

    // Merge data
    // The order of the documentation stream is meaninful, so use the
    // documentation as the order and merge in the prototypes.
    for (const doc of this._documentation) {
      if (INTERNAL_CEPHES_FUNCTIONS.has(doc.functionName)) continue;

      if (this._prototypes.has(doc.functionName)) {
        this.push(
          Object.assign({}, doc, this._prototypes.get(doc.functionName))
        );
      }
    }

    done(null);
  }

  _flush(done) {
    if (this._documentationEnded) {
      process.nextTick(() => this._finish(done));
    } else {
      this._documentationStream.once('end', () => this._finish(done));
    }
  }
}


function parser() {
  return pumpify.obj(
    cprotoParser(),
    new MergeDocumentation()
  );
}

module.exports = parser;