/*
 * SDK Pullenti Lingvo, version 4.33, fabruary 2026. Copyright (c) 2013-2026, Pullenti. All rights reserved. 
 * Non-Commercial Freeware and Commercial Software.
 * This class is generated using the converter Unisharping (www.unisharping.ru) from Pullenti C#.NET project. 
 * The latest version of the code is available on the site www.pullenti.ru
 */


const MorphGender = require("./../../morph/MorphGender");
const SemObject = require("./../SemObject");
const MorphClass = require("./../../morph/MorphClass");
const SemObjectType = require("./../SemObjectType");
const SemLinkType = require("./../SemLinkType");
const MorphWordForm = require("./../../morph/MorphWordForm");
const AnaforHelper = require("./AnaforHelper");
const Token = require("./../../ner/Token");
const MetaToken = require("./../../ner/MetaToken");

class OptimizerHelper {
    
    static optimize(doc, pars) {
        for (const blk of doc.blocks) {
            for (const fr of blk.fragments) {
                OptimizerHelper._optimizeGraph(fr.graph);
            }
            let objs = new Array();
            objs.splice(objs.length, 0, ...blk.graph.objects);
            for (const fr of blk.fragments) {
                objs.splice(objs.length, 0, ...fr.graph.objects);
            }
            for (const fr of blk.fragments) {
                for (let i = fr.graph.links.length - 1; i >= 0; i--) {
                    let li = fr.graph.links[i];
                    if (!objs.includes(li.source) || !objs.includes(li.target)) 
                        fr.graph.removeLink(li);
                }
                OptimizerHelper._processParticiples(fr.graph);
                OptimizerHelper._processLinks(fr.graph);
            }
            OptimizerHelper._sortObjects(objs);
            OptimizerHelper._processPointers(objs);
            OptimizerHelper._processFormulas(objs);
            if (pars.dontCreateAnafor) {
            }
            else {
                AnaforHelper.processAnafors(objs);
                for (const fr of blk.fragments) {
                    OptimizerHelper._collapseAnafors(fr.graph);
                }
            }
        }
    }
    
    static _optimizeGraph(gr) {
        for (const o of gr.objects) {
            OptimizerHelper._optimizeTokens(o);
        }
        OptimizerHelper._sortObjects(gr.objects);
    }
    
    static _compareToks(t1, t2) {
        if (t1.beginChar < t2.beginChar) 
            return -1;
        if (t1.beginChar > t2.beginChar) 
            return 1;
        if (t1.endChar < t2.endChar) 
            return -1;
        if (t1.endChar > t2.endChar) 
            return 1;
        return 0;
    }
    
    static _optimizeTokens(o) {
        for (let i = 0; i < o.tokens.length; i++) {
            let ch = false;
            for (let j = 0; j < (o.tokens.length - 1); j++) {
                if (OptimizerHelper._compareToks(o.tokens[j], o.tokens[j + 1]) > 0) {
                    let t = o.tokens[j];
                    o.tokens[j] = o.tokens[j + 1];
                    o.tokens[j + 1] = t;
                    ch = true;
                }
            }
            if (!ch) 
                break;
        }
        for (let i = 0; i < (o.tokens.length - 1); i++) {
            if (o.tokens[i].endToken.next === o.tokens[i + 1].beginToken) {
                o.tokens[i] = new MetaToken(o.tokens[i].beginToken, o.tokens[i + 1].endToken);
                o.tokens.splice(i + 1, 1);
                i--;
            }
        }
    }
    
    static _sortObjects(objs) {
        for (let i = 0; i < objs.length; i++) {
            let ch = false;
            for (let j = 0; j < (objs.length - 1); j++) {
                if (objs[j].compareTo(objs[j + 1]) > 0) {
                    let o = objs[j];
                    objs[j] = objs[j + 1];
                    objs[j + 1] = o;
                    ch = true;
                }
            }
            if (!ch) 
                break;
        }
    }
    
    static _processParticiples(gr) {
        let ret = false;
        for (let i = 0; i < gr.objects.length; i++) {
            let obj = gr.objects[i];
            if (obj.typ !== SemObjectType.PARTICIPLE) 
                continue;
            let own = null;
            let has = false;
            for (const li of obj.linksTo) {
                if (li.typ === SemLinkType.PARTICIPLE) 
                    own = li;
                else 
                    has = true;
            }
            if (!has) 
                continue;
            if (own === null) {
                let dum = SemObject._new3820(gr, SemObjectType.NOUN);
                if (obj.morph !== null) 
                    dum.morph = MorphWordForm._new3821(MorphClass.NOUN, obj.morph.number, obj.morph.gender, obj.morph._case);
                gr.objects.push(dum);
                own = gr.addLink(SemLinkType.PARTICIPLE, dum, obj, "какой", false, null);
                ret = true;
            }
            for (let j = obj.linksTo.length - 1; j >= 0; j--) {
                let li = obj.linksTo[j];
                if (li.typ === SemLinkType.PARTICIPLE) 
                    continue;
                let exi = false;
                for (const ll of li.source.linksFrom) {
                    if (ll.target === own.source) 
                        exi = true;
                }
                if (exi) 
                    gr.removeLink(li);
                else {
                    obj.linksTo.splice(j, 1);
                    li.m_Target = own.source;
                }
                ret = true;
            }
        }
        return ret;
    }
    
    static _processLinks(gr) {
        let ret = false;
        for (let i = 0; i < gr.objects.length; i++) {
            let obj = gr.objects[i];
            for (let j = obj.linksFrom.length - 1; j >= 0; j--) {
                let li = obj.linksFrom[j];
                if (li.typ !== SemLinkType.PACIENT) 
                    continue;
                let exi = false;
                for (const ll of obj.linksFrom) {
                    if (ll !== li && ll.typ === SemLinkType.AGENT && ll.target === li.target) 
                        exi = true;
                }
                if (exi) {
                    if (obj.beginChar > li.target.beginChar) {
                        gr.removeLink(li);
                        ret = true;
                    }
                }
            }
        }
        return ret;
    }
    
    static _collapseAnafors(gr) {
        let ret = false;
        for (let i = 0; i < gr.objects.length; i++) {
            let obj = gr.objects[i];
            if (obj.typ === SemObjectType.PERSONALPRONOUN || obj.morph.normalFull === "КОТОРЫЙ") {
            }
            else 
                continue;
            if (obj.attrs.length > 0 || obj.quantity !== null) 
                continue;
            if (obj.linksFrom.length === 1 && obj.linksFrom[0].typ === SemLinkType.ANAFOR) {
            }
            else if (obj.linksFrom.length === 2 && obj.linksFrom[0].typ === SemLinkType.ANAFOR && obj.linksFrom[0].altLink === obj.linksFrom[1]) {
            }
            else 
                continue;
            let alink = obj.linksFrom[0];
            for (const li of obj.linksTo) {
                let nli = gr.addLink(li.typ, li.source, alink.target, li.question, li.isOr, li.preposition);
                if (alink.altLink !== null) {
                    let nli2 = gr.addLink(li.typ, li.source, alink.altLink.target, li.question, li.isOr, li.preposition);
                    nli2.altLink = nli;
                    nli.altLink = nli2;
                }
            }
            gr.removeObject(obj);
            i--;
            ret = true;
        }
        return ret;
    }
    
    static _processFormulas(objs) {
        let ret = false;
        for (let i = 0; i < objs.length; i++) {
            let o = objs[i];
            if (o.typ !== SemObjectType.NOUN || !o.isValue("РАЗ", SemObjectType.UNDEFINED)) 
                continue;
            if (o.quantity === null) 
                continue;
            if (o.linksFrom.length === 0 && o.linksTo.length === 1) {
            }
            else 
                continue;
            let frm = o.linksTo[0].source;
            for (let k = 0; k < 5; k++) {
                let brek = false;
                for (const li of frm.linksFrom) {
                    if (((li.typ === SemLinkType.DETAIL || li.typ === SemLinkType.PACIENT)) && li.target !== o) {
                        if (o.beginChar > frm.endChar && (o.beginChar < li.target.beginChar)) {
                            brek = true;
                            o.graph.addLink(SemLinkType.DETAIL, o, li.target, "чего", false, null);
                            o.graph.removeLink(li);
                        }
                        else 
                            frm = li.target;
                        break;
                    }
                }
                if (brek) 
                    break;
            }
        }
        return ret;
    }
    
    static _processPointers(objs) {
        let ret = false;
        for (let i = 0; i < objs.length; i++) {
            let o = objs[i];
            if (o.typ !== SemObjectType.NOUN) 
                continue;
            if (o.quantity !== null && o.quantity.spelling === "1") {
            }
            else 
                continue;
            if (o.linksFrom.length > 0) 
                continue;
            let ok = false;
            for (let j = i - 1; j >= 0; j--) {
                let oo = objs[j];
                if (oo.typ !== SemObjectType.NOUN) 
                    continue;
                if (oo.morph.normalFull !== o.morph.normalFull) 
                    continue;
                if (oo.quantity !== null && oo.quantity.spelling !== "1") {
                    ok = true;
                    break;
                }
            }
            if (!ok) {
                for (let j = i + 1; j < objs.length; j++) {
                    let oo = objs[j];
                    if (oo.typ !== SemObjectType.NOUN) 
                        continue;
                    if (oo.morph.normalFull !== o.morph.normalFull) 
                        continue;
                    if (oo.findFromObject("ДРУГОЙ", SemLinkType.UNDEFINED, SemObjectType.UNDEFINED) !== null || oo.findFromObject("ВТОРОЙ", SemLinkType.UNDEFINED, SemObjectType.UNDEFINED) !== null) {
                        ok = true;
                        break;
                    }
                }
            }
            if (!ok) 
                continue;
            let first = SemObject._new3820(o.graph, SemObjectType.ADJECTIVE);
            first.tokens.push(o.tokens[0]);
            first.morph.normalFull = "ПЕРВЫЙ";
            first.morph.normalCase = (((o.morph.gender.value()) & (MorphGender.FEMINIE.value())) !== (MorphGender.UNDEFINED.value()) ? "ПЕРВАЯ" : (((o.morph.gender.value()) & (MorphGender.NEUTER.value())) !== (MorphGender.UNDEFINED.value()) ? "ПЕРВОЕ" : "ПЕРВЫЙ"));
            first.morph.gender = o.morph.gender;
            o.graph.objects.push(first);
            o.graph.addLink(SemLinkType.DETAIL, o, first, "какой", false, null);
            o.quantity = null;
            ret = true;
        }
        for (let i = 0; i < objs.length; i++) {
            let o = objs[i];
            if (o.typ !== SemObjectType.NOUN) 
                continue;
            if (o.quantity !== null && o.quantity.spelling === "1") {
            }
            else 
                continue;
            let other = o.findFromObject("ДРУГОЙ", SemLinkType.UNDEFINED, SemObjectType.UNDEFINED);
            if (other === null) 
                continue;
            let ok = false;
            for (let j = i - 1; j >= 0; j--) {
                let oo = objs[j];
                if (oo.typ !== SemObjectType.NOUN) 
                    continue;
                if (oo.morph.normalFull !== o.morph.normalFull) 
                    continue;
                if (oo.findFromObject("ПЕРВЫЙ", SemLinkType.UNDEFINED, SemObjectType.UNDEFINED) !== null) {
                    ok = true;
                    break;
                }
            }
            if (ok) {
                other.morph.normalFull = "ВТОРОЙ";
                other.morph.normalCase = (((o.morph.gender.value()) & (MorphGender.FEMINIE.value())) !== (MorphGender.UNDEFINED.value()) ? "ВТОРАЯ" : (((o.morph.gender.value()) & (MorphGender.NEUTER.value())) !== (MorphGender.UNDEFINED.value()) ? "ВТОРОЕ" : "ВТОРОЙ"));
            }
        }
        return ret;
    }
}


module.exports = OptimizerHelper