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

const MetaToken = require("./../MetaToken");
const ComplexNumCompareType = require("./ComplexNumCompareType");
const SingleNumComparer = require("./SingleNumComparer");
const SingleNumValueType = require("./SingleNumValueType");
const SingleNumToken = require("./SingleNumToken");
const NumberToken = require("./../NumberToken");
const ReferentToken = require("./../ReferentToken");

/**
 * Поддержка сложной нумерации разделов, пунктов, формул и т.п. 
 * (составная, возможны римские цифры, буквы)
 */
class ComplexNumToken extends MetaToken {
    
    constructor(b, e) {
        super(b, e, null);
        this.nums = new Array();
    }
    
    get normal() {
        if (this.nums.length === 0) 
            return null;
        let res = this.nums[0].normal;
        for (let i = 1; i < this.nums.length; i++) {
            res = (res + "." + this.nums[i].normal);
        }
        return res;
    }
    
    get signature() {
        let res = new StringBuilder();
        if (this.prefix !== null) 
            res.append(this.prefix);
        for (const n of this.nums) {
            if (n.vals.length === 0) 
                continue;
            if (n.vals[0].typ === SingleNumValueType.DIGIT) 
                res.append('n');
            else if (n.vals[0].typ === SingleNumValueType.ROMAN) 
                res.append('r');
            else if (n.vals[0].typ === SingleNumValueType.LETTER) 
                res.append('l');
            else 
                res.append('?');
        }
        if (this.suffix !== null) 
            res.append(this.suffix);
        return res.toString();
    }
    
    get prefix() {
        return (this.nums.length > 0 ? this.nums[0].prefix : null);
    }
    
    get suffix() {
        return (this.nums.length > 0 ? this.nums[this.nums.length - 1].suffix : null);
    }
    
    get isOne() {
        if (this.nums.length === 0) 
            return false;
        return this.nums[this.nums.length - 1].isOne;
    }
    
    toString() {
        return this.toStringEx(false);
    }
    
    toStringEx(ignoreSuffix) {
        if (this.nums.length === 0) 
            return "";
        let res = this.nums[0].value;
        for (let i = 1; i < this.nums.length; i++) {
            res = (res + ((this.nums[i].prefix != null ? this.nums[i].prefix : "")) + this.nums[i].value + (this.nums[i].suffix === null || this.nums[i].value.endsWith(this.nums[i].suffix) ? "" : this.nums[i].suffix));
        }
        if (ignoreSuffix && this.suffix !== null && res.endsWith(this.suffix)) 
            res = res.substring(0, 0 + res.length - this.suffix.length);
        return res;
    }
    
    /**
     * Выделить сложный номер с указанного токена
     * @param t 
     * @param prev предыдущий номер (если есть)
     * @param force обязательно ли выделять
     * @param ignoreLastSuffix суффикс у последнего составного числа игнорировать
     * @return номер или null
     */
    static tryParse(t, prev = null, force = false, ignoreLastSuffix = false) {
        if (t === null) 
            return null;
        if (t instanceof ReferentToken) {
            let r = t.getReferent();
            if (r !== null && r.typeName === "BOOKLINKREF") 
                t = t.beginToken;
        }
        let nt = SingleNumToken.tryParse(t, true, force);
        if (nt === null) 
            return null;
        let res = new ComplexNumToken(t, nt.endToken);
        res.nums.push(nt);
        for (t = nt.endToken.next; t !== null; t = t.next) {
            if (t.isWhitespaceBefore) {
                if (t.whitespacesBeforeCount > 2) 
                    break;
                if (force && t.isCharOf("<(")) {
                }
                else 
                    break;
            }
            nt = SingleNumToken.tryParse(t, false, force);
            if (nt === null) {
                if (((force && t.isHiphen && !t.isNewlineBefore) && !t.isNewlineAfter && (t.next instanceof NumberToken)) && res.nums[res.nums.length - 1].suffix === null) {
                    nt = SingleNumToken.tryParse(t.next, false, false);
                    if (nt !== null) {
                        res.nums[res.nums.length - 1].endToken = t;
                        res.nums[res.nums.length - 1].suffix = "-";
                    }
                }
                if (nt === null) 
                    break;
            }
            if (nt.vals[0].typ === SingleNumValueType.LETTER) {
                if (res.nums[0].vals[0].typ === SingleNumValueType.LETTER) {
                    if (res.nums[0].suffix === ")") 
                        break;
                    return null;
                }
                if (res.nums[res.nums.length - 1].vals[0].typ === SingleNumValueType.LETTER) {
                    if (res.nums[res.nums.length - 1].suffix === ")") 
                        break;
                    return null;
                }
            }
            if (nt.suffix === null && res.suffix === ")" && !nt.value.startsWith("<")) 
                break;
            if (t.getMorphClassInDictionary().isPreposition && res.suffix !== null) {
                if (t.next !== null && t.next.isCharOf("<.]>)")) {
                }
                else 
                    break;
            }
            res.nums.push(nt);
            res.endToken = (t = nt.endToken);
        }
        if (res.nums.length === 1) {
            if (res.prefix === "<" && res.suffix === ">") 
                return null;
            if (!force && res.isNewlineBefore && res.isNewlineAfter) 
                return null;
        }
        if ((ignoreLastSuffix && res.suffix !== null && res.suffix.length === 1) && res.endToken.isChar(res.suffix[0])) {
            res.endToken = res.endToken.previous;
            let n1 = res.nums[res.nums.length - 1];
            if (n1.value !== null && n1.value.endsWith(n1.suffix)) 
                n1.value = n1.value.substring(0, 0 + n1.value.length - 1);
            n1.suffix = null;
            n1.endToken = res.endToken;
        }
        return res;
    }
    
    static correctSeq(seq) {
        if (seq.length === 0) 
            return;
        seq[0].correct(null);
        for (let i = 1; i < seq.length; i++) {
            seq[i].correct(seq[i - 1]);
        }
        let lat = 0;
        let cyr = 0;
        for (const s of seq) {
            for (const n of s.nums) {
                for (const v of n.vals) {
                    if (v.typ === SingleNumValueType.LETTER) {
                        if ((v.letter.charCodeAt(0)) < 0x80) 
                            lat++;
                        else 
                            cyr++;
                    }
                }
            }
        }
        if (lat > cyr || cyr > lat) {
            for (const s of seq) {
                for (const n of s.nums) {
                    for (let i = n.vals.length - 1; i >= 0; i--) {
                        if (n.vals[i].typ === SingleNumValueType.LETTER && n.vals.length > 1) {
                            if ((n.vals[i].letter.charCodeAt(0)) < 0x80) {
                                if (cyr > lat) 
                                    n.vals.splice(i, 1);
                            }
                            else if (lat > cyr) 
                                n.vals.splice(i, 1);
                        }
                    }
                }
            }
        }
    }
    
    correct(prev) {
        if (prev !== null && prev.nums.length === this.nums.length) 
            this.nums[this.nums.length - 1].correct(prev.nums[prev.nums.length - 1]);
        else if (prev === null) 
            this.nums[this.nums.length - 1].correct(null);
    }
    
    canBePsevdoSubseq(sub) {
        if (sub === null) 
            return false;
        if (this.prefix !== sub.prefix || this.suffix !== sub.suffix) 
            return false;
        if ((this.nums.length + 1) !== sub.nums.length) 
            return false;
        if (!sub.nums[sub.nums.length - 1].isOne) 
            return false;
        let comp = new SingleNumComparer();
        for (let i = 0; i < this.nums.length; i++) {
            comp.process(this.nums[i], sub.nums[i]);
            if (comp.typ !== ComplexNumCompareType.EQUALS) 
                return false;
        }
        return true;
    }
}


module.exports = ComplexNumToken