/*
 * SDK Pullenti Unitext, 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# 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 MiscHelper = require("./../util/MiscHelper");
const UnitextStyledFragmentType = require("./UnitextStyledFragmentType");

/**
 * Фрагмент, с которым связаны стили UnitextStyle (если их выделение реализовано для входного формата). 
 * Представляет собой иерархию.
 * Стилевой фрагмент
 */
class UnitextStyledFragment {
    
    constructor() {
        this.parent = null;
        this.children = new Array();
        this.m_Doc = null;
        this.typ = UnitextStyledFragmentType.UNDEFINED;
        this.styleId = -1;
        this.m_Style = null;
        this.beginChar = -1;
        this.endChar = -1;
        this.text = null;
        this.tag = null;
        this.dispInline = -1;
        this.lenInline = 0;
    }
    
    get doc() {
        if (this.m_Doc !== null) 
            return this.m_Doc;
        if (this.parent !== null) 
            return this.parent.doc;
        return null;
    }
    set doc(value) {
        this.m_Doc = value;
        return value;
    }
    
    get style() {
        if (this.m_Style !== null) 
            return this.m_Style;
        let _doc = this.doc;
        if (_doc === null || _doc.styles === null) 
            return null;
        if (this.styleId >= 0 && (this.styleId < _doc.styles.length)) 
            return _doc.styles[this.styleId];
        return null;
    }
    set style(value) {
        this.m_Style = value;
        if (value === null) 
            this.styleId = -1;
        else 
            this.styleId = value.id;
        return value;
    }
    
    toString() {
        let res = new StringBuilder();
        if (this.typ !== UnitextStyledFragmentType.UNDEFINED) {
            res.append(String(this.typ)).append(" ");
            res.append("[").append(this.beginChar).append("..").append(this.endChar).append("] ");
        }
        if (this.style !== null) 
            res.append(this.style);
        if (this.text !== null) {
            let txt = (this.text.length < 100 ? this.text : (this.text.substring(0, 0 + 100) + "..."));
            res.append(" \"").append(Utils.replaceString(txt, "\n", "\\n")).append("\"");
        }
        return res.toString();
    }
    
    /**
     * Найти значение атрибута стиля (от текущего вверх по иерархии, пока не найдём)
     * @param name имя атрибута
     * @param defvalue значение, если не нашли
     * @return значение
     */
    getStyleAttr(name, defvalue = null) {
        for (let fr = this; fr !== null; fr = fr.parent) {
            if (fr.styleId >= 0) {
                let sty = fr.style;
                if (sty !== null) {
                    let res = sty.getAttr(name);
                    if (res !== null) 
                        return res;
                }
            }
        }
        return defvalue;
    }
    
    clone() {
        let res = new UnitextStyledFragment();
        res.typ = this.typ;
        res.styleId = this.styleId;
        res.doc = this.doc;
        res.parent = this.parent;
        res.beginChar = this.beginChar;
        res.endChar = this.endChar;
        res.text = this.text;
        for (const ch of this.children) {
            let chh = ch.clone();
            chh.parent = res;
            res.children.push(chh);
        }
        return res;
    }
    
    addChild(ch) {
        ch.parent = this;
        if (this.children.length > 0 && (ch.endChar < this.children[0].beginChar)) {
            this.children.splice(0, 0, ch);
            return;
        }
        for (let i = 0; i < (this.children.length - 1); i++) {
            if ((this.children[i].endChar < ch.beginChar) && (ch.endChar < this.children[i + 1].beginChar)) {
                this.children.splice(i + 1, 0, ch);
                return;
            }
        }
        this.children.push(ch);
    }
    
    getXml(xml, _tag = null, outStyleFull = false) {
        xml.writeStartElement((_tag != null ? _tag : "stylefrag"));
        if (this.typ !== UnitextStyledFragmentType.UNDEFINED) 
            xml.writeAttributeString("typ", this.typ.toString().toLowerCase());
        if (this.styleId >= 0) 
            xml.writeAttributeString("style", this.styleId.toString());
        xml.writeAttributeString("b", this.beginChar.toString());
        xml.writeAttributeString("e", this.endChar.toString());
        if (this.styleId >= 0 && this.style !== null) {
            for (const kp of this.style.attrs.entries) {
                xml.writeAttributeString(kp.key, kp.value);
            }
        }
        if (this.text !== null) 
            xml.writeElementString("text", MiscHelper.correctXmlValue(this.text));
        if (!outStyleFull) {
            for (const ch of this.children) {
                ch.getXml(xml, null, false);
            }
        }
        xml.writeEndElement();
    }
    
    fromXml(xml) {
        for (const a of xml.attributes) {
            if (a.local_name === "typ") {
                try {
                    this.typ = UnitextStyledFragmentType.of(a.value);
                } catch (ex632) {
                }
            }
            else if (a.local_name === "style") 
                this.styleId = Utils.parseInt(a.value);
            else if (a.local_name === "b") 
                this.beginChar = Utils.parseInt(a.value);
            else if (a.local_name === "e") 
                this.endChar = Utils.parseInt(a.value);
        }
        for (const x of xml.child_nodes) {
            if (x.local_name === "stylefrag") {
                let fr = new UnitextStyledFragment();
                fr.parent = this;
                fr.fromXml(x);
                this.children.push(fr);
            }
            else if (x.local_name === "text") 
                this.text = x.inner_text;
        }
    }
    
    /**
     * Найти самый мелкий в дереве фрагмент (удалённный от корня), 
     * содержащий указанную позицию плоского текста.
     * @param cp позиция символа плоского текста
     * @return фрагмент или null
     */
    findByCharPosition(cp) {
        if ((cp < this.beginChar) || cp > this.endChar) 
            return null;
        if (this.children.length > 10) {
            let i = Utils.intDiv(this.children.length, 2);
            let d = Utils.intDiv(this.children.length, 4);
            if (d === 0) 
                d = 1;
            let k = d + 2;
            while (k > 0) {
                if (i >= this.children.length || (i < 0)) 
                    break;
                let ch = this.children[i];
                if (ch.beginChar <= cp && cp <= ch.endChar) {
                    let res = ch.findByCharPosition(cp);
                    if (res !== null) 
                        return res;
                    return this;
                }
                if (ch.beginChar < cp) 
                    i += d;
                else if (ch.endChar > cp) 
                    i -= d;
                else 
                    i += d;
                d = Utils.intDiv(d, 2);
                if (d === 0) {
                    d = 1;
                    k--;
                }
            }
        }
        for (const ch of this.children) {
            if (ch.beginChar <= cp && cp <= ch.endChar) {
                let res = ch.findByCharPosition(cp);
                if (res !== null) 
                    return res;
                return this;
            }
        }
        return this;
    }
    
    static _new448(_arg1, _arg2) {
        let res = new UnitextStyledFragment();
        res.typ = _arg1;
        res.parent = _arg2;
        return res;
    }
    
    static _new505(_arg1, _arg2) {
        let res = new UnitextStyledFragment();
        res.parent = _arg1;
        res.typ = _arg2;
        return res;
    }
    
    static _new518(_arg1, _arg2, _arg3) {
        let res = new UnitextStyledFragment();
        res.typ = _arg1;
        res.style = _arg2;
        res.parent = _arg3;
        return res;
    }
}


module.exports = UnitextStyledFragment