/*
 * 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 UnitextImage = require("./../../UnitextImage");
const UnitextPlaintextType = require("./../../UnitextPlaintextType");
const UnitextNewline = require("./../../UnitextNewline");
const UnitextPagebreak = require("./../../UnitextPagebreak");
const FileFormat = require("./../../FileFormat");
const MiscHelper = require("./../../../util/MiscHelper");
const UnitextItem = require("./../../UnitextItem");
const UnitextContainer = require("./../../UnitextContainer");
const UnitextPlaintext = require("./../../UnitextPlaintext");
const UnitextPagesection = require("./../../UnitextPagesection");

class UnilayoutHelper {
    
    static correctPdfSpacings(pages) {
        let sp0 = 0;
        let sp1 = 0;
        for (const p of pages) {
            for (const r of p.rects) {
                if (r.text !== null) {
                    for (let i = 0; i < (r.text.length - 1); i++) {
                        let ch1 = r.text[i];
                        let ch2 = r.text[i + 1];
                        if (!Utils.isWhitespace(ch1)) {
                            if (!Utils.isWhitespace(ch2)) 
                                sp0++;
                            else if (((i + 2) < r.text.length) && !Utils.isWhitespace(r.text[i + 2])) 
                                sp1++;
                        }
                    }
                }
            }
        }
        if (sp0 > (Utils.intDiv(sp1, 100))) 
            return;
        let tmp = new StringBuilder();
        for (const p of pages) {
            for (const r of p.rects) {
                if (r.text !== null) {
                    tmp.length = 0;
                    for (let i = 0; i < r.text.length; i++) {
                        let ch1 = r.text[i];
                        if (Utils.isWhitespace(ch1)) 
                            continue;
                        tmp.append(ch1);
                        let j = 0;
                        let k = 0;
                        for (j = i + 1; j < r.text.length; j++) {
                            if (!Utils.isWhitespace(r.text[j])) 
                                break;
                            else 
                                k++;
                        }
                        k = Utils.intDiv(k, 2);
                        for (; k > 0; k--) {
                            tmp.append(' ');
                        }
                    }
                    r.text = tmp.toString();
                }
            }
        }
    }
    
    static correctPdfEncoding(pages) {
        let tot = 0;
        let err = 0;
        let lat = 0;
        let rus = 0;
        for (const p of pages) {
            for (const r of p.rects) {
                if (r.text !== null) {
                    for (const ch of r.text) {
                        if (Utils.isLetter(ch)) {
                            tot++;
                            let cod = ch.charCodeAt(0);
                            if (cod < 0x80) 
                                lat++;
                            else if (cod < 0x100) 
                                err++;
                            else if (cod >= 0x400 && (cod < 0x500)) 
                                rus++;
                        }
                    }
                }
            }
        }
        if (err > 10 && err > (Utils.intDiv(tot, 2)) && (rus < (Utils.intDiv(err, 2)))) {
        }
        else 
            return;
        let tmp = new StringBuilder();
        let b1 = new Uint8Array(1);
        for (const p of pages) {
            for (const r of p.rects) {
                if (r.text !== null) {
                    tmp.length = 0;
                    for (const ch of r.text) {
                        let cod = ch.charCodeAt(0);
                        let resCh = ch;
                        if (cod > 0x80 && (cod < 0x100)) {
                            b1[0] = cod;
                            let s = MiscHelper.decodeString1251(b1, 0, -1);
                            if ((s !== null && s.length === 1 && (s.charCodeAt(0)) >= 0x400) && ((s.charCodeAt(0)) < 0x500)) {
                                resCh = s[0];
                                if (((resCh >= 'А' && resCh <= 'Я')) || ((resCh >= 'а' && resCh <= 'я'))) {
                                }
                                else if (resCh === 'њ') 
                                    resCh = 'Ё';
                                else if (cod === 188) 
                                    resCh = 'ё';
                                else if (cod === 190) 
                                    resCh = '«';
                                else if (cod === 191) 
                                    resCh = '»';
                                else {
                                }
                            }
                            else {
                            }
                        }
                        tmp.append(resCh);
                    }
                    r.text = tmp.toString();
                }
            }
        }
    }
    
    static _cmFromPt(pt) {
        let v = pt;
        v = (v * 2.54) / (72);
        return Utils.mathRound(v, 1);
    }
    
    static createContentFromPages(doc, afterOcr) {
        if (doc.pages.length === 0) 
            return;
        if (doc.sourceFormat === FileFormat.PDF && !afterOcr) 
            UnilayoutHelper.correctPdfEncoding(doc.pages);
        let cnt = new UnitextContainer();
        doc.content = cnt;
        let plines = new Array();
        for (const p of doc.pages) {
            plines.push(UnilayoutHelper.correctPageLines(p));
        }
        let headTitle = new Array();
        let header = null;
        let footer = null;
        if (doc.pages.length > 1) {
            while (plines[0].length > 0) {
                if (headTitle.length > 0) {
                    if (plines[0][0].Y > (headTitle[0].Y + headTitle[0].height)) 
                        break;
                }
                let pn = 0;
                let eq = 0;
                for (pn = 1; pn < plines.length; pn++) {
                    if (plines[pn].length > 0) {
                        if (plines[pn][0].canBeEqualsExceptPageNumber(plines[0][0])) 
                            eq++;
                        else 
                            break;
                    }
                }
                if (eq > 0 && pn >= plines.length) {
                    headTitle.push(plines[0][0]);
                    for (const pp of plines) {
                        if (pp.length > 0) 
                            pp.splice(0, 1);
                    }
                    continue;
                }
                break;
            }
            if (headTitle.length > 0) {
                let fcnt = new UnitextContainer();
                for (const ht of headTitle) {
                    for (const b of ht.boxes) {
                        b.ignored = true;
                        if (b.text !== null) 
                            fcnt.children.push(UnitextPlaintext._new57(b.text));
                        else if (b.imageContent !== null) 
                            fcnt.children.push(UnitextImage._new277(b.imageContent));
                    }
                }
                let ttt = fcnt.getPlaintextString(null);
                if (ttt !== null && ttt.length > 5) 
                    header = fcnt;
            }
        }
        let cur = new UnitextPagesection();
        cur.width = UnilayoutHelper._cmFromPt(doc.pages[0].width);
        cur.height = UnilayoutHelper._cmFromPt(doc.pages[0].height);
        doc.sections.push(cur);
        for (let pn = 0; pn < doc.pages.length; pn++) {
            let p = doc.pages[pn];
            if (pn > 0) 
                cnt.children.push(UnitextPagebreak._new350(cur.id, pn));
            let pw = UnilayoutHelper._cmFromPt(p.width);
            let ph = UnilayoutHelper._cmFromPt(p.height);
            if (Math.abs(pw - cur.width) > 1 || Math.abs(ph - cur.height) > 1) {
                cur = new UnitextPagesection();
                doc.sections.push(cur);
                cur.id = ("ps" + doc.sections.length);
                cur.width = pw;
                cur.height = ph;
            }
            let lines = plines[pn];
            if (lines === null) 
                continue;
            let charWidth = 0;
            let cou = 0;
            for (const l of lines) {
                for (const r of l.boxes) {
                    if (r.text !== null) {
                        cou += r.text.length;
                        charWidth += (r.right - r.left);
                    }
                }
            }
            if (cou > 0) 
                charWidth /= (cou);
            for (let i = 0; i < lines.length; i++) {
                if (i > 0) {
                    cou = 1;
                    let h1 = lines[i - 1].height;
                    let h2 = lines[i].height;
                    let y1 = lines[i - 1].Y;
                    let y2 = lines[i].Y;
                    if (((h1 < (h2 * (0.75)))) || ((h1 * (0.75)) >= h2)) 
                        cou = 2;
                    else if ((y1 + h1) <= y2 && h1 > 0) {
                        cou = Math.floor((((y2 - y1)) / h1));
                        if (cou < 1) 
                            cou = 1;
                        else if (cou > 5) 
                            cou = 5;
                    }
                    else if (y1 > y2) 
                        cou = 3;
                    cnt.children.push(UnitextNewline._new351(cou, cur.id));
                }
                let l = lines[i];
                for (const b of l.boxes) {
                    b.tag = null;
                }
                if (i === 13) {
                }
                for (let j = 0; j < l.boxes.length; j++) {
                    let r = l.boxes[j];
                    r.lineNumber = j + 1;
                    if (j > 0) {
                        let d = r.left - l.boxes[j - 1].right;
                        if (d > (charWidth / (3))) {
                            let sps = (charWidth > 0 ? Math.floor((d / charWidth)) : 1);
                            if (sps < 1) 
                                sps = 1;
                            let str = ' '.repeat(sps);
                            cnt.children.push(UnitextPlaintext._new352(str, cur.id));
                        }
                    }
                    if (r.text !== null) {
                        let txt = UnitextPlaintext._new352(r.text, cur.id);
                        txt.layout = Array(r.text.length).fill(null);
                        for (let k = 0; k < txt.layout.length; k++) {
                            txt.layout[k] = r;
                        }
                        cnt.children.push(txt);
                        if (j > 0 && UnilayoutHelper._hasLetterOrDigit(txt.text)) {
                            let r0 = l.boxes[j - 1];
                            if (r0.tag === null && (r0.right + (3)) >= r.left) {
                                let prev = (cnt.children.length > 1 ? Utils.as(cnt.children[cnt.children.length - 2], UnitextPlaintext) : null);
                                if (prev !== null && UnilayoutHelper._hasLetterOrDigit(prev.text)) {
                                    if (r.bottom > ((((r0.bottom + r0.top)) / (2))) && r.bottom > r0.top && (r.top < r0.top)) {
                                        if ((prev.text.length < 3) && txt.text.length > 4) 
                                            prev.typ = UnitextPlaintextType.SUB;
                                        else if (txt.text.length < 5) {
                                            r.tag = txt;
                                            txt.typ = UnitextPlaintextType.SUP;
                                        }
                                    }
                                    else if ((r.top < ((((r0.bottom + r0.top)) / (2)))) && (r.top < r0.bottom)) {
                                        if (r.bottom > ((r0.bottom + (((r.bottom - r0.top)) / (6))))) {
                                            if (prev !== null && (prev.text.length < 3) && txt.text.length > 4) 
                                                prev.typ = UnitextPlaintextType.SUP;
                                            else if (txt.text.length < 5) {
                                                r.tag = txt;
                                                txt.typ = UnitextPlaintextType.SUB;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else if (r.imageContent !== null) {
                        let img = UnitextImage._new354(r.imageContent, r, cur.id);
                        img.width = ((String(Math.floor((r.right - r.left)))) + "pt");
                        img.height = ((String(Math.floor((r.bottom - r.top)))) + "pt");
                        cnt.children.push(img);
                    }
                }
            }
            if (p.imageContent !== null) {
                let img = UnitextImage._new355(cur.id);
                img.content = p.imageContent;
                if (p.width > 0) 
                    img.width = ((String(p.width)) + "pt");
                if (p.height > 0) 
                    img.height = ((String(p.height)) + "pt");
                cnt.children.push(img);
            }
        }
    }
    
    static _hasLetterOrDigit(txt) {
        if (txt !== null) {
            for (const ch of txt) {
                if ((Utils.isLetterOrDigit(ch) || ch === '*' || ch === '<') || ch === '>') 
                    return true;
            }
        }
        return false;
    }
    
    static correctPageLines(p) {
        let lines = new Array();
        for (const r of p.rects) {
            if (lines.length > 0 && lines[lines.length - 1].tryAppend(r)) {
            }
            else {
                let l = new UnilayoutHelper.LayLine();
                l.boxes.push(r);
                lines.push(l);
                if (lines.length === 14) {
                }
            }
        }
        if (lines.length < 1) 
            return lines;
        let height = 0;
        let x = 10000000;
        for (const l of lines) {
            height += l.height;
            if (l.X < x) 
                x = l.X;
        }
        height /= (lines.length);
        let errs = 0;
        let max = Utils.intDiv(lines.length * ((lines.length - 1)), 2);
        for (let i = 0; i < (lines.length - 1); i++) {
            for (let j = i + 1; j < lines.length; j++) {
                if (lines[i].Y > (lines[j].Y + height)) 
                    errs++;
            }
        }
        if (errs > (Utils.intDiv(max, 2))) {
            for (let ii = 0; ii < lines.length; ii++) {
                let ch = false;
                for (let jj = 0; jj < (lines.length - 1); jj++) {
                    if (lines[jj].compareTo(lines[jj + 1]) > 0) {
                        let v = lines[jj];
                        lines[jj] = lines[jj + 1];
                        lines[jj + 1] = v;
                        ch = true;
                    }
                }
                if (!ch) 
                    break;
            }
        }
        return lines;
    }
}


UnilayoutHelper.LayLine = class  {
    
    constructor() {
        this.boxes = new Array();
    }
    
    canBeEqualsExceptPageNumber(ll) {
        if (ll.boxes.length !== this.boxes.length) 
            return false;
        for (let i = 0; i < ll.boxes.length; i++) {
            let b0 = this.boxes[i];
            let b1 = ll.boxes[i];
            if (b0.text === null && b1.text === null) {
                if (b0.imageContent !== null && b1.imageContent !== null) {
                    if (b0.imageContent.length !== b1.imageContent.length) 
                        return false;
                    for (let j = 0; j < b0.imageContent.length; j++) {
                        if (b0.imageContent[j] !== b1.imageContent[j]) 
                            return false;
                    }
                }
                else 
                    return false;
                continue;
            }
            if (b0.text === null || b1.text === null) 
                return false;
            if (b0.text === b1.text) 
                continue;
            let i0 = 0;
            let i1 = 0;
            while ((i0 < b0.text.length) && (i1 < b1.text.length)) {
                let ch0 = b0.text[i0];
                let ch1 = b1.text[i1];
                if (ch0 === ch1) {
                    i0++;
                    i1++;
                    continue;
                }
                let oo = false;
                while ((i0 < b0.text.length) && Utils.isDigit(b0.text[i0])) {
                    i0++;
                    oo = true;
                }
                while ((i1 < b1.text.length) && Utils.isDigit(b1.text[i1])) {
                    i1++;
                    oo = true;
                }
                if (!oo) 
                    return false;
            }
            if ((i0 < b0.text.length) || (i1 < b1.text.length)) 
                return false;
        }
        return true;
    }
    
    toString() {
        let txt = new StringBuilder();
        for (const b of this.boxes) {
            if (b.text !== null) {
                if (txt.length > 0) 
                    txt.append(' ');
                txt.append(b.text);
            }
        }
        return (txt.toString() + " X=" + this.X + " X1=" + (this.X + this.width) + " Y=" + this.Y + " Y2=" + (this.Y + this.height));
    }
    
    get X() {
        return (this.boxes.length === 0 ? 0 : Math.floor(this.boxes[0].left));
    }
    
    get width() {
        if (this.boxes.length === 0) 
            return 0;
        return (this.boxes[this.boxes.length - 1].right - this.boxes[0].left);
    }
    
    get Y() {
        let y = 0;
        for (const w of this.boxes) {
            y += w.top;
        }
        if (this.boxes.length > 0) 
            y /= (this.boxes.length);
        return y;
    }
    
    get height() {
        let y = 0;
        let h = 0;
        for (const w of this.boxes) {
            y += w.top;
            h += w.bottom;
        }
        if (this.boxes.length > 0) {
            y /= (this.boxes.length);
            h /= (this.boxes.length);
            h -= y;
        }
        return h;
    }
    
    tryAppend(r) {
        if (this.boxes.length === 0) {
            this.boxes.push(r);
            return true;
        }
        if ((((r.top + r.bottom)) / (2)) < this.Y) 
            return false;
        if (r.top > ((this.Y + ((this.height / (2)))))) 
            return false;
        if (r.left >= ((this.X + this.width) - (2))) {
            this.boxes.push(r);
            return true;
        }
        for (let i = 0; i < this.boxes.length; i++) {
            if (r.right <= this.boxes[i].left) {
                this.boxes.splice(i, 0, r);
                return true;
            }
        }
        return false;
    }
    
    compareTo(obj) {
        let l = Utils.as(obj, UnilayoutHelper.LayLine);
        if (this.Y < l.Y) 
            return -1;
        if (this.Y > l.Y) 
            return 1;
        if (this.X < l.X) 
            return -1;
        if (this.X > l.X) 
            return 1;
        return 0;
    }
}


module.exports = UnilayoutHelper