/*
 * 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 Referent = require("./../Referent");
const Analyzer = require("./../Analyzer");
const Token = require("./../Token");
const GetTextAttr = require("./../core/GetTextAttr");
const MetaToken = require("./../MetaToken");
const GoodAttrType = require("./GoodAttrType");
const ReferentToken = require("./../ReferentToken");
const PullentiNerCoreInternalResourceHelper = require("./../core/internal/PullentiNerCoreInternalResourceHelper");
const AttrMeta = require("./internal/AttrMeta");
const GoodMeta = require("./internal/GoodMeta");
const MiscHelper = require("./../core/MiscHelper");
const Termin = require("./../core/Termin");
const ProcessorService = require("./../ProcessorService");
const AnalyzerDataWithOntology = require("./../core/AnalyzerDataWithOntology");
const GoodAttributeReferent = require("./GoodAttributeReferent");
const GoodAttrToken = require("./internal/GoodAttrToken");
const GoodReferent = require("./GoodReferent");

/**
 * Анализатор названий товаров (номенклатур) и выделение из них характеристик. 
 * Специфический анализатор, то есть нужно явно создавать процессор через функцию CreateSpecificProcessor, 
 * указав имя анализатора. 
 * Выделение происходит из небольшого фрагмента текста, содержащего только один товар и его характеристики. 
 * Выделять из большого текста такие фрагменты - это не задача анализатора. 
 * Примеры текстов: "Плед OXFORD Cashnere Touch Herringbone 1.5-сп с эффектом кашемира", 
 * "Имплантат размером 5 см х 5 см предназначен для реконструкции твердой мозговой оболочки. Изготовлен из биологически совместимого материала на коллагеновой основе.".
 */
class GoodsAnalyzer extends Analyzer {
    
    get name() {
        return GoodsAnalyzer.ANALYZER_NAME;
    }
    
    get caption() {
        return "Товары и атрибуты";
    }
    
    get description() {
        return "Товары и их атрибуты";
    }
    
    get isSpecific() {
        return true;
    }
    
    clone() {
        return new GoodsAnalyzer();
    }
    
    get typeSystem() {
        return [AttrMeta.GLOBAL_META, GoodMeta.GLOBAL_META];
    }
    
    get images() {
        let res = new Hashtable();
        res.put(AttrMeta.ATTR_IMAGE_ID, PullentiNerCoreInternalResourceHelper.getBytes("bullet_ball_glass_grey.png"));
        res.put(GoodMeta.IMAGE_ID, PullentiNerCoreInternalResourceHelper.getBytes("shoppingcart.png"));
        return res;
    }
    
    createReferent(type) {
        if (type === GoodAttributeReferent.OBJ_TYPENAME) 
            return new GoodAttributeReferent();
        if (type === GoodReferent.OBJ_TYPENAME) 
            return new GoodReferent();
        return null;
    }
    
    get progressWeight() {
        return 100;
    }
    
    createAnalyzerData() {
        return new AnalyzerDataWithOntology();
    }
    
    process(kit) {
        let ad = kit.getAnalyzerData(this);
        let delta = 100000;
        let parts = Utils.intDiv(((kit.sofa.text.length + delta) - 1), delta);
        if (parts === 0) 
            parts = 1;
        let cur = 0;
        let nextPos = 0;
        let _goods = new Array();
        for (let t = kit.firstToken; t !== null; t = t.next) {
            if (!t.isNewlineBefore) 
                continue;
            if (t.beginChar > nextPos) {
                nextPos += delta;
                cur++;
                if (!this.onProgress(cur, parts, kit)) 
                    break;
            }
            if (!t.chars.isLetter && t.next !== null) 
                t = t.next;
            let rts = GoodAttrToken.tryParseList(t);
            if (rts === null || rts.length === 0) 
                continue;
            let good = new GoodReferent();
            for (const rt of rts) {
                rt.referent = ad.registerReferent(rt.referent);
                if (good.findSlot(GoodReferent.ATTR_ATTR, rt.referent, true) === null) 
                    good.addSlot(GoodReferent.ATTR_ATTR, rt.referent, false, 0);
                kit.embedToken(rt);
            }
            _goods.push(good);
            let rt0 = new ReferentToken(good, rts[0], rts[rts.length - 1]);
            kit.embedToken(rt0);
            t = rt0;
        }
        for (const g of _goods) {
            ad.referents.push(g);
        }
    }
    
    processOntologyItem(begin) {
        if (begin === null) 
            return null;
        let ga = new GoodAttributeReferent();
        if (begin.chars.isLatinLetter) {
            if (begin.isValue("KEYWORD", null)) {
                ga.typ = GoodAttrType.KEYWORD;
                begin = begin.next;
            }
            else if (begin.isValue("CHARACTER", null)) {
                ga.typ = GoodAttrType.CHARACTER;
                begin = begin.next;
            }
            else if (begin.isValue("PROPER", null)) {
                ga.typ = GoodAttrType.PROPER;
                begin = begin.next;
            }
            else if (begin.isValue("MODEL", null)) {
                ga.typ = GoodAttrType.MODEL;
                begin = begin.next;
            }
            if (begin === null) 
                return null;
        }
        let res = new ReferentToken(ga, begin, begin);
        for (let t = begin; t !== null; t = t.next) {
            if (t.isChar(';')) {
                ga.addSlot(GoodAttributeReferent.ATTR_VALUE, MiscHelper.getTextValue(begin, t.previous, GetTextAttr.NO), false, 0);
                begin = t.next;
                continue;
            }
            res.endToken = t;
        }
        if (res.endChar > begin.beginChar) 
            ga.addSlot(GoodAttributeReferent.ATTR_VALUE, MiscHelper.getTextValue(begin, res.endToken, GetTextAttr.NO), false, 0);
        if (ga.typ === GoodAttrType.UNDEFINED) {
            if (!begin.chars.isAllLower) 
                ga.typ = GoodAttrType.PROPER;
        }
        return res;
    }
    
    static initialize() {
        /* this is synchronized block by GoodsAnalyzer.m_Lock, but this feature isn't supported in JS */ {
            if (GoodsAnalyzer.m_Initialized) 
                return;
            GoodsAnalyzer.m_Initialized = true;
            AttrMeta.initialize();
            GoodMeta.initialize();
            try {
                Termin.ASSIGN_ALL_TEXTS_AS_NORMAL = true;
                GoodAttrToken.initialize();
                Termin.ASSIGN_ALL_TEXTS_AS_NORMAL = false;
            } catch (ex) {
                throw new Error(ex.message);
            }
            ProcessorService.registerAnalyzer(new GoodsAnalyzer());
        }
    }
    
    static static_constructor() {
        GoodsAnalyzer.ANALYZER_NAME = "GOODS";
        GoodsAnalyzer.m_Initialized = false;
        GoodsAnalyzer.m_Lock = new Object();
    }
}


GoodsAnalyzer.static_constructor();

module.exports = GoodsAnalyzer