/*
 * 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
 */

package com.pullenti.unitext.internal.html;

public class HtmlNode {

    public String tagName;

    public java.util.HashMap<String, String> attrs;

    public String text;

    public boolean whitespacePreserve;

    public boolean whitespaceBefore;

    public HtmlNode parent;

    public java.util.ArrayList<HtmlNode> children = new java.util.ArrayList<HtmlNode>();

    public int sourceHtmlPosition;

    public int sourceHtmlLength;

    public int sourceHtmlEndPosition;

    public Object misc;

    public int getTotalChildren() {
        int res = children.size();
        for (HtmlNode ch : children) {
            res += ch.getTotalChildren();
        }
        return res;
    }


    public void getHtml(StringBuilder res) {
        HtmlTag tag = HtmlTag.findTag(tagName);
        if (tag != null && ((tag.isBlock() || tag.isTable())) && res.length() > 0) 
            res.append("\r\n");
        else if (whitespaceBefore) 
            res.append(" ");
        if (tagName != null) {
            res.append("<").append(tagName.toLowerCase());
            if (attrs != null) {
                for (java.util.Map.Entry<String, String> kp : attrs.entrySet()) {
                    res.append(" ").append(kp.getKey().toLowerCase()).append("=\"");
                    com.pullenti.util.MiscHelper.correctHtmlValue(res, kp.getValue(), true, false, false);
                    res.append("\"");
                }
            }
            if (com.pullenti.unisharp.Utils.isNullOrEmpty(text) && children.size() == 0) {
                res.append("/>");
                return;
            }
            res.append(">");
        }
        if (children.size() > 0) {
            for (HtmlNode ch : children) {
                ch.getHtml(res);
            }
        }
        else if (!com.pullenti.unisharp.Utils.isNullOrEmpty(text)) 
            com.pullenti.util.MiscHelper.correctHtmlValue(res, text, false, false, false);
        if (tagName != null) 
            res.append("</").append(tagName.toLowerCase()).append(">");
    }

    public int plainTextBegin;

    public int plainTextEnd;

    public String getPlaintext(com.pullenti.unitext.GetPlaintextParam pars) {
        StringBuilder res = new StringBuilder();
        this._getFullText(res, pars, null);
        return res.toString();
    }

    public void _getFullText(StringBuilder res, com.pullenti.unitext.GetPlaintextParam pars, String liNumStr) {
        plainTextBegin = res.length();
        plainTextEnd = plainTextBegin - 1;
        if ((com.pullenti.unisharp.Utils.stringsEq(tagName, "SCRIPT") || com.pullenti.unisharp.Utils.stringsEq(tagName, "STYLE") || com.pullenti.unisharp.Utils.stringsEq(tagName, "META")) || com.pullenti.unisharp.Utils.stringsEq(tagName, "TITLE")) 
            return;
        HtmlTag tag = HtmlTag.findTag(tagName);
        boolean isBlock = false;
        if (tag != null && ((tag.isBlock() || tag.isTable()))) 
            isBlock = true;
        boolean isTab = tag != null && com.pullenti.unisharp.Utils.stringsEq(tag.name, "TABLE");
        boolean isTd = tag != null && ((com.pullenti.unisharp.Utils.stringsEq(tag.name, "TD") || com.pullenti.unisharp.Utils.stringsEq(tag.name, "TH")));
        boolean isTr = tag != null && com.pullenti.unisharp.Utils.stringsEq(tag.name, "TR");
        boolean isLi = tag != null && com.pullenti.unisharp.Utils.stringsEq(tag.name, "LI");
        boolean isOl = tag != null && com.pullenti.unisharp.Utils.stringsEq(tag.name, "OL");
        if (tag != null && com.pullenti.unisharp.Utils.stringsEq(tag.name, "BR")) {
            if (whitespacePreserve) {
                res.append(' ');
                plainTextEnd = res.length() - 1;
                return;
            }
            res.append((pars == null ? "\r\n" : pars.newLine));
            plainTextEnd = res.length() - 1;
            return;
        }
        if (isTab && pars != null) {
            if (pars.tableStart != null) 
                res.append(pars.tableStart);
        }
        if ((isBlock && !isTd && !isTr) && res.length() > 0) 
            res.append((pars == null ? "\r\n" : pars.newLine));
        if (isLi) {
            if (liNumStr != null) 
                res.append(liNumStr);
            else 
                res.append('');
        }
        int liNum = 0;
        String liTyp = null;
        if (isOl) {
            com.pullenti.unisharp.Outargwrapper<Integer> wrapliNum3 = new com.pullenti.unisharp.Outargwrapper<Integer>();
            boolean inoutres4 = com.pullenti.unisharp.Utils.parseInteger((String)com.pullenti.unisharp.Utils.notnull(this.getAttribute("START"), ""), 0, null, wrapliNum3);
            liNum = (wrapliNum3.value != null ? wrapliNum3.value : 0);
            if (!inoutres4) 
                liNum = 1;
            liTyp = (String)com.pullenti.unisharp.Utils.notnull(this.getAttribute("TYPE"), "1");
        }
        if (!com.pullenti.unisharp.Utils.isNullOrEmpty(text)) 
            res.append(text);
        else if (children.size() == 1) 
            ((HtmlNode)com.pullenti.unisharp.Utils.cast(children.get(0), HtmlNode.class))._getFullText(res, pars, _getLiNum(liNum, liTyp));
        else {
            boolean first = true;
            for (int i = 0; i < children.size(); i++) {
                HtmlNode ch = children.get(i);
                HtmlNode hn = (HtmlNode)com.pullenti.unisharp.Utils.cast(ch, HtmlNode.class);
                if (first) 
                    first = false;
                else if (!ch.whitespacePreserve && !isTr) {
                    if (ch.tagName == null || com.pullenti.unisharp.Utils.stringsCompare((ch.tagName != null ? ch.tagName : ""), "SPAN", true) == 0) {
                    }
                    else if (i > 0 && ((children.get(i - 1).tagName == null || com.pullenti.unisharp.Utils.stringsCompare((children.get(i - 1).tagName != null ? children.get(i - 1).tagName : ""), "SPAN", true) == 0))) {
                    }
                    else 
                        res.append(' ');
                }
                hn._getFullText(res, pars, _getLiNum(liNum, liTyp));
                if (liNum > 0 && com.pullenti.unisharp.Utils.stringsEq(hn.tagName, "LI")) 
                    liNum++;
            }
        }
        if (isTd) {
            if (this.getAttribute("ROWSPAN") != null) {
                int span;
                com.pullenti.unisharp.Outargwrapper<Integer> wrapspan5 = new com.pullenti.unisharp.Outargwrapper<Integer>();
                boolean inoutres6 = com.pullenti.unisharp.Utils.parseInteger(this.getAttribute("ROWSPAN"), 0, null, wrapspan5);
                span = (wrapspan5.value != null ? wrapspan5.value : 0);
                if (inoutres6) {
                    for (; span > 1; span--) {
                        res.append(pars.pageBreak);
                    }
                }
            }
            if (this.getAttribute("COLSPAN") != null) {
                int span;
                com.pullenti.unisharp.Outargwrapper<Integer> wrapspan7 = new com.pullenti.unisharp.Outargwrapper<Integer>();
                boolean inoutres8 = com.pullenti.unisharp.Utils.parseInteger(this.getAttribute("COLSPAN"), 0, null, wrapspan7);
                span = (wrapspan7.value != null ? wrapspan7.value : 0);
                if (inoutres8) {
                    for (; span > 1; span--) {
                        res.append(pars.tab);
                    }
                }
            }
            res.append(pars.tableCellEnd);
        }
        else if (isTr) 
            res.append((pars.tableRowEnd != null ? pars.tableRowEnd : pars.newLine));
        else if (isTab) {
        }
        if (isTab && pars != null) {
            if (pars.tableEnd != null) 
                res.append(pars.tableEnd);
        }
        plainTextEnd = res.length() - 1;
    }

    private static String _trimTabText(String txt) {
        if (com.pullenti.unisharp.Utils.isNullOrEmpty(txt)) 
            return "";
        int i;
        for (i = txt.length() - 1; i >= 0; i--) {
            if (txt.charAt(i) == ' ' || ((int)txt.charAt(i)) == 0xD || ((int)txt.charAt(i)) == 0xA) {
            }
            else 
                break;
        }
        if (i < 0) 
            return "";
        if (i < (txt.length() - 1)) 
            txt = txt.substring(0, 0 + i + 1);
        for (i = 0; i < txt.length(); i++) {
            if (txt.charAt(i) == ' ' || ((int)txt.charAt(i)) == 0xD || ((int)txt.charAt(i)) == 0xA) {
            }
            else 
                break;
        }
        if (i > 0) 
            txt = txt.substring(i);
        return txt;
    }

    public static String _getLiNum(int num, String typ) {
        if (num <= 0) 
            return null;
        char ty = (com.pullenti.unisharp.Utils.isNullOrEmpty(typ) ? '1' : typ.charAt(0));
        if (ty == '1') 
            return (((Integer)num).toString() + ". ");
        if (ty == 'A') 
            return ((String.valueOf((char)((((int)'A') + ((num - 1)))))) + ") ");
        if (ty == 'a') 
            return ((String.valueOf((char)((((int)'a') + ((num - 1)))))) + ") ");
        if (ty == 'i' && ((num - 1) < com.pullenti.unitext.internal.uni.UnitextHelper.m_Romans.length)) 
            return (com.pullenti.unitext.internal.uni.UnitextHelper.m_Romans[num - 1].toLowerCase() + ". ");
        if (ty == 'I' && ((num - 1) < com.pullenti.unitext.internal.uni.UnitextHelper.m_Romans.length)) 
            return (com.pullenti.unitext.internal.uni.UnitextHelper.m_Romans[num - 1] + ". ");
        return (((Integer)num).toString() + ". ");
    }

    public void correctRusTexts(int corr) {
        String attr = this.getAttribute("STYLE");
        if (attr != null) {
            if ((attr.indexOf("small-caps") >= 0)) 
                corr = 1;
        }
        for (HtmlNode ch : children) {
            ch.correctRusTexts(corr);
        }
        if (corr > 0 && !com.pullenti.unisharp.Utils.isNullOrEmpty(text)) 
            text = text.toUpperCase();
    }

    public String getAttribute(String name) {
        if (attrs == null) 
            return null;
        if (attrs.containsKey(name)) 
            return attrs.get(name);
        return null;
    }

    public String getStyleValue(String name) {
        if (attrs == null || name == null) 
            return null;
        String val;
        com.pullenti.unisharp.Outargwrapper<String> wrapval9 = new com.pullenti.unisharp.Outargwrapper<String>();
        boolean inoutres10 = com.pullenti.unisharp.Utils.tryGetValue(attrs, "STYLE", wrapval9);
        val = wrapval9.value;
        if (!inoutres10) 
            return null;
        for (String vv : com.pullenti.unisharp.Utils.split(val, String.valueOf(';'), false)) {
            String vvv = vv.trim();
            int ii = vvv.indexOf(':');
            if (ii < 0) 
                continue;
            if (com.pullenti.unisharp.Utils.stringsCompare(name, vvv.substring(0, 0 + ii).trim(), true) == 0) 
                return vv.substring(ii + 1).trim();
        }
        return null;
    }

    @Override
    public String toString() {
        if (tagName == null) {
            if (text == null) 
                return "";
            return text;
        }
        StringBuilder tmp = new StringBuilder();
        tmp.append("<").append(tagName);
        if (attrs != null) {
            for (java.util.Map.Entry<String, String> kp : attrs.entrySet()) {
                tmp.append(" ").append(kp.getKey()).append("='").append(((String)com.pullenti.unisharp.Utils.notnull(kp.getValue(), ""))).append("'");
            }
        }
        if (text != null) 
            tmp.append(">").append(text).append("</").append(tagName).append(">");
        else if (children.size() > 0) 
            tmp.append(">...</").append(tagName).append(">");
        else 
            tmp.append("/>");
        return tmp.toString();
    }

    public boolean getHasText() {
        if (text != null) {
            for (int i = 0; i < text.length(); i++) {
                if (!com.pullenti.unisharp.Utils.isWhitespace(text.charAt(i))) 
                    return true;
            }
        }
        for (int j = 0; j < children.size(); j++) {
            if (((HtmlNode)com.pullenti.unisharp.Utils.cast(children.get(j), HtmlNode.class)).getHasText()) 
                return true;
        }
        return false;
    }


    public boolean hasTag(String tag) {
        if (tagName != null && com.pullenti.unisharp.Utils.stringsCompare(tagName, tag, true) == 0) 
            return true;
        for (HtmlNode ch : children) {
            if (ch.hasTag(tag)) 
                return true;
        }
        return false;
    }

    public boolean hasBlockTag() {
        if (tagName != null) {
            HtmlTag ti = HtmlTag.findTag(tagName);
            if (ti != null && ti.isBlock()) 
                return true;
        }
        for (HtmlNode ch : children) {
            if (ch.hasBlockTag()) 
                return true;
        }
        return false;
    }

    public void remove() {
        if (parent == null) 
            return;
        int i = parent.children.indexOf(this);
        if (i < 0) 
            return;
        parent.children.remove(this);
        if (children.size() > 0) {
            com.pullenti.unisharp.Utils.insertToArrayList(parent.children, children, i);
            children.clear();
        }
        parent = null;
    }

    public HtmlNode findSubnode(String _tagName, String attrName, String attrValue) {
        if (attrName != null) 
            attrName = attrName.toUpperCase();
        if (_tagName != null) 
            _tagName = _tagName.toUpperCase();
        return this._FindSubnode(_tagName, attrName, attrValue);
    }

    private HtmlNode _FindSubnode(String _tagName, String attrName, String attrValue) {
        if (_tagName != null && com.pullenti.unisharp.Utils.stringsEq(tagName, _tagName)) {
            if (attrName != null) {
                String val = this.getAttribute(attrName);
                if (val != null) {
                    if (attrValue == null) 
                        return this;
                    if (com.pullenti.unisharp.Utils.stringsCompare(attrValue, val, true) == 0) 
                        return this;
                }
            }
            else 
                return this;
        }
        for (HtmlNode ch : children) {
            HtmlNode res = ch._FindSubnode(_tagName, attrName, attrValue);
            if (res != null) 
                return res;
        }
        return null;
    }

    public HtmlNode implantateNode(int beginChar, int endChar, String _tagName) {
        if (beginChar >= plainTextEnd || (endChar < plainTextBegin)) 
            return null;
        int ind = -1;
        if (parent != null) 
            ind = parent.children.indexOf(this);
        if (children.size() == 0) {
            if (com.pullenti.unisharp.Utils.isNullOrEmpty(text)) 
                return null;
            if (beginChar < plainTextBegin) 
                beginChar = plainTextBegin;
            if (endChar > plainTextEnd) 
                endChar = plainTextEnd;
            if (beginChar == plainTextBegin && endChar == plainTextEnd) {
                HtmlNode res = _new11(_tagName, beginChar, endChar);
                if (ind >= 0) {
                    com.pullenti.unisharp.Utils.putArrayValue(parent.children, ind, res);
                    res.parent = parent;
                    res.children.add(this);
                    this.parent = res;
                    return res;
                }
                else {
                    HtmlNode txt = _new12(beginChar, endChar);
                    txt.text = text;
                    text = null;
                    res.children.add(txt);
                    txt.parent = res;
                    children.add(res);
                    res.parent = this;
                    return res;
                }
            }
            HtmlNode res1 = _new11(_tagName, beginChar, endChar);
            if (beginChar > plainTextBegin) {
                int d = beginChar - plainTextBegin;
                HtmlNode txt = _new14(beginChar - 1);
                txt.plainTextBegin = plainTextBegin;
                txt.text = text.substring(0, 0 + d);
                children.add(txt);
                txt.parent = this;
                text = text.substring(d);
            }
            if (beginChar <= endChar) {
                HtmlNode txt = _new12(beginChar, endChar);
                txt.text = text;
                if (endChar < plainTextEnd) {
                    txt.text = text.substring(0, 0 + (endChar - beginChar) + 1);
                    text = text.substring((endChar - beginChar) + 1);
                }
                else 
                    text = "";
                children.add(res1);
                res1.parent = this;
                res1.children.add(txt);
                txt.parent = res1;
            }
            if (endChar < plainTextEnd) {
                HtmlNode txt = _new16(endChar + 1);
                txt.plainTextEnd = plainTextEnd;
                txt.text = text;
                children.add(txt);
                txt.parent = this;
                text = "";
            }
            text = null;
            return res1;
        }
        if (children.size() == 0) 
            return null;
        for (int i = 0; i < children.size(); i++) {
            if (children.get(i).plainTextBegin <= beginChar && beginChar <= children.get(i).plainTextEnd) {
                for (int j = i; j < children.size(); j++) {
                    String tag = children.get(j).tagName;
                    if ((com.pullenti.unisharp.Utils.stringsEq(tag, "TD") || com.pullenti.unisharp.Utils.stringsEq(tag, "TR") || com.pullenti.unisharp.Utils.stringsEq(tag, "TH")) || ((children.get(j).plainTextBegin <= endChar && endChar <= children.get(j).plainTextEnd))) {
                        if (i == j) {
                            HtmlNode res1 = children.get(i).implantateNode(beginChar, endChar, _tagName);
                            if (res1 != null) 
                                return res1;
                        }
                        HtmlNode head = null;
                        HtmlNode tail = null;
                        HtmlNode ch = children.get(i);
                        if (beginChar > ch.plainTextBegin && ch.text != null) {
                            head = _new11(ch.tagName, ch.plainTextBegin, beginChar - 1);
                            head.parent = parent;
                            head.text = ch.text.substring(0, 0 + beginChar - ch.plainTextBegin);
                            ch.text = ch.text.substring(beginChar - ch.plainTextBegin);
                            ch.plainTextBegin = beginChar;
                        }
                        if ((endChar < children.get(j).plainTextEnd) && children.get(j).text != null) {
                            HtmlNode ch1 = children.get(j);
                            tail = _new11(ch1.tagName, endChar + 1, ch1.plainTextEnd);
                            tail.parent = parent;
                            tail.text = ch1.text.substring(endChar - ch1.plainTextBegin);
                            ch1.text = ch1.text.substring(0, 0 + endChar - ch1.plainTextBegin);
                            ch1.plainTextEnd = endChar;
                        }
                        beginChar = ch.plainTextBegin;
                        endChar = children.get(j).plainTextEnd;
                        HtmlNode res = _new11(_tagName, beginChar, endChar);
                        if (((com.pullenti.unisharp.Utils.stringsEq(tag, "TD") || com.pullenti.unisharp.Utils.stringsEq(tag, "TR") || com.pullenti.unisharp.Utils.stringsEq(tag, "TH"))) && i == j) {
                            for (HtmlNode chh : ch.children) {
                                res.children.add(chh);
                                ch.parent = res;
                            }
                            children.get(i).children.clear();
                            children.get(i).children.add(res);
                            res.parent = children.get(i);
                            return res;
                        }
                        for (int k = i; k <= j; k++) {
                            res.children.add(children.get(k));
                            children.get(k).parent = res;
                        }
                        for(int jjj = i + (j + 1) - i - 1, mmm = i; jjj >= mmm; jjj--) children.remove(jjj);
                        if (tail != null) 
                            children.add(i, tail);
                        children.add(i, res);
                        res.parent = this;
                        if (head != null) 
                            children.add(i, head);
                        return res;
                    }
                }
            }
        }
        return null;
    }

    public HtmlNode replaceTextRange(int bCharPos, int eCharPos, String _text, String _tagName, java.util.HashMap<String, String> _attrs) {
        if (bCharPos > eCharPos) 
            return null;
        HtmlNode startNode = this.findTextNodeAt(bCharPos);
        HtmlNode endNode = this.findTextNodeAt(eCharPos);
        if (startNode == null || endNode == null) 
            return null;
        HtmlNode _parent = startNode.parent;
        if (_parent != endNode.parent) 
            return null;
        // пока не лезем в разные уровни
        int startIndex = _parent.children.indexOf(startNode);
        int endIndex = _parent.children.indexOf(endNode);
        // режем начало
        int startOffset = bCharPos - startNode.plainTextBegin;
        String leftText = startNode.text.substring(0, 0 + startOffset);
        // режем конец
        int endOffset = eCharPos - endNode.plainTextBegin;
        String rightText = endNode.text.substring(endOffset + 1);
        // удаляем все узлы между start и end
        for (int i = endIndex; i >= startIndex; i--) {
            _parent.children.remove(i);
        }
        int insertIndex = startIndex;
        // возвращаем left
        if (!com.pullenti.unisharp.Utils.isNullOrEmpty(leftText)) {
            _parent.children.add(insertIndex, _new20(leftText, _parent, startNode.plainTextBegin, bCharPos - 1));
            insertIndex++;
        }
        // вставляем новый тег
        HtmlNode inserted = _new21(_tagName, _parent, (_attrs != null ? new java.util.HashMap<String, String>(_attrs) : null));
        inserted.children.add(_new22(_text, inserted));
        _parent.children.add(insertIndex, inserted);
        insertIndex++;
        // возвращаем right
        if (!com.pullenti.unisharp.Utils.isNullOrEmpty(rightText)) 
            _parent.children.add(insertIndex, _new20(rightText, _parent, eCharPos + 1, endNode.plainTextEnd));
        return inserted;
    }

    public HtmlNode findTextNodeAt(int charPos) {
        return this.findTextNodeAtInternal(this, charPos);
    }

    private HtmlNode findTextNodeAtInternal(HtmlNode node, int charPos) {
        if (node.children.size() == 0) 
            return node;
        for (int i = 0; i < node.children.size(); i++) {
            if (node.children.get(i).plainTextBegin <= charPos && charPos <= node.children.get(i).plainTextEnd) {
                for (int j = i; j < node.children.size(); j++) {
                    String tag = node.children.get(j).tagName;
                    if (((com.pullenti.unisharp.Utils.stringsEq(tag, "TD") || com.pullenti.unisharp.Utils.stringsEq(tag, "TR") || com.pullenti.unisharp.Utils.stringsEq(tag, "TH"))) || (((node.children.get(j).plainTextBegin <= charPos && charPos <= node.children.get(j).plainTextEnd)))) {
                        if (i == j) {
                            HtmlNode res1 = this.findTextNodeAtInternal(node.children.get(j), charPos);
                            if (res1 != null) 
                                return res1;
                        }
                    }
                }
            }
        }
        return null;
    }

    public static HtmlNode _new1(String _arg1) {
        HtmlNode res = new HtmlNode();
        res.tagName = _arg1;
        return res;
    }

    public static HtmlNode _new11(String _arg1, int _arg2, int _arg3) {
        HtmlNode res = new HtmlNode();
        res.tagName = _arg1;
        res.plainTextBegin = _arg2;
        res.plainTextEnd = _arg3;
        return res;
    }

    public static HtmlNode _new12(int _arg1, int _arg2) {
        HtmlNode res = new HtmlNode();
        res.plainTextBegin = _arg1;
        res.plainTextEnd = _arg2;
        return res;
    }

    public static HtmlNode _new14(int _arg1) {
        HtmlNode res = new HtmlNode();
        res.plainTextEnd = _arg1;
        return res;
    }

    public static HtmlNode _new16(int _arg1) {
        HtmlNode res = new HtmlNode();
        res.plainTextBegin = _arg1;
        return res;
    }

    public static HtmlNode _new20(String _arg1, HtmlNode _arg2, int _arg3, int _arg4) {
        HtmlNode res = new HtmlNode();
        res.text = _arg1;
        res.parent = _arg2;
        res.plainTextBegin = _arg3;
        res.plainTextEnd = _arg4;
        return res;
    }

    public static HtmlNode _new21(String _arg1, HtmlNode _arg2, java.util.HashMap<String, String> _arg3) {
        HtmlNode res = new HtmlNode();
        res.tagName = _arg1;
        res.parent = _arg2;
        res.attrs = _arg3;
        return res;
    }

    public static HtmlNode _new22(String _arg1, HtmlNode _arg2) {
        HtmlNode res = new HtmlNode();
        res.text = _arg1;
        res.parent = _arg2;
        return res;
    }
    public HtmlNode() {
    }
}
