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

const StreetReferent = require("./../address/StreetReferent");
const ResumeItemReferent = require("./../resume/ResumeItemReferent");
const PersonPropertyReferent = require("./../person/PersonPropertyReferent");
const UriReferent = require("./../uri/UriReferent");
const PhoneReferent = require("./../phone/PhoneReferent");
const PersonIdentityReferent = require("./../person/PersonIdentityReferent");
const AddressReferent = require("./../address/AddressReferent");
const PersonAttrTokenPersonAttrAttachAttrs = require("./../person/internal/PersonAttrTokenPersonAttrAttachAttrs");
const ProcessorService = require("./../ProcessorService");
const PersonPropertyKind = require("./../person/PersonPropertyKind");
const LinkType = require("./LinkType");
const MorphCase = require("./../../morph/MorphCase");
const MiscHelper = require("./../core/MiscHelper");
const DateRangeReferent = require("./../date/DateRangeReferent");
const PersonAttrToken = require("./../person/internal/PersonAttrToken");
const ResumeAnalyzer = require("./../resume/ResumeAnalyzer");
const DateReferent = require("./../date/DateReferent");
const NounPhraseParseAttr = require("./../core/NounPhraseParseAttr");
const ComplexNumToken = require("./../core/ComplexNumToken");
const NounPhraseHelper = require("./../core/NounPhraseHelper");
const Referent = require("./../Referent");
const LinkReferent = require("./LinkReferent");
const ResourceHelper3 = require("./../core/internal/ResourceHelper3");
const Analyzer = require("./../Analyzer");
const GeoReferent = require("./../geo/GeoReferent");
const ResumeItemType = require("./../resume/ResumeItemType");
const TextToken = require("./../TextToken");
const ReferentToken = require("./../ReferentToken");
const MetaLink = require("./MetaLink");
const PersonHelper = require("./../person/internal/PersonHelper");
const PersonReferent = require("./../person/PersonReferent");
const OrganizationReferent = require("./../org/OrganizationReferent");

/**
 * Анализатор связей (специфический анализатор)
 */
class LinkAnalyzer extends Analyzer {
    
    get name() {
        return LinkAnalyzer.ANALYZER_NAME;
    }
    
    get caption() {
        return "Связи";
    }
    
    get description() {
        return "Связи выделенных сущностй (досье)";
    }
    
    clone() {
        return new LinkAnalyzer();
    }
    
    get isSpecific() {
        return true;
    }
    
    get typeSystem() {
        return [MetaLink.GLOBAL_META];
    }
    
    get images() {
        let res = new Hashtable();
        res.put(MetaLink.IMAGE_ID.toString(), ResourceHelper3.getBytes("link.png"));
        return res;
    }
    
    createReferent(type) {
        if (type === LinkReferent.OBJ_TYPENAME) 
            return new LinkReferent();
        return null;
    }
    
    get progressWeight() {
        return 1;
    }
    
    process(kit) {
        let ad = kit.getAnalyzerData(this);
        let t0 = kit.firstToken;
        let cou = 10;
        for (let t = t0; t !== null && cou > 0; t = t.next) {
            if (!t.isNewlineBefore) 
                continue;
            cou--;
            let num = ComplexNumToken.tryParse(t, null, false, false);
            if (num !== null) 
                break;
            let npt = NounPhraseHelper.tryParse(t, NounPhraseParseAttr.NO, 0, null);
            if (npt === null || !npt.isNewlineAfter) 
                continue;
            if (npt.endToken.isValue("ЗАПИСКА", null) || npt.endToken.isValue("СПРАВКА", null) || npt.endToken.isValue("ОТЧЕТ", null)) {
                t0 = npt.endToken.next;
                break;
            }
        }
        let mainPers = null;
        let curPers = null;
        let curOrg = null;
        let curTyp = ResumeItemType.UNDEFINED;
        let tmpOrgs = new Array();
        for (let t = t0; t !== null; t = t.next) {
            let nl = false;
            if (t.isNewlineBefore) {
                nl = true;
                let num = ComplexNumToken.tryParse(t, null, false, false);
                if (num !== null && num.endToken.next !== null) {
                    t = num.endToken.next;
                    nl = true;
                }
            }
            if (curOrg !== null && mainPers !== null && curOrg.occurrence.length > 0) {
                if ((t.beginChar - curOrg.occurrence[curOrg.occurrence.length - 1].endChar) > 100) 
                    curOrg = null;
            }
            let r = t.getReferent();
            let p = Utils.as(r, PersonReferent);
            if (p !== null) {
                curOrg = null;
                if (LinkAnalyzer._isObject(p)) {
                    curPers = p;
                    continue;
                }
                if (p === mainPers || p === curPers) 
                    continue;
                if (mainPers === null || nl) {
                    mainPers = p;
                    LinkAnalyzer._processPerson(p, ad);
                    continue;
                }
                if (mainPers !== null) {
                    let nam = p.getStringValue(PersonReferent.ATTR_FIRSTNAME);
                    if (nam === null || (nam.length < 2)) 
                        continue;
                    LinkAnalyzer._processPerson(p, ad);
                    let li = LinkReferent._new2298(mainPers, p);
                    li = Utils.as(ad.registerReferent(li), LinkReferent);
                    li.addOccurenceOfRefTok(new ReferentToken(li, t, t));
                    continue;
                }
                curPers = p;
                LinkAnalyzer._processPerson(p, ad);
                continue;
            }
            p = (curPers != null ? curPers : mainPers);
            if (p === null) {
                if (r instanceof OrganizationReferent) {
                    curOrg = Utils.as(r, OrganizationReferent);
                    LinkAnalyzer._setObject(r);
                    continue;
                }
                else if (curOrg === null) 
                    continue;
            }
            else if (((p.isFemale && !p.isMale)) || ((!p.isFemale && p.isMale))) {
            }
            else 
                PersonHelper.createSex(p, t);
            let _geo = Utils.as(r, GeoReferent);
            if (_geo !== null && _geo.isCity && p !== null) {
                for (let tt = t.previous; tt !== null; tt = tt.previous) {
                    let te = Utils.as(tt, TextToken);
                    if (te !== null) {
                        if (te.term.startsWith("УР") || te.term.startsWith("РОДИ") || te.term.startsWith("РОЖД")) {
                            LinkAnalyzer._setObject(_geo);
                            let li = LinkReferent._new2299(LinkType.BORN, p, _geo);
                            li = Utils.as(ad.registerReferent(li), LinkReferent);
                            li.addOccurenceOfRefTok(new ReferentToken(li, tt, t));
                            break;
                        }
                    }
                    if (tt.getReferent() instanceof DateReferent) 
                        continue;
                    if (tt.lengthChar > 1) 
                        break;
                }
            }
            if (p !== null && p.findSlot(PersonReferent.ATTR_BORN, null, true) === null && (t.getReferent() instanceof DateReferent)) {
                for (let tt = t.previous; tt !== null; tt = tt.previous) {
                    let te = Utils.as(tt, TextToken);
                    if (te !== null) {
                        if (te.term.startsWith("УРОЖ") || te.term.startsWith("РОДИ") || te.term.startsWith("РОЖД")) {
                            p.addSlot(PersonReferent.ATTR_BORN, t.getReferent(), false, 0);
                            break;
                        }
                    }
                    if (tt.getReferent() instanceof GeoReferent) 
                        continue;
                    if (tt.lengthChar > 1) 
                        break;
                }
                if (t.next instanceof TextToken) {
                    if (t.next.term.startsWith("РОДИ") || t.next.term.startsWith("РОЖД")) 
                        p.addSlot(PersonReferent.ATTR_BORN, t.getReferent(), false, 0);
                }
            }
            if (r instanceof PhoneReferent) {
                LinkAnalyzer._setObject(r);
                let li = LinkReferent._new2300(LinkType.CONTACT, r);
                if (curOrg !== null) 
                    li.object1 = curOrg;
                else 
                    li.object1 = p;
                li = Utils.as(ad.registerReferent(li), LinkReferent);
                li.addOccurenceOfRefTok(new ReferentToken(li, t, t));
                continue;
            }
            if (r instanceof UriReferent) {
                let sh = r.scheme;
                if (sh === "http" || sh === "https" || sh === "www") 
                    continue;
                LinkAnalyzer._setObject(r);
                let li = LinkReferent._new2299(LinkType.CONTACT, p, r);
                if (curOrg !== null) 
                    li.object1 = curOrg;
                else 
                    li.object1 = p;
                li = Utils.as(ad.registerReferent(li), LinkReferent);
                li.addOccurenceOfRefTok(new ReferentToken(li, t, t));
                continue;
            }
            if (r instanceof PersonIdentityReferent) {
                if (p === null) 
                    continue;
                LinkAnalyzer._setObject(r);
                let li = LinkReferent._new2299(LinkType.DOCUMENT, p, r);
                for (const s of r.slots) {
                    if (s.value instanceof DateReferent) {
                        li.dateFrom = Utils.as(s.value, DateReferent);
                        break;
                    }
                }
                li = Utils.as(ad.registerReferent(li), LinkReferent);
                li.addOccurenceOfRefTok(new ReferentToken(li, t, t));
                continue;
            }
            if ((r instanceof StreetReferent) || (r instanceof AddressReferent)) {
                LinkAnalyzer._setObject(r);
                let li = LinkReferent._new2300(LinkType.ADDRESS, r);
                if (curOrg !== null) 
                    li.object1 = curOrg;
                else 
                    li.object1 = p;
                LinkAnalyzer._corrAddrTyp(li, t);
                li = Utils.as(ad.registerReferent(li), LinkReferent);
                li.addOccurenceOfRefTok(new ReferentToken(li, t, t));
                continue;
            }
            if (r !== null && p !== null) {
                if (r.typeName === "WEAPON" || r.typeName === "TRANSPORT") {
                    LinkAnalyzer._setObject(r);
                    let li = LinkReferent._new2298(p, r);
                    li = Utils.as(ad.registerReferent(li), LinkReferent);
                    li.addOccurenceOfRefTok(new ReferentToken(li, t, t));
                    continue;
                }
            }
            if (t.isValue("ОБРАЗОВАНИЕ", null)) 
                curTyp = ResumeItemType.STUDY;
            if ((t.isValue2("ОПЫТ", "РАБОТЫ") || t.isValue2("МЕСТО", "РАБОТЫ") || t.isValue2("ПЕРИОД", "РАБОТЫ")) || t.isValue("РАБОТАТЬ", null)) 
                curTyp = ResumeItemType.ORGANIZATION;
            if (p === null) 
                continue;
            if (t.isNewlineBefore) {
                let rt = ResumeAnalyzer._parseOrg(t, curTyp);
                if (rt !== null) {
                    curTyp = rt.referent.typ;
                    let li = LinkAnalyzer._processResumeOrg(p, ad, rt);
                    if (li !== null) {
                        ad.registerReferent(li);
                        li.addOccurenceOfRefTok(rt);
                        curOrg = Utils.as(li.object2, OrganizationReferent);
                    }
                    t = rt.endToken;
                    continue;
                }
            }
            tmpOrgs.splice(0, tmpOrgs.length);
            ResumeAnalyzer._parseOrg2(t, null, tmpOrgs);
            if (tmpOrgs.length > 0) {
                for (const rt1 of tmpOrgs) {
                    let li = LinkAnalyzer._processResumeOrg(p, ad, rt1);
                    if (li !== null) {
                        ad.registerReferent(li);
                        li.addOccurenceOfRefTok(rt1);
                        curOrg = Utils.as(li.object2, OrganizationReferent);
                    }
                    t = rt1.endToken;
                }
                continue;
            }
            if (r instanceof OrganizationReferent) {
                curOrg = Utils.as(r, OrganizationReferent);
                LinkAnalyzer._processOrg(Utils.as(r, OrganizationReferent), ad, 0);
                let li = LinkReferent._new2298(p, r);
                let rt1 = LinkAnalyzer._corrOrgTyp(t, t, li);
                if (li.typ !== LinkType.UNDEFINED || li.param !== null) {
                    li = Utils.as(ad.registerReferent(li), LinkReferent);
                    li.addOccurenceOfRefTok(rt1);
                }
                continue;
            }
            if (r instanceof PersonPropertyReferent) {
                let li = LinkAnalyzer._processPersonAttr(p, Utils.as(r, PersonPropertyReferent), ad);
                if (li !== null) {
                    let rt1 = LinkAnalyzer._corrOrgTyp(t, t, li);
                    ad.registerReferent(li);
                    li.addOccurenceOfRefTok(rt1);
                    curOrg = Utils.as(li.object2, OrganizationReferent);
                }
                continue;
            }
            let pat = PersonAttrToken.tryAttach(t, PersonAttrTokenPersonAttrAttachAttrs.NO);
            if (pat !== null && pat.propRef !== null) {
                let li = LinkAnalyzer._processPersonAttr(p, pat.propRef, ad);
                if (li !== null) {
                    let rt1 = LinkAnalyzer._corrOrgTyp(t, pat.endToken, li);
                    ad.registerReferent(li);
                    li.addOccurenceOfRefTok(rt1);
                    curOrg = Utils.as(li.object2, OrganizationReferent);
                    t = pat.endToken;
                    continue;
                }
            }
        }
        for (const r of ad.referents) {
            let li = Utils.as(r, LinkReferent);
            if (li === null) 
                continue;
            if (li.typ !== LinkType.BORN) 
                continue;
            if (!(li.object1 instanceof PersonReferent)) 
                continue;
            let dt = Utils.as(li.object1.getSlotValue(PersonReferent.ATTR_BORN), DateReferent);
            if (dt !== null && li.dateFrom === null) 
                li.dateFrom = dt;
        }
    }
    
    static _isObject(r) {
        return r.getStringValue("OBJECT") === "true";
    }
    
    static _setObject(r) {
        r.addSlot("OBJECT", "true", true, 0);
    }
    
    static _processPerson(p, ad) {
        if (LinkAnalyzer._isObject(p)) 
            return;
        LinkAnalyzer._setObject(p);
        for (let i = p.slots.length - 1; i >= 0; i--) {
            let sl = p.slots[i];
            let r = Utils.as(sl.value, Referent);
            if ((r instanceof UriReferent) || (r instanceof PhoneReferent)) {
                LinkAnalyzer._setObject(r);
                ad.registerReferent(LinkReferent._new2306(p, r, LinkType.CONTACT));
                p.slots.splice(i, 1);
                continue;
            }
            if (r instanceof PersonPropertyReferent) {
                let li = LinkAnalyzer._processPersonAttr(p, Utils.as(r, PersonPropertyReferent), ad);
                if (li !== null) {
                    p.slots.splice(i, 1);
                    ad.registerReferent(li);
                }
            }
        }
    }
    
    static _processPersonAttr(p, pr, ad) {
        let nam = pr.name;
        if (pr.higher !== null) {
            let nam0 = MiscHelper.getTextMorphVarByCase(pr.higher.name, MorphCase.GENITIVE, false);
            nam = (nam + " " + ((nam0 != null ? nam0 : pr.higher.name)));
            pr = pr.higher;
        }
        let li = LinkReferent._new2307(p);
        let r = Utils.as(pr.getSlotValue(PersonPropertyReferent.ATTR_REF), Referent);
        if (r instanceof OrganizationReferent) {
            LinkAnalyzer._processOrg(Utils.as(r, OrganizationReferent), ad, 0);
            li.object2 = r;
            if (pr.kind === PersonPropertyKind.BOSS) 
                li.typ = LinkType.WORK;
        }
        else if (r instanceof PersonReferent) {
            LinkAnalyzer._processPerson(Utils.as(r, PersonReferent), ad);
            li.object2 = r;
            if (pr.kind === PersonPropertyKind.KIN) 
                li.typ = LinkType.FAMILY;
        }
        else if (r instanceof GeoReferent) {
            if (r.isCity && nam === "уроженец") {
                LinkAnalyzer._setObject(r);
                li.object2 = r;
                li.typ = LinkType.BORN;
                return li;
            }
            else 
                return null;
        }
        else 
            return null;
        li.param = nam;
        return li;
    }
    
    static _processResumeOrg(p, ad, rt) {
        let rr = Utils.as(rt.referent, ResumeItemReferent);
        if (!(rr.ref instanceof OrganizationReferent)) 
            return null;
        LinkAnalyzer._processOrg(Utils.as(rr.ref, OrganizationReferent), ad, 0);
        let li = LinkReferent._new2299((rr.typ === ResumeItemType.STUDY ? LinkType.STUDY : LinkType.WORK), p, rr.ref);
        li.param = rr.value;
        let dr = Utils.as(rr.dateRange, DateRangeReferent);
        if (dr !== null) {
            li.dateFrom = dr.dateFrom;
            li.dateTo = dr.dateTo;
        }
        if (rr.dateRange instanceof DateReferent) 
            li.dateFrom = li.dateTo = Utils.as(rr.dateRange, DateReferent);
        return li;
    }
    
    static _processOrg(org, ad, lev = 0) {
        if (lev > 4) 
            return;
        if (LinkAnalyzer._isObject(org)) 
            return;
        LinkAnalyzer._setObject(org);
        if (org.higher === null) 
            return;
        LinkAnalyzer._processOrg(org.higher, ad, lev + 1);
        ad.registerReferent(LinkReferent._new2306(org, org.higher, LinkType.UNIT));
        org.higher = null;
    }
    
    static _corrOrgTyp(tb, te, li) {
        let dt = null;
        let dr = null;
        let cou = 5;
        let t0 = tb;
        let t1 = te;
        for (let tt = tb.previous; tt !== null && cou > 0; tt = tt.previous) {
            if (tt.isNewlineAfter) 
                break;
            if (tt.lengthChar === 1) 
                continue;
            if (tt.getReferent() instanceof DateReferent) {
                dt = Utils.as(tt.getReferent(), DateReferent);
                t0 = tt;
            }
            else if (tt.getReferent() instanceof DateRangeReferent) 
                dr = Utils.as(tt.getReferent(), DateRangeReferent);
            else if (tt instanceof ReferentToken) 
                break;
            else if ((tt.isValue("ОБРАЗОВАНИЕ", null) || tt.isValue("ОКОНЧИТЬ", null) || tt.isValue("ОБУЧАТЬСЯ", null)) || tt.isValue("ЗАКОНЧИТЬ", null)) 
                li.typ = LinkType.STUDY;
            else if (tt.isValue("РАБОТАТЬ", null) || tt.isValue2("ПРОХОДИТЬ", "ПРАКТИКА")) 
                li.typ = LinkType.WORK;
            else if (tt.isValue("ПОСТУПИТЬ", null)) {
                if (tt.next !== null && tt.next.isValue2("НА", "РАБОТА")) 
                    li.typ = LinkType.WORK;
                else 
                    li.typ = LinkType.STUDY;
            }
            else 
                cou--;
        }
        if (li.typ !== LinkType.UNDEFINED && dt === null && dr === null) {
            for (let tt = te.next; tt !== null; tt = tt.next) {
                if (tt.isNewlineBefore) 
                    break;
                if (MiscHelper.canBeStartOfSentence(tt)) 
                    break;
                if (tt.lengthChar === 1 || tt.isValue("ПЕРИОД", null)) 
                    continue;
                if (tt.getReferent() instanceof DateReferent) {
                    dt = Utils.as(tt.getReferent(), DateReferent);
                    t1 = tt;
                    break;
                }
                else if (tt.getReferent() instanceof DateRangeReferent) {
                    dr = Utils.as(tt.getReferent(), DateRangeReferent);
                    t1 = tt;
                    break;
                }
            }
        }
        if (dr !== null) {
            li.dateFrom = dr.dateFrom;
            li.dateTo = dr.dateTo;
        }
        else if (dt !== null) 
            li.dateFrom = li.dateTo = dt;
        return new ReferentToken(li, t0, t1);
    }
    
    static _corrAddrTyp(li, t) {
        let isOrg = li.object1 instanceof OrganizationReferent;
        let typ = LinkAnalyzer._checkAddrTyp(t.beginToken, isOrg);
        if (typ !== null) {
            li.param = typ;
            return;
        }
        for (let tt = t.previous; tt !== null; tt = tt.previous) {
            typ = LinkAnalyzer._checkAddrTyp(tt, isOrg);
            if (typ !== null) {
                li.param = typ;
                return;
            }
            if (tt.isNewlineBefore || MiscHelper.canBeStartOfSentence(tt) || tt.isComma) 
                break;
        }
    }
    
    static _checkAddrTyp(t, isOrg) {
        let tt = Utils.as(t, TextToken);
        if (tt === null) 
            return null;
        if (tt.isValue("АДРЕС", null)) 
            return LinkAnalyzer._checkAddrTyp(t.next, isOrg);
        if (tt.term.startsWith("ЮР") && isOrg) 
            return "юридический";
        if (tt.term.startsWith("ФАКТ") && isOrg) 
            return "фактический";
        if (!isOrg) {
            if (tt.term.startsWith("ЗАРЕГ") || tt.term.startsWith("РЕГИСТР") || tt.term.startsWith("ПРОПИС")) 
                return "регистрация";
            if (tt.term.startsWith("ФАКТИЧ") || tt.term.startsWith("ПРОЖИВ")) 
                return "проживание";
        }
        return null;
    }
    
    static initialize() {
        /* this is synchronized block by LinkAnalyzer.m_Lock, but this feature isn't supported in JS */ {
            if (LinkAnalyzer.m_Initialized) 
                return;
            LinkAnalyzer.m_Initialized = true;
            MetaLink.initialize();
            ProcessorService.registerAnalyzer(new LinkAnalyzer());
        }
    }
    
    static static_constructor() {
        LinkAnalyzer.ANALYZER_NAME = "LINK";
        LinkAnalyzer.m_Initialized = false;
        LinkAnalyzer.m_Lock = new Object();
    }
}


LinkAnalyzer.static_constructor();

module.exports = LinkAnalyzer