// LICENSE : MIT
"use strict";
import { RuleHelper } from "textlint-rule-helper";

const prh = require("prh");
const path = require("path");
const untildify = require("untildify");

function createPrhEngine(rulePaths, baseDir) {
    if (rulePaths.length === 0) {
        return null;
    }
    const expandedRulePaths = rulePaths.map(rulePath => untildify(rulePath));
    const prhEngine = prh.fromYAMLFilePath(path.resolve(baseDir, expandedRulePaths[0]));
    expandedRulePaths.slice(1).forEach(ruleFilePath => {
        const config = prh.fromYAMLFilePath(path.resolve(baseDir, ruleFilePath));
        prhEngine.merge(config);
    });
    return prhEngine;
}

function createPrhEngineFromContents(yamlContents) {
    if (yamlContents.length === 0) {
        return null;
    }
    const dummyFilePath = "";
    const prhEngine = prh.fromYAML(dummyFilePath, yamlContents[0]);
    yamlContents.slice(1).forEach(content => {
        const config = prh.fromYAML(dummyFilePath, content);
        prhEngine.merge(config);
    });
    return prhEngine;
}

function mergePrh(...engines) {
    const engines_ = engines.filter(engine => !!engine);
    const mainEngine = engines_[0];
    engines_.slice(1).forEach(engine => {
        mainEngine.merge(engine);
    });
    return mainEngine;
}

const assertOptions = options => {
    if (typeof options.ruleContents === "undefined" && typeof options.rulePaths === "undefined") {
        throw new Error(`textlint-rule-prh require Rule Options.
Please set .textlinrc:
{
    "rules": {
        "prh": {
            "rulePaths" :["path/to/prh.yml"]
        }
    }
}
`);
    }
};

/**
 * for each diff of changeSet
 * @param {ChangeSet} changeSet
 * @param {string} str
 * @param {function({
            matchStartIndex: number,
            matchEndIndex: number,
            actual: string
            expected: string
        })}onChangeOfMatch
 */
const forEachChange = (changeSet, str, onChangeOfMatch) => {
    const sortedDiffs = changeSet.diffs.sort(function(a, b) {
        return a.index - b.index;
    });
    let delta = 0;
    sortedDiffs.forEach(function(diff) {
        const result = diff.expected.replace(/\$([0-9]{1,2})/g, function(match, g1) {
            const index = parseInt(g1);
            if (index === 0 || diff.matches.length - 1 < index) {
                return match;
            }
            return diff.matches[index] || "";
        });
        // matchStartIndex/matchEndIndex value is original position, not replaced position
        // textlint use original position
        const matchStartIndex = diff.index;
        const matchEndIndex = matchStartIndex + diff.matches[0].length;
        // actual => expected
        const actual = str.slice(diff.index + delta, diff.index + delta + diff.matches[0].length);
        onChangeOfMatch({
            matchStartIndex,
            matchEndIndex,
            actual: actual,
            expected: result
        });
        str = str.slice(0, diff.index + delta) + result + str.slice(diff.index + delta + diff.matches[0].length);
        delta += result.length - diff.matches[0].length;
    });
};
const getConfigBaseDir = context => {
    if (typeof context.getConfigBaseDir === "function") {
        return context.getConfigBaseDir() || process.cwd();
    }
    // Old fallback that use deprecated `config` value
    // https://github.com/textlint/textlint/issues/294
    const textlintRcFilePath = context.config ? context.config.configFile : null;
    // .textlinrc directory
    return textlintRcFilePath ? path.dirname(textlintRcFilePath) : process.cwd();
};

function reporter(context, options = {}) {
    assertOptions(options);
    // .textlinrc directory
    const textlintRCDir = getConfigBaseDir(context);
    // create prh config
    const rulePaths = options.rulePaths || [];
    const ruleContents = options.ruleContents || [];
    // yaml file + yaml contents
    const prhEngineContent = createPrhEngineFromContents(ruleContents);
    const prhEngineFiles = createPrhEngine(rulePaths, textlintRCDir);
    const prhEngine = mergePrh(prhEngineFiles, prhEngineContent);
    const helper = new RuleHelper(context);
    const { Syntax, getSource, report, fixer, RuleError } = context;
    return {
        [Syntax.Str](node) {
            if (helper.isChildNode(node, [Syntax.Link, Syntax.Image, Syntax.BlockQuote, Syntax.Emphasis])) {
                return;
            }
            const text = getSource(node);
            // to get position from index
            // https://github.com/prh/prh/issues/29
            const dummyFilePath = "";
            const makeChangeSet = prhEngine.makeChangeSet(dummyFilePath, text);
            forEachChange(makeChangeSet, text, ({ matchStartIndex, matchEndIndex, actual, expected }) => {
                // If result is not changed, should not report
                if (actual === expected) {
                    return;
                }

                const messages = actual + " => " + expected;
                report(
                    node,
                    new RuleError(messages, {
                        index: matchStartIndex,
                        fix: fixer.replaceTextRange([matchStartIndex, matchEndIndex], expected)
                    })
                );
            });
        }
    };
}

module.exports = {
    linter: reporter,
    fixer: reporter
};

@@ -74,16 +74,280 @@
 helper%22;
+%0A/**%0A * RegExp#flags polyfill%0A */%0Aif (RegExp.prototype.flags === undefined) %7B%0A    Object.defineProperty(RegExp.prototype, %22flags%22, %7B%0A        configurable: true,%0A        get: function() %7B%0A            return this.toString().match(/%5Bgimuy%5D*$/)%5B0%5D;%0A        %7D%0A    %7D);%0A%7D
 %0A%0Aconst 

Add RegExp.prototype.flags workaround for Node.js v4