"use strict";
var fs = require('fs');
var path = require('path');
var crypto = require('crypto');
const TextAnalysisManagerBase = require("./textAnalysisManagerBase");
const SentenceRule = require("./sentenceRule");
const TokenRuleConditionServices = require("./tokenRuleConditionServices");
const fsServices = require("./fsServices");

module.exports = class TextAnalysisManager extends TextAnalysisManagerBase {
    constructor(options, language, ruleType, category, ruleId) {
        super(options, language, ruleType);
        this.isProduction = true;

        this.rootFolder += "generated/";
        this.localRootFolder += "generated/";
        // var self = this;
        // dictionaries
        this.ReadDictionaries();
        var conditions = require(this.localRootFolder + 'conditions/conditions');
        this.conditions = {};
        for (var key in conditions) {
            var jsonCondition = conditions[key];
            var condition = TokenRuleConditionServices.Instanciate(this, jsonCondition);
            if (!condition) {
                debugger;
                continue;
            }
            this.conditions[key] = condition;
        }
        // functions
        this.ReadFunctions();
        // annotator rules
        var folderName = "annotatorRules";
        this.LoadPostTaggerRules(folderName, this.annotatorRules);
        // phrase rules
        this.LoadPhraseRules();
        // rules
        this.LoadAllRules(ruleType, category, ruleId);
        // conflict Rules
        this.UnstreamConflictRules();
    }
    static CleanGeneratedFolder() {
    }
    // static AnalyseDependencies(localRootFolder)
    // {
    //     var rules = {};
    //     try {
    //         rules = require(localRootFolder + "lessonRules");
    //     }
    //     catch (e) {
    //         debugger;
    //     }
    // }
    static RecSearchPhraseInCondition(data, phrases, macros) {
        var type = Object.prototype.toString.call(data);
        if (type == "[object Array]") {
            for (var i = 0; i < data.length; i++) {
                TextAnalysisManager.RecSearchPhraseInCondition(data[i], phrases, macros);
            }
        }
        else if (type == "[object Object]") {
            if ((data.operator == "isPhrase" ||
                data.operator == "isNotPhrase" ||
                data.operator == "isPartOfPhrase" ||
                data.operator == "isNotPartOfPhrase") && data.attribute) {
                if (phrases.indexOf(data.attribute) == -1) {
                    phrases.push(data.attribute);
                }
            }
            if (data.macro) {
                var rootCondition = macros[data.macro];
                if (rootCondition) {
                    TextAnalysisManager.RecSearchPhraseInCondition(rootCondition, phrases, macros);
                }
                else {
                    debugger;
                }
            }
            // "operator": "isPhrase",
            // "attribute": "as-long-as"
            for (var key in data) {
                TextAnalysisManager.RecSearchPhraseInCondition(data[key], phrases, macros);
            }
        }
    }
    static RecSearchPhraseIdInOverload(data, rule, phraseRules) {
        var type = Object.prototype.toString.call(data);
        if (type == "[object Array]") {
            for (var i = 0; i < data.length; i++) {
                TextAnalysisManager.RecSearchPhraseIdInOverload(data[i], rule, phraseRules);
            }
        }
        else if (type == "[object Object]") {
            if (data.object == "phrase" &&
                data.modifications &&
                data.modifications.id &&
                data.modifications.id != "") {
                var phraseId = data.modifications.id;
                // exceptionPhrases[data.modifications.id] = true;
                if (!phraseRules.phraseRules[phraseId]) {
                    phraseRules.phraseRules[phraseId] = [];
                }
                phraseRules.phraseRules[phraseId].push(rule.id);
            }
            // "operator": "isPhrase",
            // "attribute": "as-long-as"
            for (var key in data) {
                TextAnalysisManager.RecSearchPhraseIdInOverload(data[key], rule, phraseRules);
            }
        }
    }
    static RecSearchLearningOverloads(data, overloads) {
        var type = Object.prototype.toString.call(data);
        if (type == "[object Array]") {
            for (var i = 0; i < data.length; i++) {
                TextAnalysisManager.RecSearchLearningOverloads(data[i], overloads);
            }
        }
        else if (type == "[object Object]") {
            var modifNode = null;
            if (data.object == "learningCard" &&
                data.modifications) {
                modifNode = data.modifications;
            }
            else if (data.overloads && data.overloads.learningCard) {
                modifNode = data.overloads.learningCard;
            }
            if (modifNode) {
                if (modifNode.title &&
                    modifNode.title != "") {
                    if (modifNode.lessonNumber) {
                        overloads.push({
                            title: modifNode.title,
                            lessonNumber: modifNode.lessonNumber
                        });
                    }
                    else {
                        overloads.push({ title: modifNode.title });
                    }
                }
                else if (modifNode.lessonNumber) {
                    overloads.push({ lessonNumber: modifNode.lessonNumber });
                }
            }
            else {
                // "operator": "isPhrase",
                // "attribute": "as-long-as"
                for (var key in data) {
                    TextAnalysisManager.RecSearchLearningOverloads(data[key], overloads);
                }
            }
        }
    }
    static RecComputeRuleDepth(rules, id, phraseRules, ruleUsingPhrases) {
        var rule = rules[id];
        if (!rule) {
            debugger;
            return 0;
        }
        if (rule.depth == undefined) {
            rule.depth = 0;
            var phrases = ruleUsingPhrases[id];
            if (phrases && phrases.length) {
                var maxSubDepth = -1;
                for (var pi = 0; pi < phrases.length; pi++) {
                    var localphraseRules = phraseRules[phrases[pi]];
                    if (localphraseRules) {
                        for (var ri = 0; ri < localphraseRules.length; ri++) {
                            var depth = TextAnalysisManager.RecComputeRuleDepth(rules, localphraseRules[ri], phraseRules, ruleUsingPhrases);
                            if (maxSubDepth < depth) {
                                maxSubDepth = depth;
                            }
                        }
                    }
                }
                rule.depth = maxSubDepth + 1;
            }
        }
        return rule.depth;
    }
    static ComputeRuleDepth(rules, phraseRules, ruleUsingPhrases) {
        for (var id in rules) {
            var rule = rules[id];
            if (rule && rule.depth != undefined) {
                delete rule.depth;
            }
        }
        for (var id in rules) {
            TextAnalysisManager.RecComputeRuleDepth(rules, id, phraseRules, ruleUsingPhrases);
        }
    }
    static UpdateRuleLists(prefixFolfers, language) {

        // var pwd = process.env.PWD;
        // var dirName= __dirname;
        // localRootFolder
        var localRootFolder = prefixFolfers && prefixFolfers.local ? prefixFolfers.local : "../";
        localRootFolder += language + "/";
        // rootFolder
        var rootFolder = prefixFolfers && prefixFolfers.root ? prefixFolfers.root : "./sentenceAnalysis/";
        rootFolder += language + "/";
        //
        try {
            var dictionaries = {};
            var dictionariesFile = rootFolder + "dictionaries.json";
            if (fs.existsSync(dictionariesFile))
            {
                dictionaries = require(localRootFolder+"dictionaries.json");
                fs.unlinkSync(dictionariesFile);
            }
            
            var folder = rootFolder + "dictionaries/";
            fs.readdirSync(folder).forEach(file => {
                if (file.length > 4 && file.endsWith(".csv")) {
                    var name = file.substring(0, file.length - 4);
                    var lines = fs.readFileSync(folder + file).toString().split(/(?:\r\n?|\n)/);
                    if (lines.length > 1) {
                        var firstColumns = lines[0].split(",");
                        var dictionary = dictionaries[name];
                        if (!dictionary)
                        {
                            dictionaries[name] = {
                                title: name,
                                description: "",
                                columns: firstColumns
                            };
                        }
                        else{
                            dictionaries[name].columns = firstColumns;
                        }
                    }
                }
            });
            fs.appendFileSync(dictionariesFile, JSON.stringify(dictionaries,null,2));
        }
        catch(e){
            // debugger;
        }

        // read macros
        var dicMacros = {};
        var folder = rootFolder + "macros/";
        var localFolder = localRootFolder + "macros/";
        if (fs.existsSync(folder)) {
            fs.readdirSync(folder).forEach(file => {
                if (file.length > 5 && file.endsWith(".json")) {
                    var name = file.substring(0, file.length - 5);

                    var localRuleFileName = localFolder + name;
                    var macros = require(localRuleFileName);

                    for (var mi = 0; mi < macros.length; mi++) {
                        var macro = macros[mi];
                        if (!macro.name) {
                            debugger;
                            continue;
                        }
                        dicMacros[macro.name] = macro;
                    }
                }
            });
        }
        // read phrases
        var dicoPhraseRules = { "phraseRules": {}, "lessonRules": {}, "errorRules": {} };
        var ruleUsingPhrases = { "phraseRules": {}, "lessonRules": {}, "errorRules": {} };
        // var exceptionPhrases={};
        //
        var ruleType = "phraseRules";
        var folder = rootFolder + ruleType + "/";
        var localFolder = localRootFolder + ruleType + "/";
        // var phrase
        var allPhraseRules = {};
        if (fs.existsSync(folder)) {
            fs.readdirSync(folder).forEach(file => {
                if (file.length > 5 && file.endsWith(".json")) {
                    var rulesName = file.substring(0, file.length - 5);
                    var localRuleFileName = localFolder + rulesName;
                    try {
                        var json = require(localRuleFileName);
                        var rootNode = json[ruleType];
                        if (rootNode) {
                            var streamRuleFile = false;
                            for (var i = 0; i < rootNode.length; i++) {

                                var rule = rootNode[i];
                                var id = rule.id;
                                if (!id) {
                                    id = TextAnalysisManagerBase.GetId(allPhraseRules);
                                    rule.id = id;
                                    streamRuleFile = true;
                                }
                                allPhraseRules[id] = {
                                    "type": rule.type,
                                    "file": rulesName,
                                    "depth": 0
                                };
                                if (rule.phrase &&
                                    rule.phrase.id &&
                                    rule.phrase.id != "") {
                                    // exceptionPhrases[rule.phrase.id] = true;
                                    if (!dicoPhraseRules.phraseRules[rule.phrase.id]) {
                                        dicoPhraseRules.phraseRules[rule.phrase.id] = [];
                                    }
                                    dicoPhraseRules.phraseRules[rule.phrase.id].push(rule.id);
                                }
                                TextAnalysisManager.RecSearchPhraseIdInOverload(rule, rule, dicoPhraseRules);
                                var phrases = [];
                                TextAnalysisManager.RecSearchPhraseInCondition(rule.tokenRules, phrases, dicMacros);
                                if (phrases.length) {
                                    ruleUsingPhrases.phraseRules[rule.id] = phrases;
                                }
                            }
                            if (streamRuleFile) {
                                var ruleFileName = folder + file;
                                if (fs.existsSync(ruleFileName)) {
                                    fs.unlinkSync(ruleFileName);
                                }
                                fs.appendFileSync(ruleFileName, JSON.stringify(json, null, 2));
                            }
                        }
                    } catch (e) {
                        debugger;
                    }
                }
            });
        }
        TextAnalysisManager.ComputeRuleDepth(allPhraseRules, dicoPhraseRules.phraseRules, ruleUsingPhrases.phraseRules);
        if (fs.existsSync(folder)) {
            fs.readdirSync(folder).forEach(file => {
                if (file.length > 5 && file.endsWith(".json")) {
                    var rulesName = file.substring(0, file.length - 5);
                    var localRuleFileName = localFolder + rulesName;
                    try {
                        var json = require(localRuleFileName);
                        var rootNode = json[ruleType];
                        if (rootNode) {
                            var streamRuleFile = false;
                            for (var i = 0; i < rootNode.length; i++) {

                                var rule = rootNode[i];
                                if (rule.id) {
                                    var depth = 0;
                                    var ruleData = allPhraseRules[rule.id];
                                    if (ruleData == undefined) {
                                        debugger;
                                        // continue;
                                        depth = 0;
                                    }
                                    else {
                                        depth = ruleData.depth;
                                    }
                                    if (rule.depth == undefined || rule.depth != depth) {
                                        rule.depth = depth;
                                        streamRuleFile = true;
                                    }
                                }
                                else {
                                    debugger;
                                }
                            }
                            if (streamRuleFile) {
                                var ruleFileName = folder + file;
                                if (fs.existsSync(ruleFileName)) {
                                    fs.unlinkSync(ruleFileName);
                                }
                                fs.appendFileSync(ruleFileName, JSON.stringify(json, null, 2));
                            }
                        }
                    } catch (e) {
                        debugger;
                    }
                }
            });
        }
        //
        // read lesson & error rules
        //
        var ruleTypes = ["lessonRules", "errorRules"];
        var types = ["lesson", "error"];
        var priorityGroups = {};
        var ruleStatistics = {};
        var usedIds = {};
        var allRules = {};
        for (var rti = 0; rti < ruleTypes.length; rti++) {
            var titles = {};
            var ruleType = ruleTypes[rti];
            var type = types[rti];

            var rules = {};
            try {
                rules = require(localRootFolder + ruleType);
            }
            catch (e) {
                debugger;
            }
            var folder = rootFolder + ruleType + "/";
            var localFolder = localRootFolder + ruleType + "/";

            if (fs.existsSync(folder)) {
                fs.readdirSync(folder).forEach(file => {
                    if (file.length > 5 && file.endsWith(".json")) {
                        var rulesName = file.substring(0, file.length - 5);
                        var localRuleFileName = localFolder + rulesName;
                        try {
                            var json = require(localRuleFileName);
                            var rootNode = json[ruleType];
                            if (rootNode) {
                                var streamRuleFile = false;
                                for (var i = 0; i < rootNode.length; i++) {
                                    var rule = rootNode[i];
                                    var id = rule.id;
                                    if (!id) {
                                        id = TextAnalysisManagerBase.GetId(rules);
                                        rule.id = id;
                                        streamRuleFile = true;
                                    }
                                    if (usedIds[id]) {
                                        console.error("already used id=" + id);
                                        debugger;
                                    }
                                    else {
                                        usedIds[id] = true;
                                    }
                                    if (rule.learningCard) {
                                        if (rule.learningCard.title != "" &&
                                            rule.learningCard.lessonNumber != undefined) {
                                            var key = rule.learningCard.title + "_" + rule.learningCard.lessonNumber;
                                            key = key.replace(" ","-");
                                            key = key.toLowerCase();
                                            if (!ruleStatistics[key]) {
                                                ruleStatistics[key] = {
                                                    lessonRules: 0,
                                                    errorRules: 0
                                                };
                                            }
                                            ruleStatistics[key][ruleType]++;
                                        }
                                    }
                                    else {
                                        console.error("LearningCard Missing: " + rule.id);
                                    }
                                    if (!rule.title || rule.title == "") {
                                        console.error("Title missing id: " + rule.id);
                                    }
                                    else {
                                        if (titles[rule.title]) {
                                            console.error("Title already used= " + rule.title);
                                        }
                                        else {
                                            titles[rule.title] = true;
                                        }
                                    }
                                    var ruleData = rules[id];
                                    if (ruleData) {
                                        if (ruleData.title != rule.title) {
                                            ruleData.title = rule.title;
                                        }
                                        if (ruleData.file != rulesName) {
                                            ruleData.file = rulesName;
                                        }
                                    }
                                    else {
                                        var newRule = {
                                            "category": "Miscellaneous",
                                            "status": "development",
                                            "title": rule.title,
                                            "type": type,
                                            "file": rulesName
                                        };
                                        if (rule.learningCard && rule.learningCard.title) {
                                            newRule.category = rule.learningCard.title;
                                        }
                                        rules[id] = newRule;
                                        console.log("New rule identified title: " + rule.title);
                                    }
                                    allRules[id] = rules[id];
                                    if (rule.priorityGroup) {
                                        if (!priorityGroups[rule.priorityGroup.name]) {
                                            priorityGroups[rule.priorityGroup.name] = {};
                                        }
                                        if (!priorityGroups[rule.priorityGroup.name][rule.priorityGroup.priority]) {
                                            priorityGroups[rule.priorityGroup.name][rule.priorityGroup.priority] = [];
                                        }
                                        priorityGroups[rule.priorityGroup.name][rule.priorityGroup.priority].push(id);
                                    }
                                    if (rule.phrase) {
                                        if (rule.phrase.id &&
                                            rule.phrase.id != "") {
                                            if (!dicoPhraseRules[ruleType][rule.phrase.id]) {
                                                dicoPhraseRules[ruleType][rule.phrase.id] = [];
                                            }
                                            dicoPhraseRules[ruleType][rule.phrase.id].push(rule.id);
                                        }
                                        else {
                                            debugger;
                                        }
                                    }
                                    var phrases = [];
                                    TextAnalysisManager.RecSearchPhraseInCondition(rule.tokenRules, phrases, dicMacros);
                                    if (phrases.length) {
                                        ruleUsingPhrases[ruleType][rule.id] = phrases;
                                    }
                                }
                                if (streamRuleFile) {
                                    var ruleFileName = folder + file;
                                    if (fs.existsSync(ruleFileName)) {
                                        fs.unlinkSync(ruleFileName);
                                    }
                                    fs.appendFileSync(ruleFileName, JSON.stringify(json, null, 2));
                                }
                            }
                        } catch (e) {
                            debugger;
                        }
                    }
                });
            }
            TextAnalysisManager.ComputeRuleDepth(rules, dicoPhraseRules[ruleType], ruleUsingPhrases[ruleType]);
            var ruleName = rootFolder + ruleType + ".json";
            if (fs.existsSync(ruleName)) {
                fs.unlinkSync(ruleName);
            }
            fs.appendFileSync(ruleName, JSON.stringify(rules, null, 2));
        }
        //
        // create/update phraseRules.json
        //
        var ruleName = rootFolder  + "phraseRules.json";
        var phrases = {};
        if (fs.existsSync(ruleName)) {
            phrases = require(localRootFolder + "phraseRules.json");
            fs.unlinkSync(ruleName);
        }
        for (var ruleType in dicoPhraseRules)
        {
            var dicoRTPhraseRules = dicoPhraseRules[ruleType];
            for (var key in dicoRTPhraseRules)
            {
                var phrase = phrases[key];
                if (!phrase)
                {
                    phrases[key] = {
                        public: 1,
                        description: key
                    };
                }
                else if (!phrase.title)
                {
                    phrase.title = phrase.description;
                    phrase.description = "";
                }
            }
        }
        fs.appendFileSync(ruleName, JSON.stringify(phrases, null, 2));
        //
        // identify errorRule having higher priority than lessonRule
        // 
        var errorRuleUsedByLessonRules = {};
        for (var id in priorityGroups) {
            var priorityGroup = priorityGroups[id];
            line += id + "\n";
            var lessons = [];
            for (var priority in priorityGroup) {
                for (var ri = 0; ri < priorityGroup[priority].length; ri++) {
                    var rule = allRules[priorityGroup[priority][ri]];
                    if (rule.type == "lesson") {
                        for (var li = 0; li < lessons.length; li++) {
                            errorRuleUsedByLessonRules[lessons[li]] = allRules[lessons[li]];
                        }
                        lessons = [];
                    }
                }
                for (var ri = 0; ri < priorityGroup[priority].length; ri++) {
                    var ruleId = priorityGroup[priority][ri];
                    var rule = allRules[ruleId];
                    if (rule.type == "error") {
                        lessons.push(ruleId);
                    }
                }
            }
        }
        var errorRuleUsedByLessonRulesPath = rootFolder + "errorRuleUsedByLessonRules.json";
        if (fs.existsSync(errorRuleUsedByLessonRulesPath)) {
            fs.unlinkSync(errorRuleUsedByLessonRulesPath);
        }
        fs.appendFileSync(errorRuleUsedByLessonRulesPath, JSON.stringify(errorRuleUsedByLessonRules, null, 2));
        //
        // identify lessonRule having higher priority than errorRule
        // 
        var lessonRuleUsedByErrorRules = {};
        for (var id in priorityGroups) {
            var priorityGroup = priorityGroups[id];
            line += id + "\n";
            var lessons = [];
            for (var priority in priorityGroup) {
                for (var ri = 0; ri < priorityGroup[priority].length; ri++) {
                    var rule = allRules[priorityGroup[priority][ri]];
                    if (rule.type == "error") {
                        for (var li = 0; li < lessons.length; li++) {
                            lessonRuleUsedByErrorRules[lessons[li]] = allRules[lessons[li]];
                        }
                        lessons = [];
                    }
                }
                for (var ri = 0; ri < priorityGroup[priority].length; ri++) {
                    var ruleId = priorityGroup[priority][ri];
                    var rule = allRules[ruleId];
                    if (rule.type == "lesson") {
                        lessons.push(ruleId);
                    }
                }
            }
        }
        //
        // errorRule using lesson phrases
        //
        var errorRulesUsingPhrases = ruleUsingPhrases.errorRules;
        for (var ruleId in errorRulesUsingPhrases) {
            var phrases = errorRulesUsingPhrases[ruleId];
            for (var pi = 0; pi < phrases.length; pi++) {
                var lessonRules = dicoPhraseRules.lessonRules[phrases[pi]];
                if (lessonRules) {
                    for (var lri = 0; lri < lessonRules.length; lri++) {
                        var ruleId = lessonRules[lri];
                        var rule = allRules[ruleId];
                        lessonRuleUsedByErrorRules[ruleId] = rule;
                    }
                }
            }
        }
        var lessonRuleUsedByErrorRulesPath = rootFolder + "lessonRuleUsedByErrorRules.json";
        if (fs.existsSync(lessonRuleUsedByErrorRulesPath)) {
            fs.unlinkSync(lessonRuleUsedByErrorRulesPath);
        }
        fs.appendFileSync(lessonRuleUsedByErrorRulesPath, JSON.stringify(lessonRuleUsedByErrorRules, null, 2));

        //
        // statistics
        // 
        //valeur du mois d'avril
        //var lcCount = 294; 
        var lcCount = 348;
        var minTime = 21;
        var maxTime = 60;
        // var lessonCopyTime = 7; // 4 - 9 
        // var errorCopyTime = 10; // 2 - 10
        var copyTime = 10;
        var line = "lesson,lessonRuleCount,errorRuleCount,minTime, maxTime";
        var count = 0;
        var minTotalTime = 0;
        var maxTotalTime = 0;

        for (var id in ruleStatistics) {
            count++;
            var errorRuleCount = ruleStatistics[id].errorRules;
            // if (!errorRuleCount) {
            //     errorRuleCount = 0;
            // }
            var lessonRuleCount = ruleStatistics[id].lessonRules;
            // if (!lessonRuleCount) {
            //     lessonRuleCount = 0;
            // }
            line += id;
            line += ",";
            line += lessonRuleCount;
            line += ",";
            line += errorRuleCount;
            line += ",";
            // var ruleTime = 2 * minTime + (lessonRuleCount + errorRuleCount - 2) * copyTime;
            var ruleTime = minTime * (lessonRuleCount + errorRuleCount);
            minTotalTime += ruleTime;
            line += ruleTime;
            line += ",";
            // ruleTime = 2 * maxTime + (lessonRuleCount + errorRuleCount - 2) * copyTime;
            ruleTime = maxTime * (lessonRuleCount + errorRuleCount)
            maxTotalTime += ruleTime;
            line += ruleTime;
            line += "\n";
        }
        lcCount -= count;
        console.log("Nombre de cartes restantes " +lcCount);
        var minMeanTime = minTotalTime / count;
        var maxMeanTime = maxTotalTime / count;
        console.log("Estimation(j) min: " + Math.round(minMeanTime * lcCount / (60 * 8)) + " max: " + Math.round(maxMeanTime * lcCount / (60 * 8)));
        var ruleStatisticsPath = rootFolder + "ruleStatistics.csv";
        if (fs.existsSync(ruleStatisticsPath)) {
            fs.unlinkSync(ruleStatisticsPath);
        }
        fs.appendFileSync(ruleStatisticsPath, line);
        //
        // ority group
        //
        //priorityGroups[rule.priorityGroup.id][rule.priorityGroup.priority].push(rules[id]);
        line = "";
        for (var id in priorityGroups) {
            var priorityGroup = priorityGroups[id];
            line += id + "\n";
            for (var priority in priorityGroup) {
                line += priority + "\n";
                var lessonLine = "lessons";
                var errorLine = "errors";
                for (var ri = 0; ri < priorityGroup[priority].length; ri++) {
                    var rule = allRules[priorityGroup[priority][ri]];
                    if (rule.type == "lesson") {
                        lessonLine += "," + rule.title;
                    }
                    else {
                        errorLine += "," + rule.title;
                    }
                }
                line += lessonLine;
                line += "\n";
                line += errorLine;
                line += "\n";
            }
            line += "\n";
        }
        var priorityGroupsPath = rootFolder + "priorityGroups.csv";
        if (fs.existsSync(priorityGroupsPath)) {
            fs.unlinkSync(priorityGroupsPath);
        }
        fs.appendFileSync(priorityGroupsPath, line);
    }
    static CheckLearningCardKey(dbLearningCards, title, lessonNumber, id) {
        if (title != "" && lessonNumber >= 0) {
            var key = title.toLowerCase().replace(" ", "-") + "_" + lessonNumber;
            if (!dbLearningCards[key]) {
                console.error("No learning card in db: " + title + " " + lessonNumber + " id: " + id);
                debugger;
            }
        }
    }
    static CheckLearningCard(dbLearningCards, rootNode) {
        if (!dbLearningCards) {
            return;
        }
        if (rootNode.learningCard) {
            var id = rootNode.id;
            TextAnalysisManager.CheckLearningCardKey(dbLearningCards, rootNode.learningCard.title, rootNode.learningCard.lessonNumber, id);
            var overloads = [];
            TextAnalysisManager.RecSearchLearningOverloads(rootNode, overloads);
            for (var i = 0; i < overloads.length; i++) {
                var title = rootNode.learningCard.title;
                var lessonNumber = rootNode.learningCard.lessonNumber;
                if (overloads[i].title) {
                    title = overloads[i].title;
                }
                if (overloads[i].lessonNumber) {
                    lessonNumber = overloads[i].lessonNumber;
                }

                TextAnalysisManager.CheckLearningCardKey(dbLearningCards, title, lessonNumber, id);
            }
        }
    }

    static BuildRules(language, dbLearningCards) {
        var rootFolder = "./sentenceAnalysis/" + language + "/";
        var localRootFolder = "../" + language + "/";

        var dicoConditions = {};
        var data = {
            macros: {}
        };

        var folder = rootFolder + "macros/";
        var localFolder = localRootFolder + "macros/";
        if (fs.existsSync(folder)) {
            fs.readdirSync(folder).forEach(file => {
                if (file.length > 5 && file.endsWith(".json")) {
                    var name = file.substring(0, file.length - 5);

                    var localRuleFileName = localFolder + name;
                    var macros = require(localRuleFileName);

                    for (var mi = 0; mi < macros.length; mi++) {
                        var macro = macros[mi];
                        if (!macro.name) {
                            debugger;
                            continue;
                        }
                        TokenRuleConditionServices.Build(data, macro, dicoConditions);
                        if (!macro.conditionId) {
                            debugger;
                            continue;
                        }
                        data.macros[macro.name] = macro;
                    }
                }
            });
        }
        var rootGeneratedFolder = rootFolder + "generated/";
        // conflict Rules
        var conflictRuleNames = ["lesson","error","lessonError"];
        for (var i=0; i<conflictRuleNames.length;i++)
        {
            var conflictRules = require(localRootFolder + conflictRuleNames[i] + "ConflictRules");
            if (conflictRules) {
                for (var cri = 0; cri < conflictRules.length; cri++) {
                    var conflictRule = conflictRules[cri];
                    if (conflictRule.rule1.title)
                        delete conflictRule.rule1.title;
                    if (conflictRule.rule1.type)
                        delete conflictRule.rule1.type;
                    if (conflictRule.rule2.title)
                        delete conflictRule.rule2.title;
                        if (conflictRule.rule2.type)
                        delete conflictRule.rule2.type;
                    if (conflictRule.sentences) {
                        delete conflictRule.sentences;
                    }
                }
                var cRPath = rootGeneratedFolder + conflictRuleNames[i] + "ConflictRules.json";
                if (fs.existsSync(cRPath)) {
                    fs.unlinkSync(cRPath);
                }
                fs.appendFileSync(cRPath, JSON.stringify(conflictRules, null, 2));
            }
            else {
                debugger;
            }
        }

        var ruleTypes = ["annotatorRules", "phraseRules", "lessonRules", "errorRules"];
        var ruleLists = ["", "", "lessonRules", "errorRules"];

        for (var rti = 0; rti < ruleTypes.length; rti++) {
            var ruleType = ruleTypes[rti];
            var folder = rootFolder + ruleType + "/";
            var localFolder = localRootFolder + ruleType + "/";

            var generatedFolder = rootGeneratedFolder + ruleType + "/";

            if (fs.existsSync(generatedFolder)) {
                fs.readdirSync(generatedFolder).forEach(function (file, index) {
                    var curPath = generatedFolder + "/" + file;
                    // if (fs.lstatSync(curPath).isDirectory()) { // recurse
                    //   deleteFolderRecursive(curPath);
                    // } else { // delete file
                    fs.unlinkSync(curPath);
                    // }
                });
                //   fs.rmdirSync(generatedFolder);
            }
            else {
                fs.mkdirSync(generatedFolder);
            }
            var rules = null;
            var newRules = {};
            if (ruleLists[rti] != "") {
                rules = require(localRootFolder + ruleLists[rti]);
            }
            var rootNodes = [ruleType, "exceptions", "rulesOnResults"];

            if (fs.existsSync(folder)) {
                fs.readdirSync(folder).forEach(file => {
                    if (file.length > 5 && file.endsWith(".json")) {
                        var rulesName = file.substring(0, file.length - 5);
                        var json = require(localFolder + rulesName);
                        for (var rni = 0; rni < rootNodes.length; rni++) {
                            var rootNode = json[rootNodes[rni]];
                            if (rootNode) {
                                if (rni == 0 && rules) {
                                    for (var i = rootNode.length - 1; i >= 0; i--) {
                                        var rule = rules[rootNode[i].id];
                                        if (rule && rule.status == "production") {
                                            console.log("build rule= " + rule.title);

                                            TextAnalysisManager.CheckLearningCard(dbLearningCards, rootNode[i]);
                                            SentenceRule.Build(data, rootNode[i], dicoConditions);
                                            newRules[rootNode[i].id] = rule;
                                        }
                                        else {
                                            rootNode.splice(i, 1);
                                        }
                                    }
                                }
                                else {
                                    for (var i = 0; i < rootNode.length; i++) {
                                        SentenceRule.Build(data, rootNode[i], dicoConditions);
                                    }
                                }
                            }
                        }
                        var generatedFile = generatedFolder + path.basename(file);
                        if (fs.existsSync(generatedFile)) {
                            fs.unlinkSync(generatedFile);
                        }
                        if (json[ruleType].length) {
                            fs.appendFileSync(generatedFile, JSON.stringify(json));
                        }
                    }
                });
            }
            //
            //
            if (rules) {
                var generatedFile = rootGeneratedFolder + ruleLists[rti] + ".json";
                if (fs.existsSync(generatedFile)) {
                    fs.unlinkSync(generatedFile);
                }
                fs.appendFileSync(generatedFile, JSON.stringify(newRules));
            }
        }
        // errorRuleUsedByLessonRules
        var errorRuleUsedByLessonRules = require(localRootFolder + "errorRuleUsedByLessonRules");
        var prodErrorRuleUsedByLessonRules = {};
        for (var id in errorRuleUsedByLessonRules) {
            var rule = errorRuleUsedByLessonRules[id];
            if (rule.status == "production") {
                prodErrorRuleUsedByLessonRules[id] = rule;
            }
        }
        var filtePath = rootGeneratedFolder + "errorRuleUsedByLessonRules.json";
        if (fs.existsSync(filtePath)) {
            fs.unlinkSync(filtePath);
        }
        fs.appendFileSync(filtePath, JSON.stringify(prodErrorRuleUsedByLessonRules));
        // lessonRuleUsedByErrorRules
        var lessonRuleUsedByErrorRules = require(localRootFolder + "lessonRuleUsedByErrorRules");
        var prodLessonRuleUsedByErrorRules = {};
        for (var id in lessonRuleUsedByErrorRules) {
            var rule = lessonRuleUsedByErrorRules[id];
            if (rule.status == "production") {
                prodLessonRuleUsedByErrorRules[id] = rule;
            }
        }
        filtePath = rootGeneratedFolder + "lessonRuleUsedByErrorRules.json";
        if (fs.existsSync(filtePath)) {
            fs.unlinkSync(filtePath);
        }
        fs.appendFileSync(filtePath, JSON.stringify(prodLessonRuleUsedByErrorRules));

        // conditions
        var conditionFile = rootFolder + "generated/conditions/conditions.json";
        if (fs.existsSync(conditionFile)) {
            fs.unlinkSync(conditionFile);
        }
        fs.appendFileSync(conditionFile, JSON.stringify(dicoConditions));

        var target = rootFolder + "generated";
        // functions
        var folderName = "functions";
        fsServices.deleteFileRecursiveSync(target + "/" + folderName);
        fsServices.copyFolderRecursiveSync(rootFolder + folderName, target);
        // dictionaries
        folderName = "dictionaries";
        fsServices.deleteFileRecursiveSync(target + "/" + folderName);
        fsServices.copyFolderRecursiveSync(rootFolder + folderName, target);
    }
}