// LICENSE : MIT
"use strict";
import {RuleHelper} from "textlint-rule-helper";
import {getTokenizer} from "kuromojin";
import splitSentences, {Syntax as SentenceSyntax} from "sentence-splitter";
import StringSource from "textlint-util-to-string";
// 助詞どうか
const is助詞Token = (token) => {
    return token.pos === "助詞";
};
const is読点Token = (token) => {
    return token.surface_form === "、" && token.pos === "名詞";
};
// 助詞tokenから品詞細分類1までを元にしたkeyを作る
const createKeyFromKey = (token) => {
    // e.g.) は:助詞.係助詞
    return `${token.surface_form}:${token.pos}.${token.pos_detail_1}`
};
// keyからsurfaceを取り出す
const restoreToSurfaceFromKey = (key) => {
    return key.split(":")[0];
};
/**
 * Create token map object
 * {
 *  "で": [token, token],
 *  "の": [token, token]
 * }
 * @param tokens
 * @returns {*}
 */
function createSurfaceKeyMap(tokens) {
    // 助詞のみを対象とする
    return tokens.filter(is助詞Token).reduce((keyMap, token) => {
        // "は:助詞.係助詞" : [token]
        const tokenKey = createKeyFromKey(token);
        if (!keyMap[tokenKey]) {
            keyMap[tokenKey] = [];
        }
        keyMap[tokenKey].push(token);
        return keyMap;
    }, {});
}
function matchExceptionRule(tokens) {
    let token = tokens[0];
    // "の" の重なりは例外
    if (token.pos_detail_1 === "連体化") {
        return true;
    }
    // "を" の重なりは例外
    if (token.pos_detail_1 === "格助詞" && token.surface_form === "を") {
        return true;
    }
    // 接続助詞 "て" の重なりは例外
    if (token.pos_detail_1 === "接続助詞" && token.surface_form === "て") {
        return true;
    }
    return false;
}
/*
    default options
 */
const defaultOptions = {
    min_interval: 1,
    strict: false
};

/*
    1. Paragraph Node -> text
    2. text -> sentences
    3. tokenize sentence
    4. report error if found word that match the rule.

    TODO: need abstraction
 */
export default function (context, options = {}) {
    const helper = new RuleHelper(context);
    // 最低間隔値
    const minInterval = options.min_interval || defaultOptions.min_interval;
    const isStrict = options.strict || defaultOptions.strict;
    const {Syntax, report, getSource, RuleError} = context;
    return {
        [Syntax.Paragraph](node){
            if (helper.isChildNode(node, [Syntax.Link, Syntax.Image, Syntax.BlockQuote, Syntax.Emphasis])) {
                return;
            }
            const source = new StringSource(node);
            const text = source.toString();
            const isSentenceNode = node => {
                return node.type === SentenceSyntax.Sentence;
            };
            let sentences = splitSentences(text, {
                charRegExp: /[。\?\!？！]/
            }).filter(isSentenceNode);
            return getTokenizer().then(tokenizer => {
                const checkSentence = (sentence) => {
                    let tokens = tokenizer.tokenizeForSentence(sentence.raw);
                    let countableTokens = tokens.filter(token => {
                        if (isStrict) {
                            return is助詞Token(token);
                        }
                        // デフォルトでは、"、"を間隔値の距離としてカウントする
                        // "、" があると助詞同士の距離が開くようにすることで、並列的な"、"の使い方を許容する目的
                        // https://github.com/azu/textlint-rule-no-doubled-joshi/issues/2
                        return is助詞Token(token) || is読点Token(token);
                    });
                    let joshiTokenSurfaceKeyMap = createSurfaceKeyMap(countableTokens);
                    /*
                    # Data Structure

                        joshiTokens = [tokenA, tokenB, tokenC, tokenD, tokenE, tokenF]
                        joshiTokenSurfaceKeyMap = {
                            "は:助詞.係助詞": [tokenA, tokenC, tokenE],
                            "で:助詞.係助詞": [tokenB, tokenD, tokenF]
                        }
                     */
                    Object.keys(joshiTokenSurfaceKeyMap).forEach(key => {
                        const tokens = joshiTokenSurfaceKeyMap[key];
                        const joshiName = restoreToSurfaceFromKey(key);
                        // strict mode ではない時例外を除去する
                        if (!isStrict) {
                            if (matchExceptionRule(tokens)) {
                                return;
                            }
                        }
                        if (tokens.length <= 1) {
                            return;// no duplicated token
                        }
                        // if found differenceIndex less than
                        // tokes are sorted ascending order
                        tokens.reduce((prev, current) => {
                            let startPosition = countableTokens.indexOf(prev);
                            let otherPosition = countableTokens.indexOf(current);
                            // if difference
                            let differenceIndex = otherPosition - startPosition;
                            if (differenceIndex <= minInterval) {
                                let originalPosition = source.originalPositionFor({
                                    line: sentence.loc.start.line,
                                    column: sentence.loc.start.column + (current.word_position - 1)
                                });
                                // padding position
                                var padding = {
                                    line: originalPosition.line - 1,
                                    // matchLastToken.word_position start with 1
                                    // this is padding column start with 0 (== -1)
                                    column: originalPosition.column
                                };
                                report(node, new RuleError(`一文に二回以上利用されている助詞 "${joshiName}" がみつかりました。`, padding));
                            }
                            return current;
                        });
                    });
                };
                sentences.forEach(checkSentence);
            });
        }
    }
};

@@ -4637,18 +4637,20 @@
         
-le
+cons
 t startP
@@ -4718,18 +4718,20 @@
         
-le
+cons
 t otherP
@@ -4809,21 +4809,36 @@
  // 
-if difference
+%E5%8A%A9%E8%A9%9Etoken%E5%90%8C%E5%A3%AB%E3%81%AE%E8%B7%9D%E9%9B%A2%E3%81%8C%E8%A8%AD%E5%AE%9A%E5%80%A4%E4%BB%A5%E4%B8%8B%E3%81%AA%E3%82%89%E3%82%A8%E3%83%A9%E3%83%BC%E3%82%92%E5%A0%B1%E5%91%8A%E3%81%99%E3%82%8B
 %0A   
@@ -4862,18 +4862,20 @@
         
-le
+cons
 t differ
@@ -5015,18 +5015,20 @@
         
-le
+cons
 t origin
@@ -5323,16 +5323,21 @@
 position
+%E3%82%92%E8%A8%88%E7%AE%97%E3%81%99%E3%82%8B
 %0A       
@@ -5365,11 +5365,13 @@
     
-var
+const
  pad

use const insteadof let