/*
 * 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 Utils = require("./../../unisharp/Utils");
const StringBuilder = require("./../../unisharp/StringBuilder");

const MorphGender = require("./../../morph/MorphGender");
const MorphNumber = require("./../../morph/MorphNumber");
const MorphCase = require("./../../morph/MorphCase");
const MorphMood = require("./../../morph/MorphMood");
const MorphVoice = require("./../../morph/MorphVoice");
const MorphPerson = require("./../../morph/MorphPerson");
const MorphBaseInfo = require("./../../morph/MorphBaseInfo");
const MetaToken = require("./../../ner/MetaToken");
const TextToken = require("./../../ner/TextToken");
const NounPhraseToken = require("./../../ner/core/NounPhraseToken");
const VerbPhraseToken = require("./../../ner/core/VerbPhraseToken");
const SemanticService = require("./../SemanticService");
const MeasureKind = require("./../../ner/measure/MeasureKind");
const NGLinkType = require("./NGLinkType");
const SentItemType = require("./SentItemType");
const SemanticRole = require("./../core/SemanticRole");
const UnitsHelper = require("./../../ner/measure/internal/UnitsHelper");
const Token = require("./../../ner/Token");
const UnitToken = require("./../../ner/measure/internal/UnitToken");
const SemanticHelper = require("./../core/SemanticHelper");
const NumbersWithUnitToken = require("./../../ner/measure/internal/NumbersWithUnitToken");

class NGLink {
    
    constructor() {
        this.typ = NGLinkType.UNDEFINED;
        this.from = null;
        this.to = null;
        this.toVerb = null;
        this.coef = 0;
        this.plural = -1;
        this.fromIsPlural = false;
        this.reverce = false;
        this.toAllListItems = false;
        this.canBePacient = false;
        this.canBeParticiple = false;
        this.altLink = null;
    }
    
    get fromMorph() {
        if (this.from.source.source !== null) 
            return this.from.source.source.morph;
        return null;
    }
    
    get fromPrep() {
        return (this.from.source.prep != null ? this.from.source.prep : "");
    }
    
    get toMorph() {
        if (this.to !== null && this.to.source.source !== null) 
            return this.to.source.source.morph;
        return null;
    }
    
    toString() {
        let tmp = new StringBuilder();
        tmp.append(this.coef).append(": ").append(this.typ.toString()).append(" ");
        if (this.plural === 1) 
            tmp.append(" PLURAL ");
        else if (this.plural === 0) 
            tmp.append(" SINGLE ");
        if (this.reverce) 
            tmp.append(" REVERCE ");
        tmp.append(this.from.source.toString());
        if (this.toAllListItems) 
            tmp.append(" ALLLISTITEMS ");
        if (this.to !== null) 
            tmp.append(" -> ").append(this.to.source.toString());
        else if (this.toVerb !== null) 
            tmp.append(" -> ").append(this.toVerb.toString());
        if (this.altLink !== null) 
            tmp.append(" / ALTLINK: ").append(this.altLink.toString());
        return tmp.toString();
    }
    
    compareTo(other) {
        if (this.coef > other.coef) 
            return -1;
        if (this.coef < other.coef) 
            return 1;
        return 0;
    }
    
    calcCoef(noplural = false) {
        this.coef = -1;
        this.canBePacient = false;
        this.toAllListItems = false;
        this.plural = -1;
        if (this.typ === NGLinkType.GENETIVE && this.to !== null) 
            this._calcGenetive();
        else if (this.typ === NGLinkType.NAME && this.to !== null) 
            this._calcName(noplural);
        else if (this.typ === NGLinkType.BE && this.to !== null) 
            this._calcBe();
        else if (this.typ === NGLinkType.LIST) 
            this._calcList();
        else if (this.typ === NGLinkType.PARTICIPLE && this.to !== null) 
            this._calcParticiple(noplural);
        else if (this.toVerb !== null && this.toVerb.firstVerb !== null) {
            if (this.typ === NGLinkType.AGENT) 
                this._calcAgent(noplural);
            else if (this.typ === NGLinkType.PACIENT) 
                this._calcPacient(noplural);
            else if (this.typ === NGLinkType.ACTANT) 
                this._calcActant();
        }
        else if (this.typ === NGLinkType.ADVERB) 
            this._calcAdverb();
    }
    
    _calcGenetive() {
        if (!this.from.source.canBeNoun) 
            return;
        if (this.from.source.typ === SentItemType.FORMULA) {
            if (this.to.source.typ !== SentItemType.NOUN) 
                return;
            this.coef = SemanticService.PARAMS.transitiveCoef;
            return;
        }
        let frmorph = this.fromMorph;
        if (this.to.source.typ === SentItemType.FORMULA) {
            if (this.from.source.typ !== SentItemType.NOUN) 
                return;
            if (frmorph._case.isGenitive) 
                this.coef = SemanticService.PARAMS.transitiveCoef;
            else if (frmorph._case.isUndefined) 
                this.coef = 0;
            return;
        }
        if (this.from.source.source instanceof NumbersWithUnitToken) {
            if (this.from.order !== (this.to.order + 1)) 
                return;
            let num = Utils.as(this.from.source.source, NumbersWithUnitToken);
            let ki = UnitToken.calcKind(num.units);
            if (ki !== MeasureKind.UNDEFINED) {
                if (UnitsHelper.checkKeyword(ki, this.to.source.source)) {
                    this.coef = SemanticService.PARAMS.nextModel * (3);
                    return;
                }
            }
            if (this.to.source.source instanceof NumbersWithUnitToken) 
                return;
        }
        let links = SemanticHelper.tryCreateLinks(this.to.source.source, this.from.source.source, null);
        if (links !== null) {
            for (const li of links) {
                if (li.role !== SemanticRole.AGENT) {
                    this.coef = li.rank;
                    if (this.coef < 0.5) 
                        this.coef = 0.5;
                    if (li.role === SemanticRole.STRONG || li.idiom) 
                        this.coef *= (2);
                    return;
                }
            }
        }
        let nonGenText = false;
        if (Utils.isNullOrEmpty(this.fromPrep) && !(this.from.source.source instanceof VerbPhraseToken)) {
            if (this.from.order !== (this.to.order + 1)) 
                nonGenText = true;
        }
        if (nonGenText || !Utils.isNullOrEmpty(this.fromPrep)) 
            return;
        let cas0 = frmorph._case;
        if (cas0.isGenitive || cas0.isInstrumental || cas0.isDative) {
            if ((this.to.source.source instanceof NumbersWithUnitToken) && cas0.isGenitive) 
                this.coef = SemanticService.PARAMS.transitiveCoef;
            else {
                this.coef = SemanticService.PARAMS.ngLink;
                if (cas0.isNominative || this.from.source.typ === SentItemType.PARTBEFORE) 
                    this.coef /= (2);
                if (!cas0.isGenitive) 
                    this.coef /= (2);
            }
        }
        else if (this.from.source.source instanceof VerbPhraseToken) 
            this.coef = 0.1;
        if ((this.to.source.source instanceof NumbersWithUnitToken) && this.to.source.endToken.isValue("ЧЕМ", null)) 
            this.coef = SemanticService.PARAMS.transitiveCoef * (2);
    }
    
    _calcBe() {
        if (this.to.source.typ !== SentItemType.NOUN || this.from.source.typ !== SentItemType.NOUN) 
            return;
        let fm = this.from.source.source.morph;
        let tm = this.to.source.source.morph;
        if (!(tm._case.isNominative)) 
            return;
        if (!Utils.isNullOrEmpty(this.fromPrep)) 
            return;
        if (this.from.source.source instanceof NumbersWithUnitToken) {
            this.coef = SemanticService.PARAMS.transitiveCoef;
            return;
        }
        if (!fm._case.isUndefined) {
            if (!fm._case.isNominative) 
                return;
        }
        this.coef = 0;
    }
    
    _calcName(noplural) {
        if (!Utils.isNullOrEmpty(this.fromPrep)) 
            return;
        if (!(this.from.source.source instanceof NounPhraseToken) || this.from.source.typ !== SentItemType.NOUN) 
            return;
        if (this.from.source.beginToken.chars.isAllLower) 
            return;
        if (!(this.to.source.source instanceof NounPhraseToken) || this.to.source.typ !== SentItemType.NOUN) 
            return;
        if (this.from.order !== (this.to.order + 1) && !noplural) 
            return;
        let fm = this.from.source.source.morph;
        let tm = this.to.source.source.morph;
        if (!fm._case.isUndefined && !tm._case.isUndefined) {
            if ((MorphCase.ooBitand(tm._case, fm._case)).isUndefined) 
                return;
        }
        if (fm.number === MorphNumber.PLURAL) {
            if (noplural) {
                if (this.fromIsPlural) {
                }
                else if (((tm.number.value()) & (MorphNumber.SINGULAR.value())) !== (MorphNumber.UNDEFINED.value())) 
                    return;
            }
            this.plural = 1;
            this.coef = SemanticService.PARAMS.verbPlural;
        }
        else {
            if (fm.number === MorphNumber.SINGULAR) 
                this.plural = 0;
            if (NGLink._checkMorphAccord(fm, false, tm)) 
                this.coef = SemanticService.PARAMS.morphAccord;
        }
    }
    
    _calcAdverb() {
        if (this.toVerb !== null) 
            this.coef = 1;
        else if (this.to === null) 
            return;
        else if (this.to.source.typ === SentItemType.ADVERB) 
            this.coef = 1;
        else 
            this.coef = 0.5;
    }
    
    _calcList() {
        let cas0 = this.fromMorph._case;
        if (this.to === null) {
            if (this.toVerb === null) 
                return;
            return;
        }
        if (this.from.source.typ !== this.to.source.typ) {
            if (this.from.source.prep === this.to.source.prep && ((this.from.source.typ === SentItemType.NOUN || this.from.source.typ === SentItemType.PARTBEFORE || this.from.source.typ === SentItemType.PARTAFTER)) && ((this.to.source.typ === SentItemType.NOUN || this.to.source.typ === SentItemType.PARTBEFORE || this.to.source.typ === SentItemType.PARTAFTER))) {
            }
            else 
                return;
        }
        let cas1 = this.toMorph._case;
        if (!(MorphCase.ooBitand(cas0, cas1)).isUndefined) {
            this.coef = SemanticService.PARAMS.list;
            if (Utils.isNullOrEmpty(this.fromPrep) && !Utils.isNullOrEmpty(this.to.source.prep)) 
                this.coef /= (2);
            else if (!Utils.isNullOrEmpty(this.fromPrep) && Utils.isNullOrEmpty(this.to.source.prep)) 
                this.coef /= (4);
        }
        else {
            if (!cas0.isUndefined && !cas1.isUndefined) 
                return;
            if (!Utils.isNullOrEmpty(this.fromPrep) && Utils.isNullOrEmpty(this.to.source.prep)) 
                return;
            this.coef = SemanticService.PARAMS.list;
        }
        let t1 = Utils.as(this.from.source.endToken, TextToken);
        let t2 = Utils.as(this.to.source.endToken, TextToken);
        if (t1 !== null && t2 !== null) {
            if (t1.isValue(t2.getNormalCaseText(null, MorphNumber.SINGULAR, MorphGender.UNDEFINED, false), null)) 
                this.coef *= (10);
        }
        if (this.from.source.typ !== this.to.source.typ) 
            this.coef /= (2);
    }
    
    _calcParticiple(noplural) {
        let fm = this.from.source.source.morph;
        let tm = this.to.source.source.morph;
        if (this.to.source.typ === SentItemType.PARTBEFORE) 
            return this.coef = -1;
        if (this.from.source.typ === SentItemType.DEEPART) {
            if (!Utils.isNullOrEmpty(this.to.source.prep)) 
                return this.coef = -1;
            if (tm._case.isNominative) 
                return this.coef = SemanticService.PARAMS.morphAccord;
            if (tm._case.isUndefined) 
                return this.coef = 0;
            return this.coef = -1;
        }
        if (this.from.source.typ !== SentItemType.PARTBEFORE && this.from.source.typ !== SentItemType.SUBSENT) 
            return this.coef = -1;
        if (!fm._case.isUndefined && !tm._case.isUndefined) {
            if ((MorphCase.ooBitand(fm._case, tm._case)).isUndefined) {
                if (this.from.source.typ === SentItemType.PARTBEFORE) 
                    return this.coef = -1;
            }
        }
        if (fm.number === MorphNumber.PLURAL) {
            if (noplural) {
                if (this.fromIsPlural) {
                }
                else if (((tm.number.value()) & (MorphNumber.PLURAL.value())) === (MorphNumber.UNDEFINED.value())) 
                    return this.coef = -1;
            }
            this.plural = 1;
            this.coef = SemanticService.PARAMS.verbPlural;
        }
        else {
            if (fm.number === MorphNumber.SINGULAR) 
                this.plural = 0;
            if (fm.items.length > 0) {
                for (const wf of fm.items) {
                    if (NGLink._checkMorphAccord(tm, false, wf)) {
                        this.coef = SemanticService.PARAMS.morphAccord;
                        if (tm.gender !== MorphGender.UNDEFINED && wf.gender !== MorphGender.UNDEFINED) {
                            if (((tm.gender.value()) & (wf.gender.value())) === (MorphGender.UNDEFINED.value())) 
                                this.coef /= (2);
                        }
                        break;
                    }
                }
            }
        }
        return this.coef;
    }
    
    _calcAgent(noplural) {
        if (!Utils.isNullOrEmpty(this.fromPrep)) 
            return this.coef = -1;
        let vf = this.toVerb.firstVerb.verbMorph;
        if (vf === null) 
            return this.coef = -1;
        let vf2 = this.toVerb.lastVerb.verbMorph;
        if (vf2 === null) 
            return this.coef = -1;
        if (vf.misc.mood === MorphMood.IMPERATIVE) 
            return this.coef = -1;
        let links = SemanticHelper.tryCreateLinks(this.toVerb, this.from.source.source, null);
        if (links !== null) {
            for (const li of links) {
                if (li.role === SemanticRole.AGENT) {
                    this.coef = li.rank;
                    if (this.coef < 0.5) 
                        this.coef = 0.5;
                    return this.coef;
                }
            }
        }
        let _morph = this.fromMorph;
        if (vf2.misc.voice === MorphVoice.PASSIVE || this.toVerb.lastVerb.morph.containsAttr("страд.з.", null)) {
            if (!_morph._case.isUndefined) {
                if (_morph._case.isInstrumental) {
                    this.coef = SemanticService.PARAMS.transitiveCoef;
                    if (vf2._case.isInstrumental) 
                        this.coef /= (2);
                    return this.coef;
                }
                return this.coef = -1;
            }
            return this.coef = 0;
        }
        if (vf.misc.attrs.includes("инф.")) 
            return this.coef = -1;
        if (NGLink._isRevVerb(vf2)) 
            return this.coef = -1;
        if (vf.number === MorphNumber.PLURAL) {
            if (!_morph._case.isUndefined) {
                if (vf._case.isUndefined) {
                    if (!_morph._case.isNominative) 
                        return this.coef = -1;
                }
                else if ((MorphCase.ooBitand(vf._case, _morph._case)).isUndefined) 
                    return this.coef = -1;
            }
            if (noplural) {
                if (this.fromIsPlural) {
                }
                else if (((_morph.number.value()) & (MorphNumber.PLURAL.value())) === (MorphNumber.UNDEFINED.value())) 
                    return this.coef = -1;
                else if (!NGLink._checkMorphAccord(_morph, false, vf)) 
                    return this.coef = -1;
                else if (_morph.items.length > 0 && !vf._case.isUndefined) {
                    let ok = false;
                    for (const it of _morph.items) {
                        if (((it.number.value()) & (MorphNumber.PLURAL.value())) === (MorphNumber.PLURAL.value())) {
                            if (!it._case.isUndefined && (MorphCase.ooBitand(it._case, vf._case)).isUndefined) 
                                continue;
                            ok = true;
                            break;
                        }
                    }
                    if (!ok) 
                        return this.coef = -1;
                }
            }
            this.plural = 1;
            this.coef = SemanticService.PARAMS.verbPlural;
            if (vf2.normalCase === "БЫТЬ") {
                if (_morph._case.isUndefined && this.from.source.beginToken.beginChar > this.toVerb.endChar) 
                    this.coef /= (2);
            }
        }
        else {
            if (vf.number === MorphNumber.SINGULAR) {
                this.plural = 0;
                if (this.fromIsPlural) 
                    return this.coef = -1;
            }
            if (!NGLink._checkMorphAccord(_morph, false, vf)) 
                return this.coef = -1;
            if (!_morph._case.isUndefined) {
                if (!_morph._case.isNominative) {
                    if (this.toVerb.firstVerb.isParticiple) {
                    }
                    else 
                        return this.coef = -1;
                }
            }
            if (vf.misc.person !== MorphPerson.UNDEFINED) {
                if (((vf.misc.person.value()) & (MorphPerson.THIRD.value())) === (MorphPerson.UNDEFINED.value())) {
                    if (((vf.misc.person.value()) & (MorphPerson.FIRST.value())) === (MorphPerson.FIRST.value())) {
                        if (!_morph.containsAttr("1 л.", null)) 
                            return this.coef = -1;
                    }
                    if (((vf.misc.person.value()) & (MorphPerson.SECOND.value())) === (MorphPerson.SECOND.value())) {
                        if (!_morph.containsAttr("2 л.", null)) 
                            return this.coef = -1;
                    }
                }
            }
            this.coef = SemanticService.PARAMS.morphAccord;
            if (_morph._case.isUndefined) 
                this.coef /= (4);
        }
        return this.coef;
    }
    
    static _isRevVerb(vf) {
        if (vf.misc.attrs.includes("возвр.")) 
            return true;
        if (vf.normalCase !== null) {
            if (vf.normalCase.endsWith("СЯ") || vf.normalCase.endsWith("СЬ")) 
                return true;
        }
        return false;
    }
    
    _calcPacient(noplural) {
        if (!Utils.isNullOrEmpty(this.fromPrep)) 
            return this.coef = -1;
        let vf = this.toVerb.firstVerb.verbMorph;
        if (vf === null) 
            return -1;
        let vf2 = this.toVerb.lastVerb.verbMorph;
        if (vf2 === null) 
            return -1;
        let links = SemanticHelper.tryCreateLinks(this.toVerb, this.from.source.source, null);
        if (links !== null) {
            for (const li of links) {
                if (li.role === SemanticRole.PACIENT) {
                    this.coef = li.rank;
                    if (this.coef < 0.5) 
                        this.coef = 0.5;
                    return this.coef;
                }
            }
        }
        let _morph = this.fromMorph;
        if (vf2.misc.voice === MorphVoice.PASSIVE || this.toVerb.lastVerb.morph.containsAttr("страд.з.", null)) {
            if (vf.number === MorphNumber.PLURAL) {
                if (noplural) {
                    if (this.fromIsPlural) {
                    }
                    else if (!NGLink._checkMorphAccord(_morph, false, vf)) 
                        return -1;
                    else if (_morph.items.length > 0 && !vf._case.isUndefined) {
                        let ok = false;
                        for (const it of _morph.items) {
                            if (((it.number.value()) & (MorphNumber.PLURAL.value())) === (MorphNumber.PLURAL.value())) {
                                if (!it._case.isUndefined && (MorphCase.ooBitand(it._case, vf._case)).isUndefined) 
                                    continue;
                                ok = true;
                                break;
                            }
                        }
                        if (!ok) 
                            return this.coef = -1;
                    }
                }
                this.coef = SemanticService.PARAMS.verbPlural;
                this.plural = 1;
            }
            else {
                if (vf.number === MorphNumber.SINGULAR) {
                    this.plural = 0;
                    if (this.fromIsPlural) 
                        return -1;
                }
                if (!NGLink._checkMorphAccord(_morph, false, vf)) 
                    return -1;
                this.coef = SemanticService.PARAMS.morphAccord;
            }
            return this.coef;
        }
        return this.coef = -1;
    }
    
    _calcActant() {
        if (this.canBeParticiple) 
            return this.coef = -1;
        let vf2 = this.toVerb.lastVerb.verbMorph;
        if (vf2 === null) 
            return -1;
        if (this.fromPrep === null) 
            return this.coef = 0;
        let links = SemanticHelper.tryCreateLinks(this.toVerb, this.from.source.source, null);
        if (links !== null) {
            for (const li of links) {
                if (li.role !== SemanticRole.AGENT && li.role !== SemanticRole.PACIENT) {
                    this.coef = li.rank;
                    if (this.coef < 0.5) 
                        this.coef = 0.5;
                    return this.coef;
                }
            }
        }
        return this.coef = 0.1;
    }
    
    static _checkMorphAccord(m, _plural, vf) {
        let _coef = 0;
        if (vf.number === MorphNumber.PLURAL) {
            if (_plural) 
                _coef++;
            else if (m.number !== MorphNumber.UNDEFINED) {
                if (((m.number.value()) & (MorphNumber.PLURAL.value())) === (MorphNumber.PLURAL.value())) 
                    _coef++;
                else 
                    return false;
            }
        }
        else if (vf.number === MorphNumber.SINGULAR) {
            if (_plural) 
                return false;
            if (m.number !== MorphNumber.UNDEFINED) {
                if (((m.number.value()) & (MorphNumber.SINGULAR.value())) === (MorphNumber.SINGULAR.value())) 
                    _coef++;
                else 
                    return false;
            }
            if (m.gender !== MorphGender.UNDEFINED) {
                if (vf.gender !== MorphGender.UNDEFINED) {
                    if (m.gender === MorphGender.FEMINIE) {
                        if (((vf.gender.value()) & (MorphGender.FEMINIE.value())) !== (MorphGender.UNDEFINED.value())) 
                            _coef++;
                        else 
                            return false;
                    }
                    else if (((m.gender.value()) & (vf.gender.value())) !== (MorphGender.UNDEFINED.value())) 
                        _coef++;
                    else if (m.gender === MorphGender.MASCULINE && vf.gender === MorphGender.FEMINIE) {
                    }
                    else 
                        return false;
                }
            }
        }
        return _coef >= 0;
    }
    
    static _new3815(_arg1, _arg2, _arg3, _arg4) {
        let res = new NGLink();
        res.typ = _arg1;
        res.from = _arg2;
        res.to = _arg3;
        res.reverce = _arg4;
        return res;
    }
    
    static _new3817(_arg1) {
        let res = new NGLink();
        res.typ = _arg1;
        return res;
    }
    
    static _new3818(_arg1, _arg2) {
        let res = new NGLink();
        res.from = _arg1;
        res.typ = _arg2;
        return res;
    }
    
    static _new3826(_arg1, _arg2, _arg3) {
        let res = new NGLink();
        res.from = _arg1;
        res.toVerb = _arg2;
        res.typ = _arg3;
        return res;
    }
    
    static _new3842(_arg1, _arg2, _arg3) {
        let res = new NGLink();
        res.typ = _arg1;
        res.from = _arg2;
        res.toVerb = _arg3;
        return res;
    }
}


module.exports = NGLink