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

const GetTextAttr = require("./../../core/GetTextAttr");
const NumberToken = require("./../../NumberToken");
const ChemicalFormulaReferent = require("./../ChemicalFormulaReferent");
const MorphGender = require("./../../../morph/MorphGender");
const MetaToken = require("./../../MetaToken");
const MorphNumber = require("./../../../morph/MorphNumber");
const LanguageHelper = require("./../../../morph/LanguageHelper");
const Token = require("./../../Token");
const NounPhraseParseAttr = require("./../../core/NounPhraseParseAttr");
const TextToken = require("./../../TextToken");
const TerminParseAttr = require("./../../core/TerminParseAttr");
const Termin = require("./../../core/Termin");
const TerminCollection = require("./../../core/TerminCollection");
const ChemicalUnit = require("./ChemicalUnit");
const MiscHelper = require("./../../core/MiscHelper");
const NounPhraseHelper = require("./../../core/NounPhraseHelper");

class ChemicalToken extends MetaToken {
    
    toString() {
        let res = new StringBuilder();
        if (this.hiphenBefore) 
            res.append("-");
        if (this.name !== null) 
            res.append(this.name);
        if (this.subtokens !== null) {
            res.append(this.bracket);
            for (const s of this.subtokens) {
                res.append(s.toString());
            }
            res.append((this.bracket === '[' ? ']' : ')'));
        }
        if (this.items !== null && this.name === null) {
            for (const u of this.items) {
                res.append(u.mnem[0]);
                if (u.mnem.length > 1) 
                    res.append(u.mnem[1].toLowerCase());
            }
        }
        if (this.num > 0) 
            res.append(this.num);
        return res.toString();
    }
    
    constructor(b, e) {
        super(b, e, null);
        this.subtokens = null;
        this.bracket = String.fromCharCode(0);
        this.hiphenBefore = false;
        this.name = null;
        this.items = null;
        this.num = 0;
        this.isDoubt = false;
    }
    
    static tryParseList(t, lev = 0) {
        ChemicalToken.initialize();
        let res = null;
        for (let tt = t; tt !== null; tt = tt.next) {
            let it = ChemicalToken.tryParse(tt, (res === null || res.length === 0 ? null : res[res.length - 1]), lev);
            if (it !== null) {
                if (res === null) 
                    res = new Array();
                if (it.name === null && res.length > 0 && it.isWhitespaceBefore) {
                    if (res[res.length - 1].name === null) 
                        break;
                }
                res.push(it);
                tt = it.endToken;
                continue;
            }
            if ((tt.isValue("ИЛИ", null) && res !== null && res.length === 1) && res[0].name !== null) {
                it = ChemicalToken.tryParse(tt.next, null, lev);
                if (it !== null && it.name !== null) {
                    res.push(it);
                    tt = it.endToken;
                    continue;
                }
            }
            break;
        }
        return res;
    }
    
    static tryParse(t, prev, lev) {
        if (lev > 3) 
            return null;
        let tt = Utils.as(t, TextToken);
        if (tt === null) 
            return null;
        if (tt.isCharOf("[(")) {
            let li = ChemicalToken.tryParseList(tt.next, lev + 1);
            if (li === null || li.length === 0) 
                return null;
            let ee = li[li.length - 1].endToken.next;
            if (ee === null || !ee.isCharOf("])")) 
                return null;
            let ct = ChemicalToken._new538(t, ee, li);
            if (tt.isChar('(')) 
                ct.bracket = '(';
            else 
                ct.bracket = '[';
            ct._addNum();
            return ct;
        }
        if (tt.isHiphen) {
            if (prev === null) 
                return null;
            let mustBeName = false;
            if (tt.isWhitespaceBefore || tt.isWhitespaceAfter) {
                if (prev.name !== null) {
                }
                else 
                    mustBeName = true;
            }
            let ct = ChemicalToken.tryParse(tt.next, null, lev + 1);
            if (ct === null) 
                return null;
            if (mustBeName && ct.name === null) 
                return null;
            if (prev.name !== null && ct.name !== null) 
                return null;
            if (prev.name === null && ct.name === null) 
                ct.hiphenBefore = true;
            ct.beginToken = tt;
            return ct;
        }
        if (tt.chars.isLetter && !tt.chars.isAllLower) {
            let str = tt.getSourceText();
            let cus = null;
            let i = 0;
            for (i = 0; i < str.length; i++) {
                let ch0 = str[i];
                if (Utils.isLowerCase(ch0)) 
                    break;
                if ((ch0.charCodeAt(0)) > 0x80) {
                    ch0 = LanguageHelper.getLatForCyr(ch0);
                    if ((ch0.charCodeAt(0)) === 0) 
                        break;
                }
                let mnem = (ch0);
                if (((i + 1) < str.length) && Utils.isLowerCase(str[i + 1])) {
                    let ch1 = str[i + 1].toUpperCase();
                    if ((ch1.charCodeAt(0)) > 0x80) {
                        ch1 = LanguageHelper.getLatForCyr(ch1);
                        if ((ch1.charCodeAt(0)) === 0) 
                            break;
                    }
                    mnem = (ch0 + ch1);
                    i++;
                }
                let cu = null;
                let wrapcu539 = new RefOutArgWrapper();
                let inoutres540 = ChemicalToken.m_UnitsByMnem.tryGetValue(mnem, wrapcu539);
                cu = wrapcu539.value;
                if (!inoutres540) 
                    break;
                if (cus === null) 
                    cus = new Array();
                cus.push(cu);
            }
            if (i >= str.length && cus !== null) {
                let ct = ChemicalToken._new541(t, t, cus);
                ct._addNum();
                if (ct.num === 0 && ((!t.getMorphClassInDictionary().isUndefined || (t.lengthChar < 2)))) 
                    ct.isDoubt = true;
                for (let ii = 0; ii < (ct.items.length - 1); ii++) {
                    for (let jj = ii + 1; jj < ct.items.length; jj++) {
                        if (ct.items[ii] === ct.items[jj]) 
                            ct.isDoubt = true;
                    }
                }
                if (ct.items.length > 6) 
                    ct.isDoubt = true;
                return ct;
            }
        }
        let tt1 = null;
        for (t = tt; t !== null; t = t.next) {
            let tok = ChemicalToken.m_Termins.tryParse(t, TerminParseAttr.NO);
            if (tok !== null) {
                tt1 = (t = tok.endToken);
                continue;
            }
            let npt = NounPhraseHelper.tryParse(t, NounPhraseParseAttr.NO, 0, null);
            if (npt !== null) {
                let ok = false;
                for (let ttt = npt.beginToken; ttt !== null && ttt.endChar <= npt.endChar; ttt = ttt.next) {
                    if (ChemicalToken._canBePartName(Utils.as(ttt, TextToken))) 
                        ok = true;
                }
                if (ok) {
                    tt1 = (t = npt.endToken);
                    continue;
                }
            }
            if (ChemicalToken._canBePartName(Utils.as(t, TextToken))) {
                tt1 = t;
                continue;
            }
            break;
        }
        if (tt1 !== null) {
            let ci = new ChemicalToken(tt, tt1);
            ci.name = MiscHelper.getTextValue(tt, tt1, GetTextAttr.FIRSTNOUNGROUPTONOMINATIVE);
            if (tt === tt1) {
                ci.isDoubt = true;
                let tok = ChemicalToken.m_Termins.tryParse(tt, TerminParseAttr.NO);
                if (tok !== null && (tok.termin.tag instanceof ChemicalUnit)) 
                    ci.isDoubt = false;
            }
            if (tt.previous !== null && tt.previous.isValue("ФОРМУЛА", null)) 
                ci.isDoubt = false;
            return ci;
        }
        return null;
    }
    
    static _canBePartName(t) {
        if (t === null) 
            return false;
        let tok = ChemicalToken.m_Termins.tryParse(t, TerminParseAttr.NO);
        if (tok !== null) 
            return true;
        let val = t.getNormalCaseText(null, MorphNumber.UNDEFINED, MorphGender.UNDEFINED, false);
        let mc = t.getMorphClassInDictionary();
        if (((((val.endsWith("ИД") || val.endsWith("ОЛ") || val.endsWith("КИСЬ")) || val.endsWith("ТАН") || val.endsWith("ОЗА")) || val.endsWith("ИН") || val.endsWith("АТ")) || val.endsWith("СИД") || val.startsWith("ДИ")) || val.startsWith("ТРИ") || val.startsWith("ЧЕТЫР")) {
            if (mc.isUndefined) 
                return true;
            if ((((val.endsWith("КИСЬ") || val.endsWith("ТОРИД") || val.endsWith("ЦЕТАТ")) || val.endsWith("РАЗИН") || val.endsWith("КСИД")) || val.endsWith("ФИД") || val.endsWith("РИД")) || val.endsWith("ОНАТ")) 
                return true;
        }
        if ((((((val.startsWith("ГЕКСА") || val.startsWith("ДЕКА") || val.startsWith("ТЕТРА")) || val.startsWith("ПЕНТА") || val.startsWith("ГЕПТА")) || val.startsWith("ОКТА") || val.startsWith("НОНА")) || val.startsWith("УНДЕКА") || val.startsWith("ДОДЕКА")) || val.startsWith("ЭЙКОЗА") || val.startsWith("ГЕКТА")) || val.startsWith("КИЛА") || val.startsWith("МИРИА")) 
            return true;
        return false;
    }
    
    static createReferent(li) {
        if (li.length === 1) {
            if (li[0].isDoubt) 
                return null;
            if ((li[0].bracket.charCodeAt(0)) !== 0) 
                return null;
            if (((li[0].items !== null && li[0].items.length === 1)) || (((li[0].lengthChar < 5) || li[0].isDoubt))) {
                let ok = false;
                let cou = 40;
                for (let tt = li[0].beginToken.previous; tt !== null && cou > 0; tt = tt.previous,cou--) {
                    if (tt.getReferent() instanceof ChemicalFormulaReferent) {
                        ok = true;
                        break;
                    }
                    if (ChemicalToken.m_Termins.tryParse(tt, TerminParseAttr.NO) !== null || ChemicalToken.m_Keywords.tryParse(tt, TerminParseAttr.NO) !== null) {
                        ok = true;
                        break;
                    }
                }
                cou = 40;
                if (!ok) {
                    for (let tt = li[li.length - 1].endToken.next; tt !== null && cou > 0; tt = tt.next,cou--) {
                        if (tt.getReferent() instanceof ChemicalFormulaReferent) {
                            ok = true;
                            break;
                        }
                        if (ChemicalToken.m_Termins.tryParse(tt, TerminParseAttr.NO) !== null || ChemicalToken.m_Keywords.tryParse(tt, TerminParseAttr.NO) !== null) {
                            ok = true;
                            break;
                        }
                    }
                }
                if (!ok) 
                    return null;
            }
        }
        let res = new ChemicalFormulaReferent();
        let val = null;
        for (let i = 0; i < li.length; i++) {
            if (li[i].name !== null) 
                res.addSlot(ChemicalFormulaReferent.ATTR_NAME, li[i].name, false, 0);
            else if (val === null) 
                val = li[i].toString();
            else 
                val += li[i].toString();
        }
        if (val !== null) 
            res.value = val;
        return res;
    }
    
    _addNum() {
        if (this.endToken.isWhitespaceAfter) 
            return;
        if (this.endToken.next instanceof NumberToken) {
            this.endToken = this.endToken.next;
            this.num = this.endToken.intValue;
            return;
        }
        if (this.endToken.next.lengthChar === 1) {
            for (let i = 2; i <= 9; i++) {
                if (this.endToken.next.isChar(String.fromCharCode((0x2080 + i)))) {
                    this.endToken = this.endToken.next;
                    this.num = 2;
                    return;
                }
            }
        }
        if ((this.endToken.next !== null && this.endToken.next.isCharOf("<[") && (this.endToken.next.next instanceof NumberToken)) && this.endToken.next.next.next !== null && this.endToken.next.next.next.isCharOf(">]")) {
            this.num = this.endToken.next.next.intValue;
            this.endToken = this.endToken.next.next.next;
            return;
        }
    }
    
    static initialize() {
        if (ChemicalToken.UNITS !== null) 
            return;
        ChemicalToken.m_Termins = new TerminCollection();
        ChemicalToken.m_Keywords = new TerminCollection();
        ChemicalToken.m_UnitsByMnem = new Hashtable();
        ChemicalToken.UNITS = new Array();
        let all = (((((("Водород,H;Гелий,He;Литий,Li;Бериллий,Be;Бор,B;Углерод,C;Азот,N;Кислород,O;Фтор,F;" + "Неон,Ne;Натрий,Na;Магний,Mg;Алюминий,Al;Кремний,Si;Фосфор,P;Сера,S;Хлор,Cl;Аргон,Ar;Калий,K;" + "Кальций,Ca;Скандий,Sc;Титан,Ti;Ванадий,V;Хром,Cr;Марганец,Mn;Железо,Fe;Кобальт,Co;Никель,Ni;") + "Медь,Cu;Цинк,Zn;Галлий,Ga;Германий,Ge;Мышьяк,As;Селен,Se;Бром,Br;Криптон,Kr;Рубидий,Rb;" + "Стронций,Sr;Иттрий,Y;Цирконий,Zr;Ниобий,Nb;Молибден,Mo;Технеций,Tc;Рутений,Ru;Родий,Rh;") + "Палладий,Pd;Серебро,Ag;Кадмий,Cd;Индий,In;Олово,Sn;Сурьма,Sb;Теллур,Te;Иод,I;Ксенон,Xe;" + "Цезий,Cs;Барий,Ba;Лантан,La;Церий,Ce;Празеодим,Pr;Неодим,Nd;Прометий,Pm;Самарий,Sm;") + "Европий,Eu;Гадолиний,Gd;Тербий,Tb;Диспрозий,Dy;Гольмий,Ho;Эрбий,Er;Тулий,Tm;Иттербий,Yb;" + "Лютеций,Lu;Гафний,Hf;Тантал,Ta;Вольфрам,W;Рений,Re;Осмий,Os;Иридий,Ir;Платина,Pt;") + "Золото,Au;Ртуть,Hg;Таллий,Tl;Свинец,Pb;Висмут,Bi;Полоний,Po;Астат,At;Радон,Rn;Франций,Fr;" + "Радий,Ra;Актиний,Ac;Торий,Th;Протактиний,Pa;Уран,U;Нептуний,Np;Плутоний,Pu;Америций,Am;") + "Кюрий,Cm;Берклий,Bk;Калифорний,Cf;Эйнштейний,Es;Фермий,Fm;Менделевий,Md;Нобелий,No;Лоуренсий,Lr;" + "Резерфордий,Rf;Дубний,Db;Сиборгий,Sg;Борий,Bh;Хассий,Hs;Мейтнерий,Mt;Дармштадтий,Ds;Рентгений,Rg;") + "Коперниций,Cn;Нихоний,Nh;Флеровий,Fl;Московий,Mc;Ливерморий,Lv;Теннессин,Ts;Оганесон,Og";
        for (const p of Utils.splitString(all.toUpperCase(), ';', false)) {
            let i = p.indexOf(',');
            if (i < 0) 
                continue;
            let ci = new ChemicalUnit(p.substring(i + 1), p.substring(0, 0 + i));
            ChemicalToken.UNITS.push(ci);
            ChemicalToken.m_Termins.add(Termin._new96(ci.nameCyr, ci));
            ChemicalToken.m_UnitsByMnem.put(ci.mnem, ci);
        }
        for (const s of ["КИСЛОТА", "РАСТВОР", "СПИРТ", "ВОДА", "СОЛЬ", "АММИАК", "БУТАН", "БЕНЗОЛ", "КЕРОСИН", "АМИН", "СКИПИДАР", "ОКИСЬ", "ГИДРИТ", "АММОНИЙ", "ПЕРЕКИСЬ", "КАРБОНАТ"]) {
            ChemicalToken.m_Termins.add(new Termin(s));
        }
        for (const s of ["МЕТАЛЛ", "ГАЗ", "ТОПЛИВО", "МОНОТОПЛИВО", "СМЕСЬ", "ХИМИЧЕСКИЙ", "МОЛЕКУЛА", "АТОМ", "МОЛЕКУЛЯРНЫЙ", "АТОМАРНЫЙ"]) {
            ChemicalToken.m_Keywords.add(new Termin(s));
        }
    }
    
    static _new538(_arg1, _arg2, _arg3) {
        let res = new ChemicalToken(_arg1, _arg2);
        res.subtokens = _arg3;
        return res;
    }
    
    static _new541(_arg1, _arg2, _arg3) {
        let res = new ChemicalToken(_arg1, _arg2);
        res.items = _arg3;
        return res;
    }
    
    static static_constructor() {
        ChemicalToken.m_Termins = null;
        ChemicalToken.m_Keywords = null;
        ChemicalToken.m_UnitsByMnem = null;
        ChemicalToken.UNITS = null;
    }
}


ChemicalToken.static_constructor();

module.exports = ChemicalToken