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

const ArchiveHelper = require("./../../../util/ArchiveHelper");
const PdfObject = require("./PdfObject");
const LzwDecoder = require("./../misc/LzwDecoder");
const MiscHelper = require("./../../../util/MiscHelper");
const PngWrapper = require("./../misc/PngWrapper");
const FileFormat = require("./../../FileFormat");
const PdfReference = require("./PdfReference");
const PdfIntValue = require("./PdfIntValue");
const FileFormatsHelper = require("./../../../util/FileFormatsHelper");
const UnitextImage = require("./../../UnitextImage");
const PdfBoolValue = require("./PdfBoolValue");
const PdfName = require("./PdfName");
const PdfStringValue = require("./PdfStringValue");
const PdfRealValue = require("./PdfRealValue");
const PdfArray = require("./PdfArray");

class PdfDictionary extends PdfObject {
    
    constructor() {
        super();
        this.m_Items = new Hashtable();
        this.m_StreamData = null;
        this.m_StreamIn = null;
        this.m_StreamPos = 0;
        this.m_StreamLength = 0;
        this.m_FilePos = 0;
        this.pageNum = 0;
        this.obj = null;
    }
    
    getData() {
        if (this.m_StreamData === null) {
            if (this.m_StreamLength === 0) 
                return null;
            this.m_StreamData = new Uint8Array(this.m_StreamLength);
            this.m_StreamIn.position = this.m_StreamPos;
            this.m_StreamIn.read(this.m_StreamData, 0, this.m_StreamLength);
        }
        return this.m_StreamData;
    }
    
    setData(dat) {
        this.m_StreamData = dat;
    }
    
    addObject(key, o) {
        if (!this.m_Items.containsKey(key)) {
            if (o !== null) 
                this.m_Items.put(key, o);
        }
        else if (o !== null) 
            this.m_Items.put(key, o);
        else 
            this.m_Items.remove(key);
    }
    
    get keys() {
        return Array.from(this.m_Items.keys);
    }
    
    isSimple(lev) {
        if (this.m_Items.length > 10 || lev > 10) 
            return false;
        for (const key of this.keys) {
            if (key === "Parent") 
                continue;
            let it = this.getObject(key, false);
            if (it !== null && !it.isSimple(lev + 1)) 
                return false;
        }
        return true;
    }
    
    toStringEx(lev) {
        if (lev > 5) 
            return ("<<..." + this.m_Items.length + ">>");
        let res = new StringBuilder();
        let it = this.getObject("Type", false);
        if (it !== null && lev >= 0) 
            res.append(it.toString());
        if (this.pageNum > 0) 
            res.append(" Num=").append(this.pageNum);
        if (this.id > 0) 
            res.append(" Id=").append(this.id);
        res.append("<<");
        let fi = true;
        let partial = false;
        for (const key of this.keys) {
            if (key === "Parent" || key === "Type") 
                continue;
            it = this.getObject(key, false);
            if (it === null) {
                let indir = this.m_Items.get(key);
                if (!fi) 
                    res.append(", ");
                else 
                    fi = false;
                res.append(key).append("=").append((indir === null ? "NULL" : indir.toString()));
                if (res.length > 100) 
                    break;
                continue;
            }
            if (!it.isSimple(lev + 1)) {
                partial = true;
                continue;
            }
            if (!fi) 
                res.append(", ");
            else 
                fi = false;
            if (res.length > 100) {
                partial = true;
                break;
            }
            let str = it.toStringEx(lev + 1);
            if (str.length > 50) 
                str = str.substring(0, 0 + 50) + "...";
            res.append(key).append("=").append(str);
        }
        if (partial) 
            res.append("...").append(this.m_Items.length);
        res.append(">>");
        if (this.m_StreamLength > 0) 
            res.append(" DATA:").append(this.m_StreamLength);
        return res.toString();
    }
    
    outHtml(res) {
        const PdfFont = require("./PdfFont");
        const PdfImage = require("./PdfImage");
        const PdfPage = require("./PdfPage");
        const PdfPageOper = require("./PdfPageOper");
        if (this.id > 0) 
            res.append("Id=").append(this.id).append(" ");
        res.append("<b>&lt;&lt;</b> ");
        let fi = true;
        for (const k of this.keys) {
            if (fi) 
                fi = false;
            else 
                res.append(", <br/>");
            res.append(k).append(" : ");
            let o = this.getObject(k, false);
            if (o === null) {
                res.append("null");
                continue;
            }
            if (o.id > 0) {
                res.append("<a href=\"obj").append(o.id).append("\">");
                MiscHelper.correctHtmlValue(res, o.toString(), false, false, false);
                res.append("</a>");
            }
            else 
                o.outHtml(res);
        }
        res.append("<b> &gt;&gt;</b>");
        this._outHtmlRefs(res);
        let page = null;
        if (this.isTypeItem("Page")) 
            page = new PdfPage(this, null, true);
        else if (this.isTypeItem("XObject") && this.getStringItem("Subtype") === "Form") {
            let li = this.sourceFile.getAllWhoRefObject0(this.id);
            for (const o of li) {
                if ((o instanceof PdfDictionary) && o.isTypeItem("Page")) {
                    let page0 = new PdfPage(Utils.as(o, PdfDictionary), null, true);
                    if (page0.subpages !== null) {
                        for (const p of page0.subpages) {
                            if (p.dic === this) {
                                page = p;
                                break;
                            }
                        }
                    }
                    break;
                }
            }
        }
        if (page !== null && page.lexems !== null) {
            res.append("\r\n<br/><hr/>");
            let opers = PdfPageOper.parse(page.lexems);
            for (const op of opers) {
                op.outHtml(res);
                res.append("<br/>");
            }
        }
        else if (this.isTypeItem("Font")) {
            res.append("\r\n<br/><hr/>");
            let font = new PdfFont(this);
            MiscHelper.correctHtmlValue(res, font.outDebug(), false, false, false);
        }
        else if (this.isTypeItem("Image") || ((this.isTypeItem("XObject") && this.getStringItem("Subtype") === "Image"))) {
            res.append("\r\n<br/><hr/>");
            let img = new PdfImage(this);
            if (img.content !== null) {
                let frm = FileFormatsHelper.analizeFormat(null, img.content);
                res.append("<div>Image Format: <b>").append(String(frm)).append("</b></div>");
                let uimg = new UnitextImage();
                uimg.content = img.content;
                if (img.width > 0) 
                    uimg.width = ((String(Math.floor(img.width))) + "pt");
                if (img.height > 0) 
                    uimg.height = ((String(Math.floor(img.height))) + "pt");
                res.append("<div style=\"border:1pt solid red\">");
                uimg.getHtml(res, null);
                res.append("</div>");
            }
        }
        else if (this.hasDataStream) {
            res.append("\r\n<br/><hr/>DataStream:<br/>");
            try {
                let dat = this.extractData();
                if (dat === null) 
                    dat = this.getData();
                if (dat !== null) {
                    let hasZero = 0;
                    let zind = -1;
                    for (let i = 0; i < dat.length; i++) {
                        if (dat[i] === (0)) {
                            hasZero++;
                            if (zind < 0) 
                                zind = i;
                            break;
                        }
                    }
                    if ((hasZero < 3) || zind > 1000) {
                        let len = dat.length;
                        if (dat.length > 30000) 
                            len = 30000;
                        let ch0 = String.fromCharCode(0);
                        let ch1 = String.fromCharCode(1);
                        let txt = Utils.replaceString(Utils.replaceString(Utils.decodeString("ASCII", dat, 0, len), (ch0), "{0}"), (ch1), "{1}");
                        MiscHelper.correctHtmlValue(res, txt, false, false, false);
                    }
                    else {
                        res.append("<div style=\"font-family:monospace\">");
                        for (let i = 0; (i < dat.length) && (i < 1000); i++) {
                            if (((i & 0xF)) === 0) 
                                res.append("\n<br/>");
                            res.append(Utils.correctToString((dat[i]).toString(16).toUpperCase(), 2, true)).append(" ");
                            if (res.length > 30000) {
                                res.append("\r\n<br/>...");
                                break;
                            }
                        }
                        res.append("</div>");
                    }
                }
            } catch (ex) {
                res.append("ERROR: ");
                MiscHelper.correctHtmlValue(res, ex.toString(), false, false, false);
            }
        }
    }
    
    get hasDataStream() {
        return this.m_StreamLength > 0;
    }
    
    postParse(stream) {
        this.m_Items.clear();
        this.m_StreamIn = null;
        this.m_StreamLength = 0;
        this.m_StreamData = null;
        let p0 = stream.position;
        let ch = 0;
        let i = 0;
        let str = null;
        while ((((i = stream.peekSolidByte()))) >= 0) {
            ch = i;
            if ((String.fromCharCode(ch)) === '/') {
                str = PdfStringValue.getStringByBytes(stream.readName());
                if (str === null) 
                    break;
                if (str === "Contents") {
                }
                let _obj = stream.parseObject(this.sourceFile, false);
                if (_obj === null) 
                    break;
                if (!this.m_Items.containsKey(str)) 
                    this.m_Items.put(str, _obj);
                continue;
            }
            if ((String.fromCharCode(ch)) !== '>') 
                break;
            stream.position = stream.position + 1;
            ch = stream.readByte();
            if ((String.fromCharCode(ch)) !== '>') 
                break;
            this._parseStream(stream);
            return;
        }
    }
    
    _parseStream(stream) {
        let p0 = stream.position;
        let str = stream.readWord(false);
        if (str !== "stream") {
            stream.position = p0;
            return;
        }
        let streamTag = new Uint8Array(6);
        let ch = stream.readByte();
        if (ch !== (0xD)) 
            stream.position = stream.position - 1;
        ch = stream.readByte();
        if (ch !== (0xA)) 
            stream.position = stream.position - 1;
        this.m_StreamIn = stream;
        this.m_StreamPos = stream.position;
        this.m_StreamLength = this.getIntItem("Length");
        if (this.m_StreamLength > 0 && ((this.m_StreamPos + this.m_StreamLength) < stream.length)) 
            stream.position = this.m_StreamPos + this.m_StreamLength;
        let len = 0;
        let i = 0;
        while ((((i = stream.readByte()))) >= 0) {
            ch = i;
            len++;
            if ((String.fromCharCode(ch)) !== 'e') 
                continue;
            ch = stream.readByte();
            len++;
            if ((String.fromCharCode(ch)) !== 'n') 
                continue;
            ch = stream.readByte();
            len++;
            if ((String.fromCharCode(ch)) !== 'd') 
                continue;
            p0 = stream.position;
            stream.read(streamTag, 0, 6);
            if (PdfStringValue.getStringByBytes(streamTag) === "stream") {
                len -= 3;
                break;
            }
            stream.position = p0;
        }
        if (this.m_StreamLength === 0) 
            this.m_StreamLength = len;
    }
    
    getStringItem(key) {
        let val = this.getObject(key, false);
        while (true) {
            if (val === null) 
                return null;
            if (val instanceof PdfName) 
                return val.name;
            if (val instanceof PdfBoolValue) 
                return (val.val ? "true" : "false");
            if (val instanceof PdfStringValue) 
                return PdfStringValue.getStringByBytes(val.val);
            if (val instanceof PdfIntValue) 
                return val.val.toString();
            if (val instanceof PdfArray) {
                val = val.getItem(0, false);
                if (val === null) 
                    break;
                continue;
            }
            break;
        }
        return null;
    }
    
    isTypeItem(typeVal) {
        return this.getStringItem("Type") === typeVal;
    }
    
    getIntItem(key) {
        let val = this.getObject(key, false);
        if (val === null) 
            return 0;
        if (val instanceof PdfIntValue) 
            return val.val;
        if (val instanceof PdfRealValue) 
            return Math.floor(val.getDouble());
        return 0;
    }
    
    getObject(key, keepRef = false) {
        let res = null;
        let wrapres176 = new RefOutArgWrapper();
        let inoutres177 = this.m_Items.tryGetValue(key, wrapres176);
        res = wrapres176.value;
        if (!inoutres177) 
            return null;
        if ((res instanceof PdfReference) && !keepRef) {
            res = this.sourceFile.getObject(res.id);
            if (res === null) 
                return null;
            this.m_Items.put(key, res);
        }
        return res;
    }
    
    getDictionary(key, typVal = null) {
        let _obj = this.getObject(key, false);
        if (_obj instanceof PdfArray) 
            _obj = _obj.getItem(0, false);
        if (_obj instanceof PdfDictionary) {
            let res = Utils.as(_obj, PdfDictionary);
            if (typVal !== null) {
                if (!res.isTypeItem(typVal)) 
                    return null;
            }
            return res;
        }
        return null;
    }
    
    getAllPages(res) {
        if (this.isTypeItem("Page")) {
            res.push(this);
            return;
        }
        let dic = this.getDictionary("Pages", "Pages");
        if (dic !== null) 
            dic.getAllPages(res);
        else {
            let kids = Utils.as(this.getObject("Kids", false), PdfArray);
            if (kids !== null) {
                for (let i = 0; i < kids.itemsCount; i++) {
                    let it = kids.getItem(i, false);
                    if (it instanceof PdfDictionary) 
                        it.getAllPages(res);
                }
            }
        }
    }
    
    extractData() {
        let dat = this.getData();
        if (dat === null) 
            return new Uint8Array(0);
        let str = this.getStringItem("Filter");
        if (Utils.isNullOrEmpty(str)) 
            return dat;
        if (dat.length < 10) 
            return null;
        let res = null;
        if (str === "FlateDecode") 
            res = ArchiveHelper.decompressZlib(dat);
        else if (str === "LZWDecode") {
            let lzw = new LzwDecoder();
            res = lzw.decode(dat);
        }
        else 
            return null;
        if (res === null || res.length === 0) 
            return null;
        let dparms = this.getDictionary("DecodeParms", null);
        let predict = 0;
        let N = 0;
        let columns = 0;
        if (dparms !== null) {
            let iv = Utils.as(dparms.getObject("Predictor", false), PdfIntValue);
            if (iv !== null && iv.val > 1) 
                predict = iv.val;
            iv = Utils.as(dparms.getObject("Columns", false), PdfIntValue);
            if (iv !== null) 
                columns = iv.val;
            iv = Utils.as(dparms.getObject("Colors", false), PdfIntValue);
            if (iv !== null) 
                N = iv.val;
        }
        if ((predict >= 10 && predict <= 15 && N > 0) && columns > 0) {
            let rowSize = columns * N;
            if (predict === 15) 
                rowSize += 1;
            for (let pos0 = 0; (pos0 + rowSize) < res.length; pos0 += rowSize) {
                let cod = predict - 10;
                let pos = pos0;
                if (predict === 15) {
                    cod = res[pos0];
                    pos++;
                }
                for (let j = pos; j < (pos0 + rowSize); j++) {
                    let valLeft = 0;
                    let valTop = 0;
                    let valCorner = 0;
                    let k = j - N;
                    let kk = j - rowSize;
                    if (kk >= 0) 
                        valTop = res[kk];
                    if (k >= pos) {
                        valLeft = res[k];
                        if (kk >= 0) 
                            valCorner = res[kk - N];
                    }
                    res[j] = PngWrapper.filterByte(cod, false, res[j], valLeft, valTop, valCorner);
                }
            }
            if (predict === 15) {
                let p = 0;
                for (let i = 0; i < res.length; i++) {
                    if (((i % rowSize)) === 0) {
                    }
                    else {
                        res[p] = res[i];
                        p++;
                    }
                }
                let res1 = new Uint8Array(p);
                for (let i = 0; i < p; i++) {
                    res1[i] = res[i];
                }
                res = res1;
            }
        }
        return res;
    }
    
    setDataValue(data) {
        let filt = Utils.as(this.getObject("Filter", false), PdfName);
        if (filt !== null) {
            if (filt.name === "FlateDecode" || filt.name === "LZWDecode") {
                filt.name = "FlateDecode";
                data = ArchiveHelper.compressZlib(data);
            }
        }
        this.m_StreamData = data;
    }
    
    getTotalDataStream(key) {
        let _obj = this.getObject(key, false);
        if (_obj === null) 
            return null;
        if (_obj instanceof PdfDictionary) 
            return _obj.extractData();
        let arr = Utils.as(_obj, PdfArray);
        if (arr === null) 
            return null;
        let res = null;
        for (let i = 0; i < arr.itemsCount; i++) {
            _obj = arr.getItem(i, false);
            if (_obj === null) 
                continue;
            if (!(_obj instanceof PdfDictionary)) 
                continue;
            let tmp = _obj.extractData();
            if (tmp === null) 
                continue;
            if (tmp.length < 1) 
                continue;
            if (res === null) 
                res = Array.from(tmp);
            else {
                res.push(0xD);
                res.push(0xA);
                res.splice(res.length, 0, ...tmp);
            }
        }
        if (res === null) 
            return null;
        return res;
    }
    
    clearTotalDataStream(key) {
        let _obj = this.getObject(key, false);
        if (_obj === null) 
            return;
        if (_obj instanceof PdfDictionary) 
            _obj.setData(null);
        let arr = Utils.as(_obj, PdfArray);
        if (arr === null) 
            return;
        for (let i = 0; i < arr.itemsCount; i++) {
            _obj = arr.getItem(i, false);
            if (_obj === null) 
                continue;
            if (!(_obj instanceof PdfDictionary)) 
                continue;
            _obj.setData(null);
        }
    }
    
    write(s) {
        if (this.id === 18) {
        }
        s.writeByte('<'.charCodeAt(0));
        s.writeByte('<'.charCodeAt(0));
        let buf = [ ];
        let fi = true;
        let dat = this.getData();
        let hasSize = false;
        if (dat !== null) {
            if (dat.length === 448) {
            }
        }
        for (const kp of this.m_Items.entries) {
            let it = kp.value;
            if (it.deleted) 
                continue;
            if (kp.key === "Length" && dat !== null && (it instanceof PdfIntValue)) {
                hasSize = true;
                it.val = dat.length;
            }
            if (fi) 
                fi = false;
            else 
                s.writeByte(' '.charCodeAt(0));
            buf = Utils.encodeString("ASCII", ("/" + kp.key));
            s.write(buf, 0, buf.length);
            s.writeByte(' '.charCodeAt(0));
            if (it.id === 0 || kp.key === "Length") 
                it.write(s);
            else {
                buf = Utils.encodeString("ASCII", (String(it.writeId) + " 0 R"));
                s.write(buf, 0, buf.length);
            }
        }
        if (!hasSize && dat !== null) {
            if (fi) 
                fi = false;
            else 
                s.writeByte(' '.charCodeAt(0));
            buf = Utils.encodeString("ASCII", ("/Length " + dat.length));
            s.write(buf, 0, buf.length);
        }
        s.writeByte('>'.charCodeAt(0));
        s.writeByte('>'.charCodeAt(0));
        if (dat === null) 
            return;
        buf = Utils.encodeString("ASCII", "stream\r\n");
        s.write(buf, 0, buf.length);
        s.write(dat, 0, dat.length);
        buf = Utils.encodeString("ASCII", "endstream");
        s.write(buf, 0, buf.length);
    }
    
    getAllReferences(refs, ids) {
        if (this.id > 0) {
            if (ids.includes(this.id)) 
                return;
            ids.push(this.id);
        }
        for (const k of this.keys) {
            let o = this.getObject(k, true);
            if (o !== null) 
                o.getAllReferences(refs, ids);
        }
        if (this.id > 0) 
            Utils.removeItem(ids, this.id);
    }
    
    getAllWhoRefObject(objId, cur, res, ids) {
        if (this.id > 0) {
            if (ids.includes(this.id)) 
                return;
            ids.push(this.id);
            cur = this;
        }
        for (const k of this.keys) {
            let o = this.getObject(k, true);
            if (o !== null && o.id === objId) {
                if (!res.includes(cur)) 
                    res.push(cur);
            }
            else if (o !== null && o.id === 0) 
                o.getAllWhoRefObject(objId, cur, res, ids);
        }
        if (this.id > 0) 
            Utils.removeItem(ids, this.id);
    }
    
    setNewDataContent(data) {
        if (this.isTypeItem("XObject")) {
            this.setDataValue(data);
            return;
        }
        let cnts = this.getObject("Contents", false);
        if (cnts instanceof PdfDictionary) 
            cnts.setDataValue(data);
        else if (cnts instanceof PdfArray) {
            let arr = Utils.as(cnts, PdfArray);
            if (arr.itemsCount > 0) {
                let ddic = null;
                let len = 0;
                for (let i = 0; i < arr.itemsCount; i++) {
                    let cc = Utils.as(arr.getItem(i, false), PdfDictionary);
                    if (cc === null) 
                        continue;
                    let le = cc.getIntItem("Length");
                    if (le > len) {
                        len = le;
                        ddic = cc;
                    }
                }
                if (ddic !== null) {
                    ddic.setDataValue(data);
                    for (let i = arr.itemsCount - 1; i >= 0; i--) {
                        let oo = arr.getItem(i, false);
                        if (oo !== null && oo !== ddic) 
                            oo.deleted = true;
                        if (oo !== ddic) 
                            arr.removeItem(i);
                    }
                }
            }
        }
        else if (this.hasDataStream) 
            this.setDataValue(data);
        else {
        }
    }
    
    static _new244(_arg1) {
        let res = new PdfDictionary();
        res.m_FilePos = _arg1;
        return res;
    }
}


module.exports = PdfDictionary