# 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

import math
import typing
from pullenti.unisharp.Utils import Utils
from pullenti.unisharp.Misc import RefOutArgWrapper

from pullenti.unitext.UnitextNewline import UnitextNewline
from pullenti.unitext.UnitextTable import UnitextTable
from pullenti.unitext.FileFormat import FileFormat
from pullenti.unitext.UnitextPagebreak import UnitextPagebreak
from pullenti.unitext.UnitextDocument import UnitextDocument
from pullenti.unitext.internal.misc.BorderInfo import BorderInfo
from pullenti.unitext.internal.word.DocTextStyles import DocTextStyles
from pullenti.unitext.internal.misc.MyXmlNodeType import MyXmlNodeType
from pullenti.unitext.UnitextItem import UnitextItem
from pullenti.unitext.UnitextPlaintext import UnitextPlaintext
from pullenti.unitext.UnitextTablecell import UnitextTablecell
from pullenti.unitext.UnitextContainer import UnitextContainer

class ExcelHelper:
    
    @staticmethod
    def __get_col_num(v : str) -> int:
        if (v is not None and len(v) >= 2): 
            num = ((ord(v[0])) - (ord('A')))
            if (len(v) > 2 and str.isalpha(v[1])): 
                num = ((((num + 1)) * 26) + (((ord(v[1])) - (ord('A')))))
            return num
        return -1
    
    @staticmethod
    def __get_row_num(v : str) -> int:
        if (v is not None and len(v) >= 2): 
            ii = 1
            if (len(v) > 2 and str.isalpha(v[1])): 
                ii = 2
            num = 0
            wrapnum71 = RefOutArgWrapper(0)
            inoutres72 = Utils.tryParseInt(v[ii:], wrapnum71)
            num = wrapnum71.value
            if (inoutres72): 
                return num
        return -1
    
    @staticmethod
    def __get_col_mnem(n : int) -> str:
        if (n < 0): 
            return "?"
        n0 = math.floor(n / 26)
        n1 = n % 26
        if (n0 == 0): 
            return "{0}".format(chr((n1 + (ord('A')))))
        return "{0}{1}".format(chr(((n0 - 1) + (ord('A')))), chr((n1 + (ord('A')))))
    
    @staticmethod
    def __get_rows(xml0_ : 'MyXmlReader', shared_strings : typing.List['UnitextItem'], hidden_cols : typing.List[int], styles : 'DocTextStyles', max_rows : int, rnum0 : int) -> typing.List[typing.List['UnitextTablecell']]:
        rnum0.value = 0
        rows = list()
        cols = 0
        all_cols = 0
        while xml0_.read():
            if (xml0_.node_type != MyXmlNodeType.ELEMENT): 
                continue
            if (xml0_.local_name == "col" or xml0_.local_name == "Column"): 
                if (xml0_.get_attribute("hidden") == "1"): 
                    hidden_cols.append(all_cols)
                all_cols += 1
                continue
            if (Utils.compareStrings(xml0_.local_name, "row", True) == 0): 
                rn = len(rows)
                nnn = 0
                wrapnnn84 = RefOutArgWrapper(0)
                inoutres85 = Utils.tryParseInt(Utils.ifNotNull(xml0_.get_attribute("r"), ""), wrapnnn84)
                nnn = wrapnnn84.value
                if (inoutres85): 
                    rn = (nnn - 1)
                    if (rnum0.value == 0): 
                        rnum0.value = nnn
                while len(rows) <= rn:
                    rows.append(list())
                row = rows[rn]
                if (xml0_.is_empty_element): 
                    continue
                while xml0_.read():
                    if (xml0_.node_type == MyXmlNodeType.ENDELEMENT and Utils.compareStrings(xml0_.local_name, "row", True) == 0): 
                        break
                    if (xml0_.node_type == MyXmlNodeType.ELEMENT and ((xml0_.local_name == "c" or xml0_.local_name == "Cell"))): 
                        num = -1
                        is_ext = False
                        inline_str = False
                        val = None
                        v = xml0_.get_attribute("r")
                        if (v is not None and len(v) >= 2): 
                            num = ExcelHelper.__get_col_num(v)
                        else: 
                            v = xml0_.get_attribute("ss:Index")
                            if ((v) is not None): 
                                wrapnum73 = RefOutArgWrapper(0)
                                inoutres74 = Utils.tryParseInt(v, wrapnum73)
                                num = wrapnum73.value
                                if (inoutres74): 
                                    num -= 1
                        v = xml0_.get_attribute("t")
                        if (v == "s"): 
                            is_ext = True
                        elif (v == "inlineStr"): 
                            inline_str = True
                        style = None
                        v = xml0_.get_attribute("s")
                        if ((v) is not None): 
                            if (styles is not None): 
                                style = styles.get_style_by_id(v)
                        merge_across = -1
                        merge_down = -1
                        v = xml0_.get_attribute("ss:MergeAcross")
                        if ((v) is not None): 
                            wrapmerge_across75 = RefOutArgWrapper(0)
                            Utils.tryParseInt(v, wrapmerge_across75)
                            merge_across = wrapmerge_across75.value
                        v = xml0_.get_attribute("ss:MergeDown")
                        if ((v) is not None): 
                            wrapmerge_down76 = RefOutArgWrapper(0)
                            Utils.tryParseInt(v, wrapmerge_down76)
                            merge_down = wrapmerge_down76.value
                        if (not xml0_.is_empty_element): 
                            while xml0_.read():
                                if (xml0_.node_type == MyXmlNodeType.ELEMENT and xml0_.local_name == "v"): 
                                    if (xml0_.is_empty_element): 
                                        break
                                    if (not xml0_.read()): 
                                        break
                                    if (xml0_.node_type == MyXmlNodeType.TEXT): 
                                        if (not is_ext): 
                                            val = (UnitextPlaintext._new57(xml0_.value))
                                            continue
                                        nu = 0
                                        wrapnu79 = RefOutArgWrapper(0)
                                        inoutres80 = Utils.tryParseInt(xml0_.value, wrapnu79)
                                        nu = wrapnu79.value
                                        if (inoutres80): 
                                            if (nu >= 0 and (nu < len(shared_strings))): 
                                                ss = shared_strings[nu]
                                                if (isinstance(ss, UnitextPlaintext)): 
                                                    val = (UnitextPlaintext._new57(ss.text))
                                                elif (ss is not None): 
                                                    val = ss.clone()
                                elif (xml0_.node_type == MyXmlNodeType.ELEMENT and xml0_.local_name == "Data"): 
                                    if (not xml0_.is_empty_element): 
                                        xml0_.read()
                                        try: 
                                            if (xml0_.node_type == MyXmlNodeType.TEXT): 
                                                val = (UnitextPlaintext._new57(xml0_.value))
                                        except Exception as eee: 
                                            break
                                elif (xml0_.node_type == MyXmlNodeType.ELEMENT and xml0_.local_name == "is"): 
                                    xml0_.read()
                                    if (xml0_.node_type == MyXmlNodeType.ELEMENT and xml0_.local_name == "t" and not xml0_.is_empty_element): 
                                        xml0_.read()
                                        if (xml0_.node_type == MyXmlNodeType.TEXT): 
                                            val = (UnitextPlaintext._new57(xml0_.value))
                                if (xml0_.node_type == MyXmlNodeType.ENDELEMENT and ((xml0_.local_name == "c" or xml0_.local_name == "Cell"))): 
                                    break
                        if (num < 0): 
                            num = len(row)
                        if ((num + 1) > cols): 
                            cols = (num + 1)
                        while len(row) <= num:
                            row.append(None)
                        row[num] = UnitextTablecell._new83(num, num, rn, rn, (None if style is None else style.xls_border))
                        if (val is not None): 
                            if (style is not None and style.xls_format is not None): 
                                pt = Utils.asObjectOrNull(val, UnitextPlaintext)
                                if (pt is None and (isinstance(val, UnitextContainer)) and len(val.children) == 1): 
                                    pt = (Utils.asObjectOrNull((val.children[0]), UnitextPlaintext))
                                if (pt is not None): 
                                    pt.text = style.correct_xls_text(pt.text)
                            row[num].content = val
                        if (merge_across > 0): 
                            row[num].col_end = row[num].col_end + merge_across
                        if (merge_down > 0): 
                            row[num].row_end = row[num].row_end + merge_down
            elif (xml0_.local_name == "mergeCell"): 
                val = xml0_.get_attribute("ref")
                if (val is None): 
                    continue
                ii = val.find(':')
                if (ii < 2): 
                    continue
                v1 = val[0:0+ii]
                v2 = val[ii + 1:]
                if (len(v2) < 2): 
                    continue
                col1 = ExcelHelper.__get_col_num(v1)
                col2 = ExcelHelper.__get_col_num(v2)
                if (((col1 < 0) or (col2 < 0) or col1 >= cols) or col2 >= cols): 
                    continue
                row1 = ExcelHelper.__get_row_num(v1)
                row2 = ExcelHelper.__get_row_num(v2)
                row1 -= 1
                row2 -= 1
                if (((row1 < 0) or (row2 < 0) or row1 >= len(rows)) or row2 >= len(rows)): 
                    continue
                if (col2 > col1): 
                    if ((col1 < len(rows[row1])) and rows[row1][col1] is not None): 
                        rows[row1][col1].col_end = col2
                    jj = row1
                    while jj <= row2: 
                        ii = (col1 + 1)
                        while ii <= col2 and (ii < len(rows[jj])): 
                            rows[jj][ii] = (None)
                            ii += 1
                        jj += 1
                if (row2 > row1): 
                    if ((col1 < len(rows[row1])) and rows[row1][col1] is not None): 
                        rows[row1][col1].row_end = row2
                    jj = row1 + 1
                    while jj <= row2: 
                        kk = col1
                        while kk <= col2: 
                            if (kk < len(rows[jj])): 
                                rows[jj][kk] = (None)
                            kk += 1
                        jj += 1
        return rows
    
    @staticmethod
    def _read_sheet(xml0_ : 'MyXmlReader', shared_strings : typing.List['UnitextItem'], sheet_name : str, styles : 'DocTextStyles', max_rows : int) -> 'UnitextItem':
        hidden_cols = list()
        rnum0 = 0
        wraprnum086 = RefOutArgWrapper(0)
        rows = ExcelHelper.__get_rows(xml0_, shared_strings, hidden_cols, styles, max_rows, wraprnum086)
        rnum0 = wraprnum086.value
        if (max_rows > 0 and len(rows) > max_rows): 
            del rows[max_rows:max_rows+len(rows) - max_rows]
        hidden_cols.clear()
        return ExcelHelper.create_table(rows, hidden_cols, sheet_name, rnum0)
    
    @staticmethod
    def create_table(rows : typing.List[typing.List['UnitextTablecell']], hidden_cols : typing.List[int], sheet_name : str, rnum0 : int) -> 'UnitextItem':
        cnt = UnitextContainer()
        tab = None
        rnu = 0
        rnu0 = 0
        rempty_max = 0
        all_rows_no_border = True
        rn = 0
        while rn < len(rows): 
            row = list()
            cn = 0
            first_pass694 = True
            while True:
                if first_pass694: first_pass694 = False
                else: cn += 1
                if (not (cn < len(rows[rn]))): break
                if (rows[rn][cn] is not None): 
                    if (cn in hidden_cols): 
                        continue
                    c = rows[rn][cn]
                    row.append(c)
                    if (isinstance(c.tag, BorderInfo)): 
                        if (not c.tag.is_empty): 
                            all_rows_no_border = False
                            break
            if (not all_rows_no_border): 
                break
            rn += 1
        rn = 0
        first_pass695 = True
        while True:
            if first_pass695: first_pass695 = False
            else: rn += 1
            if (not (rn < len(rows))): break
            row = list()
            has_border = False
            has_data = 0
            cn = 0
            first_pass696 = True
            while True:
                if first_pass696: first_pass696 = False
                else: cn += 1
                if (not (cn < len(rows[rn]))): break
                if (rows[rn][cn] is not None): 
                    if (cn in hidden_cols): 
                        continue
                    c = rows[rn][cn]
                    row.append(c)
                    if (isinstance(c.tag, BorderInfo)): 
                        if (not c.tag.is_empty): 
                            has_border = True
                    if (c.content is not None): 
                        has_data += 1
            if (has_data == 0): 
                if (rn <= rempty_max): 
                    continue
                if (not has_border or tab is None): 
                    tab = (None)
                    if (len(cnt.children) > 0 and (isinstance(cnt.children[len(cnt.children) - 1], UnitextNewline))): 
                        cnt.children[len(cnt.children) - 1].count += 1
                    else: 
                        cnt.children.append(UnitextNewline())
                    continue
                continue
            if (has_data == len(rows[rn]) and has_data > 2): 
                pass
            elif (not has_border and not all_rows_no_border): 
                if ((len(row) < 3) or tab is None): 
                    tab = (None)
                    fi = True
                    cn = 0
                    while cn < len(row): 
                        if (row[cn] is not None and row[cn].content is not None and not cn in hidden_cols): 
                            if (fi): 
                                fi = False
                            else: 
                                cnt.children.append(UnitextPlaintext._new57(" "))
                            cnt.children.append(row[cn].content)
                            row[cn].content.source_info = "XLS_CELL:{0}{1}".format(cn, rn + rnu0 + rnum0)
                        cn += 1
                    if (len(cnt.children) > 0 and (isinstance(cnt.children[len(cnt.children) - 1], UnitextNewline))): 
                        cnt.children[len(cnt.children) - 1].count += 1
                    else: 
                        cnt.children.append(UnitextNewline())
                    continue
            if (tab is None): 
                cnt.children.append(UnitextNewline())
                tab = UnitextTable()
                cnt.children.append(tab)
                rnu = 0
                rnu0 = rn
                rempty_max = rn
                tab.source_info = "XLS_SHEET:{0}".format(sheet_name)
            cn = 0
            while cn < len(row): 
                if (row[cn] is not None and not cn in hidden_cols): 
                    rr = row[cn]
                    if (rr.row_end > rempty_max): 
                        rempty_max = rr.row_end
                    rr.row_begin = rr.row_begin - rnu0
                    rr.row_end = rr.row_end - rnu0
                    cc = tab.add_cell(rr.row_begin, rr.row_end, rr.col_begin, rr.col_end, rr.content, False)
                    if (cc is not None and cc.source_info is None): 
                        cc.source_info = "XLS_CELL:{0}{1}".format(ExcelHelper.__get_col_mnem(rr.col_begin), rr.row_begin + rnu0 + rnum0)
                        if (rr.col_end > rr.col_begin or rr.row_end > rr.row_begin): 
                            cc.source_info = "{0}-{1}{2}".format(cc.source_info, ExcelHelper.__get_col_mnem(rr.col_end), rr.row_end + rnu0 + rnum0)
                        if (rr.content is not None): 
                            rr.content.source_info = cc.source_info
                cn += 1
            rnu += 1
        return cnt.optimize(True, None)
    
    @staticmethod
    def _create_doc_for_xml(xml0_ : 'MyXmlReader', max_rows : int) -> 'UnitextDocument':
        res = UnitextDocument._new47(FileFormat.XLSX)
        cnt = UnitextContainer()
        borders = dict()
        styles = DocTextStyles()
        sheet_name = None
        while xml0_.read():
            if (xml0_.node_type != MyXmlNodeType.ELEMENT): 
                continue
            if (xml0_.local_name == "Worksheet"): 
                if (len(cnt.children) > 0): 
                    cnt.children.append(UnitextPagebreak())
                if (len(xml0_.attributes) > 0): 
                    for a in xml0_.attributes.items(): 
                        name = a[1]
                        sheet_name = name
                        cnt.children.append(UnitextPlaintext._new89(name, "SheetName"))
                        cnt.children.append(UnitextNewline())
                        break
                continue
            if (xml0_.local_name == "Table"): 
                tab = ExcelHelper._read_sheet(xml0_, None, sheet_name, styles, max_rows)
                if (tab is not None): 
                    cnt.children.append(tab)
        res.content = (cnt)
        res.optimize(False, None)
        return res