/* IMPORT */
"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 _ = require("lodash");
const chokidar = require("chokidar");
const globby = require("globby");
const isBinaryPath = require("is-binary-path");
const querystring = require("querystring");
const string_matches_1 = require("string-matches");
const config_1 = require("../config");
const consts_1 = require("../consts");
const file_1 = require("./file");
const folder_1 = require("./folder");
/* EMBEDDED */
const Embedded = {
    get(rootPaths = folder_1.default.getAllRootPaths(), groupByRoot = true, groupByType = true, groupByFile = true, filter = false) {
        return __awaiter(this, void 0, void 0, function* () {
            const filePaths = yield Embedded.getFilePaths(rootPaths);
            if (!filePaths.length)
                return;
            return yield Embedded.getFilesTodos(filePaths, consts_1.default.regexes.todoEmbedded, groupByRoot, groupByType, groupByFile, filter);
        });
    },
    filePaths: undefined,
    getFilePaths(rootPaths) {
        return __awaiter(this, void 0, void 0, function* () {
            if (Embedded.filePaths)
                return Embedded.filePaths;
            rootPaths = _.castArray(rootPaths);
            const config = config_1.default.get(), { include, exclude } = config.embedded;
            let filePaths = [];
            for (let rootPath of rootPaths) {
                const rootFilePaths = yield globby(include, { cwd: rootPath, ignore: exclude, absolute: true });
                filePaths = filePaths.concat(rootFilePaths);
            }
            filePaths = filePaths.filter(filePath => !isBinaryPath(filePath));
            Embedded.filePaths = filePaths;
            Embedded.watchFilePaths();
            return filePaths;
        });
    },
    watcher: undefined,
    watchFilePaths() {
        return __awaiter(this, void 0, void 0, function* () {
            Embedded.unwatchFilePaths();
            const config = config_1.default.get();
            /* HANDLERS */
            function add(filePath) {
                if (!Embedded.filePaths)
                    return;
                if (Embedded.filePaths.includes(filePath))
                    return;
                if (isBinaryPath(filePath))
                    return;
                Embedded.filePaths.push(filePath);
            }
            function change(filePath) {
                if (!Embedded.filePaths)
                    return;
                delete Embedded.fileData[filePath];
            }
            function unlink(filePath) {
                if (!Embedded.filePaths)
                    return;
                Embedded.filePaths = Embedded.filePaths.filter(other => other !== filePath);
                change(filePath);
            }
            /* WATCHING */
            const roots = folder_1.default.getAllRootPaths();
            if (!roots.length)
                return;
            Embedded.watcher = chokidar.watch(roots, { ignored: config.embedded.exclude }).on('add', add).on('change', change).on('unlink', unlink);
        });
    },
    unwatchFilePaths() {
        return __awaiter(this, void 0, void 0, function* () {
            if (!Embedded.watcher)
                return;
            Embedded.watcher.close();
            Embedded.filePaths = undefined;
            Embedded.fileData = {};
        });
    },
    fileData: {},
    getFileData(filePath, regex) {
        return __awaiter(this, void 0, void 0, function* () {
            if (Embedded.fileData[filePath])
                return Embedded.fileData[filePath];
            const content = yield file_1.default.read(filePath);
            if (!content)
                return [];
            const data = [], lines = content.split(/\r?\n/);
            let pathData;
            lines.forEach((line, lineNr) => {
                const matches = string_matches_1.default(line, regex);
                if (!matches.length)
                    return;
                if (!pathData) {
                    pathData = folder_1.default.parsePath(filePath);
                }
                matches.forEach(match => {
                    data.push([pathData, line, lineNr, match]);
                });
            });
            Embedded.fileData[filePath] = data;
            return data;
        });
    },
    getFilesTodos(filePaths, regex, groupByRoot, groupByType, groupByFile, filter) {
        return __awaiter(this, void 0, void 0, function* () {
            const todos = {}, // { [ROOT] { [TYPE] => { [FILEPATH] => [DATA] } } }
            filterRe = filter ? new RegExp(_.escapeRegExp(filter), 'i') : false;
            yield Promise.all(filePaths.map((filePath) => __awaiter(this, void 0, void 0, function* () {
                const data = yield Embedded.getFileData(filePath, regex);
                if (!data.length)
                    return;
                const filePathGroup = groupByFile ? filePath : '';
                data.forEach(([{ root, rootPath, relativePath }, line, lineNr, match]) => {
                    if (filterRe && !filterRe.test(line))
                        return;
                    const todo = match[0], type = match[1], message = match[2], code = line.slice(0, line.indexOf(match[0])), rootGroup = groupByRoot ? root : '', typeGroup = groupByType ? type : '';
                    if (!todos[rootGroup])
                        todos[rootGroup] = {};
                    if (!todos[rootGroup][typeGroup])
                        todos[rootGroup][typeGroup] = {};
                    if (!todos[rootGroup][typeGroup][filePathGroup])
                        todos[rootGroup][typeGroup][filePathGroup] = [];
                    todos[rootGroup][typeGroup][filePathGroup].push({ root, rootPath, relativePath, filePath, line, lineNr, todo, type, message, code });
                });
            })));
            const roots = Object.keys(todos);
            if (roots.length === 1 && roots[0]) {
                return { '': todos[roots[0]] };
            }
            else {
                return todos;
            }
        });
    },
    renderTodos(todos) {
        const sepRe = new RegExp(querystring.escape('/'), 'g'), config = config_1.default.get(), { indentation, embedded: { file: { wholeLine } }, symbols: { box } } = config, lines = [];
        /* LINES */
        const roots = Object.keys(todos).sort();
        roots.forEach(root => {
            if (root) {
                lines.push(`\n${root}:`);
            }
            const types = Object.keys(todos[root]).sort();
            types.forEach(type => {
                if (type) {
                    lines.push(`${root ? indentation : '\n'}${type}:`);
                }
                const filePaths = Object.keys(todos[root][type]).sort();
                filePaths.forEach(filePath => {
                    if (filePath) {
                        const normalizedFilePath = `/${_.trimStart(filePath, '/')}`, encodedFilePath = querystring.escape(normalizedFilePath).replace(sepRe, '/');
                        lines.push(`${root ? indentation : ''}${type ? indentation : ''}@file://${encodedFilePath}`);
                    }
                    const datas = todos[root][type][filePath];
                    datas.forEach(({ filePath: todoFilePath, line, lineNr, message }) => {
                        const normalizedFilePath = `/${_.trimStart(todoFilePath, '/')}`, encodedFilePath = querystring.escape(normalizedFilePath).replace(sepRe, '/');
                        lines.push(`${root ? indentation : ''}${type ? indentation : ''}${filePath ? indentation : ''}${box} ${_.trimStart(wholeLine ? line : message)} @file://${encodedFilePath}#${lineNr + 1}`);
                    });
                });
            });
        });
        return lines.length ? `${lines.join('\n')}\n` : '';
    }
};
/* EXPORT */
exports.default = Embedded;
//# sourceMappingURL=embedded.js.map