#!/usr/bin/env node
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs = require("fs");
const path = require("path");
const flair = require("./adapters/flair");
const luis = require("./adapters/luis");
const rasa = require("./adapters/rasa");
const snips = require("./adapters/snips");
const web = require("./adapters/web");
const main_1 = require("./main");
const utils = require("./utils");
const argv = require('minimist')(process.argv.slice(2));
const logger = console;
const adapters = { default: web, rasa, snips, luis, flair };
const workingDirectory = process.cwd();
const getFileWithPath = (filename) => path.resolve(workingDirectory, filename);
const chatitoFilesFromDir = (startPath, cb) => __awaiter(this, void 0, void 0, function* () {
    if (!fs.existsSync(startPath)) {
        logger.error(`Invalid directory: ${startPath}`);
        process.exit(1);
    }
    const files = fs.readdirSync(startPath);
    for (const file of files) {
        const filename = path.join(startPath, file);
        const stat = fs.lstatSync(filename);
        if (stat.isDirectory()) {
            yield chatitoFilesFromDir(filename, cb);
        }
        else if (/\.chatito$/.test(filename)) {
            yield cb(filename);
        }
    }
});
const importer = (fromPath, importFile) => {
    const filePath = path.resolve(path.dirname(fromPath), importFile);
    if (path.extname(filePath) !== '.chatito') {
        throw new Error('Only files with .chatito extension can be imported');
    }
    if (!fs.existsSync(filePath)) {
        throw new Error(`Can't import ${filePath}`);
    }
    const dsl = fs.readFileSync(filePath, 'utf8');
    return { filePath, dsl };
};
const streamedAdapterAccumulator = (format, outputPath) => {
    const adapterHandler = adapters[format];
    if (!adapterHandler) {
        throw new Error(`Invalid adapter: ${format}`);
    }
    if (!fs.existsSync(outputPath)) {
        fs.mkdirSync(outputPath);
    }
    const trainingFileName = argv.trainingFileName || `${format}_dataset_training.txt`;
    const testingFileName = argv.testingFileName || `${format}_dataset_testing.txt`;
    const trainingClassificationFilePath = path.resolve(outputPath, `classification_${trainingFileName}`);
    const testingClassificationFilePath = path.resolve(outputPath, `classification_${testingFileName}`);
    const trainingNerFilePath = path.resolve(outputPath, `ner_${trainingFileName}`);
    const testingNerFilePath = path.resolve(outputPath, `ner_${testingFileName}`);
    const trainClassification = fs.createWriteStream(trainingClassificationFilePath, { flags: 'a' });
    const testClassification = fs.createWriteStream(testingClassificationFilePath, { flags: 'a' });
    const trainNER = fs.createWriteStream(trainingNerFilePath, { flags: 'a' });
    const testNER = fs.createWriteStream(testingNerFilePath, { flags: 'a' });
    trainClassification.on('close', () => logger.log('Train classification dataset done!'));
    testClassification.on('close', () => logger.log('Test classification dataset done!'));
    trainNER.on('close', () => logger.log('Test NER dataset done!'));
    testNER.on('close', () => logger.log('Test NER dataset done!'));
    return {
        write: (fullFilenamePath) => __awaiter(this, void 0, void 0, function* () {
            logger.log(`Processing file: ${fullFilenamePath}`);
            const dsl = fs.readFileSync(fullFilenamePath, 'utf8');
            const streams = { trainClassification, testClassification, trainNER, testNER };
            yield adapterHandler.streamAdapter(dsl, streams, importer, fullFilenamePath);
        }),
        save: () => {
            trainClassification.close();
            testClassification.close();
            trainNER.close();
            testNER.close();
            logger.log(`Saved training dataset: ${trainingClassificationFilePath}`);
            logger.log(`Saved testing dataset: ${testingClassificationFilePath}`);
            logger.log(`Saved training dataset: ${trainingNerFilePath}`);
            logger.log(`Saved testing dataset: ${testingNerFilePath}`);
        }
    };
};
const adapterAccumulator = (format, outputPath, formatOptions) => {
    const trainingDataset = {};
    const testingDataset = {};
    if (format === 'flair') {
        return streamedAdapterAccumulator('flair', outputPath);
    }
    const trainingJsonFileName = argv.trainingFileName || `${format}_dataset_training.json`;
    const trainingJsonFilePath = path.resolve(outputPath, trainingJsonFileName);
    const testingFileName = argv.testingFileName || `${format}_dataset_testing.json`;
    const testingJsonFilePath = path.resolve(outputPath, testingFileName);
    const adapterHandler = adapters[format];
    if (!adapterHandler) {
        throw new Error(`Invalid adapter: ${format}`);
    }
    return {
        write: (fullFilenamePath) => __awaiter(this, void 0, void 0, function* () {
            logger.log(`Processing file: ${fullFilenamePath}`);
            const dsl = fs.readFileSync(fullFilenamePath, 'utf8');
            const { training, testing } = yield adapterHandler.adapter(dsl, formatOptions, importer, fullFilenamePath);
            utils.mergeDeep(trainingDataset, training);
            utils.mergeDeep(testingDataset, testing);
        }),
        save: () => {
            if (!fs.existsSync(outputPath)) {
                fs.mkdirSync(outputPath);
            }
            fs.writeFileSync(trainingJsonFilePath, JSON.stringify(trainingDataset));
            logger.log(`Saved training dataset: ${trainingJsonFilePath}`);
            if (Object.keys(testingDataset).length) {
                fs.writeFileSync(testingJsonFilePath, JSON.stringify(testingDataset));
                logger.log(`Saved testing dataset: ${testingJsonFilePath}`);
            }
        }
    };
};
(() => __awaiter(this, void 0, void 0, function* () {
    if (!argv._ || !argv._.length) {
        logger.error('Invalid chatito file.');
        process.exit(1);
    }
    const dslFile = argv._[0];
    const format = (argv.format || 'default').toLowerCase();
    if (['default', 'rasa', 'snips', 'luis', 'flair'].indexOf(format) === -1) {
        logger.error(`Invalid format argument: ${format}`);
        process.exit(1);
    }
    const outputPath = argv.outputPath || process.cwd();
    if (argv.defaultDistribution && main_1.VALID_DISTRIBUTIONS.indexOf(argv.defaultDistribution) !== -1) {
        main_1.config.defaultDistribution = argv.defaultDistribution;
    }
    logger.log(`NOTE: Using ${main_1.config.defaultDistribution} as default frequency distribution.`);
    try {
        let formatOptions = null;
        if (argv.formatOptions) {
            formatOptions = JSON.parse(fs.readFileSync(path.resolve(argv.formatOptions), 'utf8'));
        }
        const dslFilePath = getFileWithPath(dslFile);
        const isDirectory = fs.existsSync(dslFilePath) && fs.lstatSync(dslFilePath).isDirectory();
        const accumulator = adapterAccumulator(format, outputPath, formatOptions);
        if (isDirectory) {
            yield chatitoFilesFromDir(dslFilePath, accumulator.write);
        }
        else {
            yield accumulator.write(dslFilePath);
        }
        accumulator.save();
    }
    catch (e) {
        if (e && e.message && e.location) {
            logger.log('==== CHATITO SYNTAX ERROR ====');
            logger.log('    ', e.message);
            logger.log(`     Line: ${e.location.start.line}, Column: ${e.location.start.column}`);
            logger.log('==============================');
        }
        else {
            logger.error(e && e.stack ? e.stack : e);
        }
        logger.log('FULL ERROR REPORT:');
        logger.error(e);
        process.exit(1);
    }
}))();
