/*
 * 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 MorphCase = require("./../../morph/MorphCase");
const NounPhraseHelper = require("./../../ner/core/NounPhraseHelper");
const SemAttributeType = require("./../SemAttributeType");
const MorphCollection = require("./../../ner/MorphCollection");
const PrepositionHelper = require("./../../ner/core/PrepositionHelper");
const ConjunctionType = require("./../../ner/core/ConjunctionType");
const NumberWithUnitParseAttr = require("./../../ner/measure/internal/NumberWithUnitParseAttr");
const NumberToken = require("./../../ner/NumberToken");
const NGItem = require("./NGItem");
const MorphWordForm = require("./../../morph/MorphWordForm");
const NounPhraseParseAttr = require("./../../ner/core/NounPhraseParseAttr");
const TextToken = require("./../../ner/TextToken");
const NGLink = require("./NGLink");
const MorphMood = require("./../../morph/MorphMood");
const SemQuantity = require("./../SemQuantity");
const VerbPhraseHelper = require("./../../ner/core/VerbPhraseHelper");
const SemAttributeEx = require("./SemAttributeEx");
const MorphGender = require("./../../morph/MorphGender");
const MetaToken = require("./../../ner/MetaToken");
const MorphNumber = require("./../../morph/MorphNumber");
const DerivateService = require("./../utils/DerivateService");
const ReferentToken = require("./../../ner/ReferentToken");
const MorphClass = require("./../../morph/MorphClass");
const NumbersWithUnitToken = require("./../../ner/measure/internal/NumbersWithUnitToken");
const SentItemType = require("./SentItemType");
const NounPhraseToken = require("./../../ner/core/NounPhraseToken");
const NGLinkType = require("./NGLinkType");
const SentItemSubtype = require("./SentItemSubtype");
const ConjunctionHelper = require("./../../ner/core/ConjunctionHelper");
const SemAttribute = require("./../SemAttribute");
const AdverbToken = require("./AdverbToken");
const ConjunctionToken = require("./../../ner/core/ConjunctionToken");
const VerbPhraseToken = require("./../../ner/core/VerbPhraseToken");
const DelimToken = require("./DelimToken");
const Sentence = require("./Sentence");

class SentItem {
    
    constructor(mt) {
        this.source = null;
        this.prep = null;
        this.typ = SentItemType.UNDEFINED;
        this.subTyp = SentItemSubtype.UNDEFINED;
        this.subSent = null;
        this.plural = -1;
        this.drGroups = null;
        this.drGroups2 = null;
        this.partVerbTyp = NGLinkType.UNDEFINED;
        this.participleCoef = 1;
        this.quant = null;
        this.attrs = null;
        this.canBeQuestion = false;
        this.result = null;
        this.resultVerbLast = null;
        this.m_ResGraph = null;
        this.resFrag = null;
        this.resultList = null;
        this.resultListOr = false;
        this.m_BeginToken = null;
        this.m_EndToken = null;
        this.source = mt;
        if (mt instanceof NounPhraseToken) {
            let npt = Utils.as(mt, NounPhraseToken);
            if (npt.preposition !== null) 
                this.prep = npt.preposition.normal;
            else 
                this.prep = "";
            this.typ = SentItemType.NOUN;
            let normal = npt.noun.getNormalCaseText(MorphClass.NOUN, MorphNumber.SINGULAR, MorphGender.MASCULINE, false);
            if (normal !== null) 
                this.drGroups = DerivateService.findDerivates(normal, true, null);
        }
        else if ((mt instanceof ReferentToken) || (mt instanceof NumbersWithUnitToken)) 
            this.typ = SentItemType.NOUN;
        else if (mt instanceof AdverbToken) 
            this.typ = SentItemType.ADVERB;
        else if (mt instanceof ConjunctionToken) 
            this.typ = SentItemType.CONJ;
        else if (mt instanceof DelimToken) 
            this.typ = SentItemType.DELIM;
        else if (mt instanceof VerbPhraseToken) {
            let vpt = Utils.as(mt, VerbPhraseToken);
            let normal = (vpt.firstVerb.verbMorph === null ? null : (vpt.firstVerb.verbMorph.normalFull != null ? vpt.firstVerb.verbMorph.normalFull : vpt.firstVerb.verbMorph.normalCase));
            if (normal !== null) 
                this.drGroups = DerivateService.findDerivates(normal, true, null);
            if (vpt.firstVerb !== vpt.lastVerb) {
                normal = (vpt.lastVerb.verbMorph === null ? vpt.getNormalCaseText(null, MorphNumber.UNDEFINED, MorphGender.UNDEFINED, false) : (vpt.lastVerb.verbMorph.normalFull != null ? vpt.lastVerb.verbMorph.normalFull : vpt.lastVerb.verbMorph.normalCase));
                this.drGroups2 = DerivateService.findDerivates(normal, true, null);
            }
            else 
                this.drGroups2 = this.drGroups;
            this.prep = (vpt.preposition === null ? "" : vpt.preposition.normal);
            this.typ = SentItemType.VERB;
        }
    }
    
    copyFrom(it) {
        this.source = it.source;
        this.typ = it.typ;
        this.subTyp = it.subTyp;
        this.prep = it.prep;
        this.participleCoef = it.participleCoef;
        this.drGroups = it.drGroups;
        this.drGroups2 = it.drGroups2;
        this.partVerbTyp = it.partVerbTyp;
        this.m_BeginToken = it.m_BeginToken;
        this.m_EndToken = it.m_EndToken;
        this.plural = it.plural;
        this.subSent = it.subSent;
        this.quant = it.quant;
        this.attrs = it.attrs;
        this.canBeQuestion = it.canBeQuestion;
        this.result = it.result;
        this.resultList = it.resultList;
        this.resultListOr = it.resultListOr;
        this.resultVerbLast = it.resultVerbLast;
        this.resGraph = it.resGraph;
        this.resFrag = it.resFrag;
    }
    
    get resGraph() {
        return this.m_ResGraph;
    }
    set resGraph(value) {
        if (this.m_ResGraph === null) 
            this.m_ResGraph = value;
        else if (value !== null && this.m_ResGraph !== value) 
            this.m_ResGraph = value;
        return value;
    }
    
    addAttr(adv) {
        let sa = SemAttribute._new3831(adv.spelling, adv.typ, adv.not);
        if (this.attrs === null) 
            this.attrs = new Array();
        this.attrs.push(SemAttributeEx._new3832(adv, sa));
    }
    
    get beginToken() {
        if (this.m_BeginToken !== null) 
            return this.m_BeginToken;
        if (this.source !== null) 
            return this.source.beginToken;
        return null;
    }
    set beginToken(value) {
        this.m_BeginToken = value;
        return value;
    }
    
    get endToken() {
        if (this.m_EndToken !== null) 
            return this.m_EndToken;
        if (this.source !== null) {
            let ret = this.source.endToken;
            if (this.attrs !== null) {
                for (const a of this.attrs) {
                    if (a.token.endChar > ret.endChar) 
                        ret = a.token.endToken;
                }
            }
            return ret;
        }
        return null;
    }
    set endToken(value) {
        this.m_EndToken = value;
        return value;
    }
    
    get canBeNoun() {
        if (((this.typ === SentItemType.NOUN || this.typ === SentItemType.DEEPART || this.typ === SentItemType.PARTAFTER) || this.typ === SentItemType.PARTBEFORE || this.typ === SentItemType.SUBSENT) || this.typ === SentItemType.FORMULA) 
            return true;
        if (this.source instanceof VerbPhraseToken) {
            if (this.source.firstVerb.verbMorph !== null && this.source.firstVerb.verbMorph.containsAttr("инф.", null)) 
                return true;
        }
        return false;
    }
    
    get canBeCommaEnd() {
        let cnj = Utils.as(this.source, ConjunctionToken);
        if (cnj === null) 
            return false;
        return cnj.typ === ConjunctionType.COMMA || cnj.typ === ConjunctionType.AND || cnj.typ === ConjunctionType.OR;
    }
    
    toString() {
        let res = new StringBuilder();
        if (!Utils.isNullOrEmpty(this.prep)) 
            res.append(this.prep).append(" ");
        res.append(this.typ.toString()).append("(");
        if (this.subTyp !== SentItemSubtype.UNDEFINED) 
            res.append(String(this.subTyp)).append(":");
        if (this.source !== null) {
            res.append(this.source.toString());
            if (this.subSent !== null) 
                res.append(" <= ").append(this.subSent.toString());
        }
        else if (this.subSent !== null) 
            res.append(this.subSent.toString());
        res.append(')');
        return res.toString();
    }
    
    static parseNearItems(t, t1, lev, prev) {
        if (lev > 100) 
            return null;
        if (t === null || t.beginChar > t1.endChar) 
            return null;
        let res = new Array();
        let delim = DelimToken.tryParse(t);
        if (delim !== null) {
            res.push(new SentItem(delim));
            return res;
        }
        let conj = ConjunctionHelper.tryParse(t);
        if (conj !== null) {
            res.push(new SentItem(conj));
            return res;
        }
        let _prep = PrepositionHelper.tryParse(t);
        let t111 = (_prep === null ? t : _prep.endToken.next);
        if ((t111 instanceof NumberToken) && ((t111.morph._class.isAdjective && !t111.morph._class.isNoun))) 
            t111 = null;
        let num = (t111 === null ? null : NumbersWithUnitToken.tryParse(t111, null, NumberWithUnitParseAttr.NO));
        if (num !== null) {
            if (num.units.length === 0) {
                let npt1 = NounPhraseHelper.tryParse(num.endToken.next, SentItem.m_NptAttrs, 0, null);
                if (npt1 === null && num.endToken.next !== null && num.endToken.next.isValue("РАЗ", null)) {
                    npt1 = new NounPhraseToken(num.endToken.next, num.endToken.next);
                    npt1.noun = new MetaToken(num.endToken.next, num.endToken.next);
                }
                if (npt1 !== null && _prep !== null) {
                    if (npt1.noun.endToken.isValue("РАЗ", null)) 
                        npt1.morph.removeItems(_prep.nextCase, false);
                    else if ((MorphCase.ooBitand(npt1.morph._case, _prep.nextCase)).isUndefined) 
                        npt1 = null;
                    else 
                        npt1.morph.removeItems(_prep.nextCase, false);
                }
                if ((npt1 !== null && npt1.endToken.isValue("ОНИ", null) && npt1.preposition !== null) && npt1.preposition.normal === "ИЗ") {
                    npt1.morph = new MorphCollection(num.endToken.morph);
                    npt1.preposition = null;
                    let nn = num.toString();
                    let si1 = new SentItem(npt1);
                    if (nn === "1" && (num.endToken instanceof NumberToken) && num.endToken.endToken.isValue("ОДИН", null)) {
                        let a = SemAttribute._new3833(SemAttributeType.ONEOF, num.endToken.endToken.getNormalCaseText(null, MorphNumber.SINGULAR, MorphGender.UNDEFINED, false));
                        let aex = SemAttributeEx._new3832(num, a);
                        si1.attrs = new Array();
                        si1.attrs.push(aex);
                    }
                    else 
                        si1.quant = new SemQuantity(nn, num.beginToken, num.endToken);
                    if (_prep !== null) 
                        si1.prep = _prep.normal;
                    res.push(si1);
                    return res;
                }
                if (npt1 !== null) {
                    let si1 = SentItem._new3835(npt1, new SemQuantity(num.toString(), num.beginToken, num.endToken));
                    if (_prep !== null) 
                        si1.prep = _prep.normal;
                    if (npt1.endToken.isValue("РАЗ", null)) 
                        si1.typ = SentItemType.FORMULA;
                    if (((npt1.morph.number.value()) & (MorphNumber.PLURAL.value())) === (MorphNumber.UNDEFINED.value()) && si1.quant.spelling !== "1") {
                        let ok = false;
                        if (si1.quant.spelling.endsWith("1")) 
                            ok = true;
                        else if (si1.typ === SentItemType.FORMULA) 
                            ok = true;
                        else if (si1.quant.spelling.endsWith("2") && npt1.morph._case.isGenitive) 
                            ok = true;
                        else if (si1.quant.spelling.endsWith("3") && npt1.morph._case.isGenitive) 
                            ok = true;
                        else if (si1.quant.spelling.endsWith("4") && npt1.morph._case.isGenitive) 
                            ok = true;
                        if (ok) {
                            npt1.morph = new MorphCollection();
                            npt1.morph.number = MorphNumber.PLURAL;
                        }
                    }
                    res.push(si1);
                    return res;
                }
            }
            num.beginToken = t;
            num.morph = new MorphCollection(num.endToken.morph);
            let si = new SentItem(num);
            if (_prep !== null) 
                si.prep = _prep.normal;
            res.push(si);
            if (si.prep === "НА") {
                let aa = AdverbToken.tryParse(si.endToken.next);
                if (aa !== null && ((aa.typ === SemAttributeType.LESS || aa.typ === SemAttributeType.GREAT))) {
                    si.addAttr(aa);
                    si.endToken = aa.endToken;
                }
            }
            return res;
        }
        let mc = t.getMorphClassInDictionary();
        let adv = AdverbToken.tryParse(t);
        let npt = NounPhraseHelper.tryParse(t, SentItem.m_NptAttrs, 0, null);
        if (npt !== null && (npt.endToken instanceof TextToken) && npt.endToken.term === "БЫЛИ") 
            npt = null;
        if (npt !== null && adv !== null) {
            if (adv.endChar > npt.endChar) 
                npt = null;
            else if (adv.endChar === npt.endChar) {
                res.push(new SentItem(npt));
                res.push(new SentItem(adv));
                return res;
            }
        }
        if (npt !== null && npt.adjectives.length === 0) {
            if (npt.endToken.isValue("КОТОРЫЙ", null) && t.previous !== null && t.previous.isCommaAnd) {
                let res1 = SentItem.parseSubsent(npt, t1, lev + 1, prev);
                if (res1 !== null) 
                    return res1;
            }
            if (npt.endToken.isValue("СКОЛЬКО", null)) {
                let tt1 = npt.endToken.next;
                if (tt1 !== null && tt1.isValue("ВСЕГО", null)) 
                    tt1 = tt1.next;
                let npt1 = NounPhraseHelper.tryParse(tt1, NounPhraseParseAttr.NO, 0, null);
                if (npt1 !== null && !npt1.morph._case.isUndefined && _prep !== null) {
                    if ((MorphCase.ooBitand(_prep.nextCase, npt1.morph._case)).isUndefined) 
                        npt1 = null;
                    else 
                        npt1.morph.removeItems(_prep.nextCase, false);
                }
                if (npt1 !== null) {
                    npt1.beginToken = npt.beginToken;
                    npt1.preposition = npt.preposition;
                    npt1.adjectives.push(new MetaToken(npt.endToken, npt.endToken));
                    npt = npt1;
                }
            }
            if (npt.endToken.morph._class.isAdjective) {
                if (VerbPhraseHelper.tryParse(t, true, false, false) !== null) 
                    npt = null;
            }
        }
        let vrb = null;
        if (npt !== null && npt.adjectives.length > 0) {
            vrb = VerbPhraseHelper.tryParse(t, true, false, false);
            if (vrb !== null && vrb.firstVerb.isParticiple) 
                npt = null;
        }
        else if (adv === null || npt !== null) 
            vrb = VerbPhraseHelper.tryParse(t, true, false, false);
        if (npt !== null) 
            res.push(new SentItem(npt));
        if (vrb !== null && !vrb.firstVerb.isParticiple && !vrb.firstVerb.isDeeParticiple) {
            let vars = new Array();
            for (const wf of vrb.firstVerb.morph.items) {
                if (wf._class.isVerb && (wf instanceof MorphWordForm) && wf.isInDictionary) 
                    vars.push(Utils.as(wf, MorphWordForm));
            }
            if (vars.length < 2) 
                res.push(new SentItem(vrb));
            else {
                vrb.firstVerb.verbMorph = vars[0];
                res.push(new SentItem(vrb));
                for (let i = 1; i < vars.length; i++) {
                    vrb = VerbPhraseHelper.tryParse(t, false, false, false);
                    if (vrb === null) 
                        break;
                    vrb.firstVerb.verbMorph = vars[i];
                    res.push(new SentItem(vrb));
                }
                if (vars[0].misc.mood === MorphMood.IMPERATIVE && vars[1].misc.mood !== MorphMood.IMPERATIVE) {
                    let rr = res[0];
                    res[0] = res[1];
                    res[1] = rr;
                }
            }
            return res;
        }
        if (vrb !== null) {
            let res1 = SentItem.parseParticiples(vrb, t1, lev + 1);
            if (res1 !== null) 
                res.splice(res.length, 0, ...res1);
        }
        if (res.length > 0) 
            return res;
        if (adv !== null) {
            if (adv.typ === SemAttributeType.OTHER) {
                let npt1 = NounPhraseHelper.tryParse(adv.endToken.next, SentItem.m_NptAttrs, 0, null);
                if (npt1 !== null && npt1.endToken.isValue("ОНИ", null) && npt1.preposition !== null) {
                    let si1 = new SentItem(npt1);
                    let a = SemAttribute._new3833(SemAttributeType.OTHER, adv.endToken.getNormalCaseText(null, MorphNumber.UNDEFINED, MorphGender.UNDEFINED, false));
                    let aex = SemAttributeEx._new3832(num, a);
                    si1.attrs = new Array();
                    si1.attrs.push(aex);
                    if (_prep !== null) 
                        si1.prep = _prep.normal;
                    res.push(si1);
                    return res;
                }
                for (let i = prev.length - 1; i >= 0; i--) {
                    if (prev[i].attrs !== null) {
                        for (const a of prev[i].attrs) {
                            if (a.attr.typ === SemAttributeType.ONEOF) {
                                let si1 = new SentItem(prev[i].source);
                                let aa = SemAttribute._new3833(SemAttributeType.OTHER, adv.endToken.getNormalCaseText(null, MorphNumber.UNDEFINED, MorphGender.UNDEFINED, false));
                                let aex = SemAttributeEx._new3832(adv, aa);
                                si1.attrs = new Array();
                                si1.attrs.push(aex);
                                if (_prep !== null) 
                                    si1.prep = _prep.normal;
                                si1.beginToken = adv.beginToken;
                                si1.endToken = adv.endToken;
                                res.push(si1);
                                return res;
                            }
                        }
                    }
                }
            }
            res.push(new SentItem(adv));
            return res;
        }
        if (mc.isAdjective) {
            npt = NounPhraseToken._new3840(t, t, new MorphCollection(t.morph));
            npt.noun = new MetaToken(t, t);
            res.push(new SentItem(npt));
            return res;
        }
        return null;
    }
    
    static parseSubsent(npt, t1, lev, prev) {
        let ok = false;
        if (prev !== null) {
            for (let i = prev.length - 1; i >= 0; i--) {
                let it = prev[i];
                if (it.typ === SentItemType.CONJ || it.typ === SentItemType.DELIM) {
                    ok = true;
                    break;
                }
                if (it.typ === SentItemType.VERB) 
                    break;
            }
        }
        if (!ok) 
            return null;
        let sents = Utils.notNull(Sentence.parseVariants(npt.endToken.next, t1, lev + 1, 20, SentItemType.SUBSENT), new Array());
        let endpos = new Array();
        let res = new Array();
        for (const s of sents) {
            s.items.splice(0, 0, new SentItem(npt));
            s.calcCoef(true);
            s.truncOborot(false);
            let end = s.items[s.items.length - 1].endToken.endChar;
            if (endpos.includes(end)) 
                continue;
            endpos.push(end);
            s.calcCoef(false);
            let part = new SentItem(npt);
            part.typ = SentItemType.SUBSENT;
            part.subTyp = SentItemSubtype.WICH;
            part.subSent = s;
            part.result = s.items[0].result;
            part.endToken = s.items[s.items.length - 1].endToken;
            res.push(part);
        }
        return res;
    }
    
    static parseParticiples(vb, t1, lev) {
        if (lev > 4) 
            return null;
        let sents = Utils.notNull(Sentence.parseVariants(vb.endToken.next, t1, lev + 1, 20, SentItemType.PARTBEFORE), new Array());
        let _typ = NGLinkType.AGENT;
        if (vb.firstVerb.morph.containsAttr("страд.з.", null)) 
            _typ = NGLinkType.PACIENT;
        else if (vb.firstVerb.morph.containsAttr("возвр.", null)) 
            _typ = NGLinkType.PACIENT;
        let endpos = new Array();
        let res = new Array();
        let changed = false;
        for (const s of sents) {
            if (vb.firstVerb.isDeeParticiple) 
                break;
            for (let i = 0; i < s.items.length; i++) {
                let it = s.items[i];
                if (!it.canBeNoun || it.typ === SentItemType.VERB) 
                    continue;
                if (!Utils.isNullOrEmpty(it.prep)) 
                    continue;
                if (it.typ === SentItemType.PARTBEFORE || it.typ === SentItemType.PARTAFTER) 
                    continue;
                let li = NGLink._new3842(_typ, NGItem._new3813(it), vb);
                li.calcCoef(true);
                if (li.coef < 0) 
                    continue;
                if (endpos.includes(it.endToken.endChar)) 
                    continue;
                let ss = Sentence._new3843(_typ);
                ss.items.push(new SentItem(vb));
                for (let j = 0; j <= i; j++) {
                    let si = new SentItem(null);
                    si.copyFrom(s.items[j]);
                    ss.items.push(si);
                }
                ss.calcCoef(false);
                changed = true;
                if (ss.coef < 0) 
                    continue;
                let part = new SentItem(it.source);
                part.typ = SentItemType.PARTAFTER;
                part.subSent = ss;
                if (vb.preposition !== null) 
                    part.prep = vb.preposition.normal;
                part.beginToken = vb.beginToken;
                part.endToken = it.source.endToken;
                if ((i + 1) < ss.items.length) 
                    part.result = ss.items[i + 1].result;
                endpos.push(it.endToken.endChar);
                res.push(part);
            }
        }
        endpos.splice(0, endpos.length);
        if (changed) 
            sents = Utils.notNull(Sentence.parseVariants(vb.endToken.next, t1, lev + 1, 20, SentItemType.PARTBEFORE), new Array());
        for (const s of sents) {
            s.items.splice(0, 0, new SentItem(vb));
            s.calcCoef(true);
            s.truncOborot(true);
            let end = s.items[s.items.length - 1].endToken.endChar;
            endpos.push(end);
            s.notLastNounToFirstVerb = _typ;
            s.calcCoef(false);
            let part = new SentItem(vb);
            part.partVerbTyp = _typ;
            part.typ = (vb.firstVerb.isDeeParticiple ? SentItemType.DEEPART : SentItemType.PARTBEFORE);
            part.subSent = s;
            part.result = s.items[0].result;
            part.resultVerbLast = s.items[0].resultVerbLast;
            part.endToken = s.items[s.items.length - 1].endToken;
            res.push(part);
        }
        if (res.length === 0 && sents.length === 0) {
            let part = new SentItem(vb);
            part.partVerbTyp = _typ;
            part.typ = (vb.firstVerb.isDeeParticiple ? SentItemType.DEEPART : SentItemType.PARTBEFORE);
            res.push(part);
        }
        return res;
    }
    
    static _new3835(_arg1, _arg2) {
        let res = new SentItem(_arg1);
        res.quant = _arg2;
        return res;
    }
    
    static static_constructor() {
        SentItem.m_NptAttrs = NounPhraseParseAttr.of(((((NounPhraseParseAttr.ADJECTIVECANBELAST.value()) | (NounPhraseParseAttr.IGNOREBRACKETS.value()) | (NounPhraseParseAttr.PARSEADVERBS.value())) | (NounPhraseParseAttr.PARSENUMERICASADJECTIVE.value()) | (NounPhraseParseAttr.PARSEPREPOSITION.value())) | (NounPhraseParseAttr.PARSEPRONOUNS.value()) | (NounPhraseParseAttr.PARSEVERBS.value())) | (NounPhraseParseAttr.REFERENTCANBENOUN.value()) | (NounPhraseParseAttr.MULTINOUNS.value()));
    }
}


SentItem.static_constructor();

module.exports = SentItem