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

const PdfObject = require("./PdfObject");
const PdfArray = require("./PdfArray");
const PdfStringValue = require("./PdfStringValue");
const PdfBoolValue = require("./PdfBoolValue");
const PdfNull = require("./PdfNull");
const PdfReference = require("./PdfReference");
const PdfIntValue = require("./PdfIntValue");
const PdfName = require("./PdfName");
const PdfRealValue = require("./PdfRealValue");
const PdfDictionary = require("./PdfDictionary");

class PdfStream {
    
    constructor(fileName, fileContent) {
        this.m_Stream = null;
        if (fileContent !== null) 
            this.m_Stream = new MemoryStream(fileContent);
        else 
            this.m_Stream = new FileStream(fileName, "r", false);
    }
    
    close() {
        if (this.m_Stream !== null) 
            this.m_Stream.close();
        this.m_Stream = null;
    }
    
    isSpace(ch) {
        if ((ch === (0x20) || ch === (0xD) || ch === (0xA)) || ch === (9)) 
            return true;
        return false;
    }
    
    isDelimiter(ch) {
        if (((String.fromCharCode(ch)) === '(' || (String.fromCharCode(ch)) === ')' || (String.fromCharCode(ch)) === '<') || (String.fromCharCode(ch)) === '>') 
            return true;
        if (((String.fromCharCode(ch)) === '[' || (String.fromCharCode(ch)) === ']' || (String.fromCharCode(ch)) === '{') || (String.fromCharCode(ch)) === '}') 
            return true;
        if ((String.fromCharCode(ch)) === '/') 
            return true;
        return false;
    }
    
    hexToInt(ch) {
        if ((String.fromCharCode(ch)) >= '0' && (String.fromCharCode(ch)) <= '9') 
            return ((ch) - ('0'.charCodeAt(0)));
        if ((String.fromCharCode(ch)) >= 'A' && (String.fromCharCode(ch)) <= 'F') 
            return (((ch) - ('A'.charCodeAt(0))) + 10);
        if ((String.fromCharCode(ch)) >= 'a' && (String.fromCharCode(ch)) <= 'f') 
            return (((ch) - ('a'.charCodeAt(0))) + 10);
        return -1;
    }
    
    toHex(val) {
        if (val < 10) 
            return (('0'.charCodeAt(0)) + val);
        return ((('A'.charCodeAt(0)) + val) - 10);
    }
    
    get position() {
        return this.m_Stream.position;
    }
    set position(value) {
        this.m_Stream.position = value;
        return value;
    }
    
    get length() {
        return this.m_Stream.length;
    }
    
    readByte() {
        return this.m_Stream.readByte();
    }
    
    read(arr, off, len) {
        return this.m_Stream.read(arr, off, len);
    }
    
    peekSolidByte() {
        let cmt = false;
        for (let i = this.m_Stream.readByte(); i >= 0; i = this.m_Stream.readByte()) {
            let ch = i;
            if ((String.fromCharCode(ch)) === '%') 
                cmt = true;
            else if (ch === (0xD) || ch === (0xA)) 
                cmt = false;
            else if (ch !== (0x20) && ch !== (9) && !cmt) {
                this.m_Stream.position = this.m_Stream.position - (1);
                return i;
            }
        }
        return -1;
    }
    
    readHead() {
        let head = new Uint8Array(4);
        let p0 = this.m_Stream.position;
        let i = 0;
        this.m_Stream.read(head, 0, 4);
        if ((head.length !== 4 || (String.fromCharCode(head[0])) !== '%' || (String.fromCharCode(head[1])) !== 'P') || (String.fromCharCode(head[2])) !== 'D' || (String.fromCharCode(head[3])) !== 'F') 
            throw new Error("File not in PDF format");
        for (i = 4; i < (this.m_Stream.length - (4)); i++) {
            this.m_Stream.position = i;
            this.m_Stream.read(head, 0, 4);
            if (((((String.fromCharCode(head[0])) === ' ' || head[0] === (0xA) || head[0] === (0xD))) && (String.fromCharCode(head[1])) === 'o' && (String.fromCharCode(head[2])) === 'b') && (String.fromCharCode(head[3])) === 'j') 
                break;
        }
        if (i >= (this.m_Stream.length - (4))) 
            throw new Error("Bad PDF format");
        for (; i > 4; i--) {
            this.m_Stream.position = i;
            let b = this.m_Stream.readByte();
            if (!Utils.isWhitespace(String.fromCharCode(b)) && (((b < (0x30)) || b > (0x39)))) {
                i++;
                break;
            }
        }
        this.m_Stream.position = p0;
        head = new Uint8Array(i);
        this.m_Stream.read(head, 0, head.length);
        return head;
    }
    
    readLexemString() {
        let ch = this.m_Stream.readByte();
        let p0 = this.m_Stream.position;
        let i = 0;
        let val = new Array();
        if ((String.fromCharCode(ch)) === '<') {
            while ((((i = this.m_Stream.readByte()))) > 0) {
                ch = i;
                if ((String.fromCharCode(ch)) === '>') 
                    break;
                if (this.isSpace(ch)) 
                    continue;
                let i1 = this.hexToInt(ch);
                ch = this.m_Stream.readByte();
                let i2 = ((String.fromCharCode(ch)) === '>' ? 0 : this.hexToInt(ch));
                if ((i1 < 0) || (i2 < 0)) 
                    val.push(0);
                else 
                    val.push(((i1 << 4) + i2));
                if ((String.fromCharCode(ch)) === '>') 
                    break;
            }
            return val;
        }
        if ((String.fromCharCode(ch)) !== '(') 
            return null;
        let lev = 0;
        let isUnicode = 0;
        while ((((i = this.m_Stream.readByte()))) >= 0) {
            ch = i;
            if ((val.length < 2) && ((ch === (0xFE) || ch === (0xFF)))) 
                isUnicode++;
            if (isUnicode > 1) {
                if ((String.fromCharCode(ch)) === ')' && (((val.length % 2)) === 0)) 
                    break;
                val.push(ch);
                continue;
            }
            val.push(ch);
            if ((String.fromCharCode(ch)) === '(') {
                lev++;
                continue;
            }
            if ((String.fromCharCode(ch)) === ')') {
                if ((--lev) < 0) {
                    val.splice(val.length - 1, 1);
                    break;
                }
                continue;
            }
            if ((String.fromCharCode(ch)) !== '\\') 
                continue;
            val.splice(val.length - 1, 1);
            ch = this.m_Stream.readByte();
            if ((String.fromCharCode(ch)) >= '0' && (String.fromCharCode(ch)) <= '7') {
                let res = ((ch) - ('0'.charCodeAt(0)));
                for (let k = 0; k < 2; k++) {
                    ch = this.m_Stream.readByte();
                    if ((String.fromCharCode(ch)) >= '0' && (String.fromCharCode(ch)) <= '7') 
                        res = (res * 8) + (((ch) - ('0'.charCodeAt(0))));
                    else {
                        this.m_Stream.position = this.m_Stream.position - (1);
                        break;
                    }
                }
                val.push(res);
                continue;
            }
            if (ch === (0xD) || ch === (0xA)) 
                continue;
            if ((String.fromCharCode(ch)) === 'r') 
                ch = 0xD;
            else if ((String.fromCharCode(ch)) === 'n') 
                ch = 0xA;
            else if ((String.fromCharCode(ch)) === 't') 
                ch = 9;
            else if ((String.fromCharCode(ch)) === 'b') 
                ch = 8;
            else if ((String.fromCharCode(ch)) === 'f') 
                ch = 12;
            else if ((String.fromCharCode(ch)) === '\\' || (String.fromCharCode(ch)) === '(' || (String.fromCharCode(ch)) === ')') {
            }
            else {
            }
            val.push(ch);
        }
        return val;
    }
    
    readName() {
        let i = this.m_Stream.readByte();
        if ((String.fromCharCode(i)) !== '/') 
            return null;
        let res = new Array();
        while ((((i = this.m_Stream.readByte()))) >= 0) {
            let ch = i;
            if (this.isSpace(ch)) 
                break;
            if (this.isDelimiter(ch)) {
                this.m_Stream.position = this.m_Stream.position - (1);
                break;
            }
            if ((String.fromCharCode(ch)) !== '#') {
                res.push(ch);
                continue;
            }
            let i1 = this.hexToInt(this.m_Stream.readByte());
            let i2 = this.hexToInt(this.m_Stream.readByte());
            if ((i1 < 0) || (i2 < 0)) 
                res.push(0);
            else 
                res.push(((i1 << 4) + i2));
        }
        if (res.length === 0) 
            return null;
        return res;
    }
    
    readWord(notNull = false) {
        let i = 0;
        let res = new StringBuilder();
        while ((((i = this.m_Stream.readByte()))) >= 0) {
            let ch = i;
            if (this.isSpace(ch)) {
                if (res.length > 0) 
                    break;
            }
            else if (this.isDelimiter(ch)) {
                if (res.length === 0 && notNull) {
                    res.append(String.fromCharCode(ch));
                    break;
                }
                this.m_Stream.position = this.m_Stream.position - (1);
                break;
            }
            else 
                res.append(String.fromCharCode(ch));
        }
        if (res.length === 0) 
            return null;
        return res.toString();
    }
    
    tryReadIdAndVersion(mustBeRef, id, vers) {
        id.value = 0;
        vers.value = 0;
        let p0 = this.m_Stream.position;
        let inoutres238 = Utils.tryParseInt(this.readWord(false), id);
        if (!inoutres238) {
            this.m_Stream.position = p0;
            return false;
        }
        let inoutres237 = Utils.tryParseInt(this.readWord(false), vers);
        if (!inoutres237) {
            this.m_Stream.position = p0;
            return false;
        }
        let str = this.readWord(false);
        if (mustBeRef) {
            if (str === "R") 
                return true;
        }
        else if (str === "obj") 
            return true;
        this.m_Stream.position = p0;
        return false;
    }
    
    parseObject(file, text) {
        let res = null;
        let i = this.peekSolidByte();
        if (i < 0) 
            return res;
        let ch = i;
        let p0 = this.m_Stream.position;
        if ((String.fromCharCode(ch)) >= '0' && (String.fromCharCode(ch)) <= '9' && !text) {
            let id = 0;
            let ver = 0;
            let wrapid239 = new RefOutArgWrapper();
            let wrapver240 = new RefOutArgWrapper();
            let inoutres241 = this.tryReadIdAndVersion(true, wrapid239, wrapver240);
            id = wrapid239.value;
            ver = wrapver240.value;
            if (inoutres241) {
                res = new PdfReference();
                res.sourceFile = file;
                res.id = id;
                res.version = ver;
                return res;
            }
        }
        if (((((String.fromCharCode(ch)) >= '0' && (String.fromCharCode(ch)) <= '9')) || (String.fromCharCode(ch)) === '+' || (String.fromCharCode(ch)) === '-') || (String.fromCharCode(ch)) === '.') {
            let str = this.readWord(false);
            if (str === null) 
                return null;
            if (str.indexOf('.') < 0) {
                let v = 0;
                let wrapv242 = new RefOutArgWrapper();
                let inoutres243 = Utils.tryParseInt(str, wrapv242);
                v = wrapv242.value;
                if (!inoutres243) 
                    return null;
                let ires = new PdfIntValue();
                ires.val = v;
                return ires;
            }
            else {
                let fres = new PdfRealValue();
                fres.val = str;
                return fres;
            }
        }
        if ((String.fromCharCode(ch)) === '/') {
            let nres = new PdfName();
            nres.name = PdfStringValue.getStringByBytes(this.readName());
            if (nres.name === null) 
                return null;
            return nres;
        }
        else if ((String.fromCharCode(ch)) === '(') {
            let sres = new PdfStringValue();
            sres.val = this.readLexemString();
            if (sres.val === null) 
                return null;
            return sres;
        }
        else if ((String.fromCharCode(ch)) === '[') 
            res = new PdfArray();
        else if ((String.fromCharCode(ch)) === '<') {
            this.m_Stream.position = p0 + 1;
            ch = this.m_Stream.readByte();
            if ((String.fromCharCode(ch)) === '<') 
                res = PdfDictionary._new244(p0);
            else {
                let sres = PdfStringValue._new245(true);
                this.m_Stream.position = p0;
                sres.val = this.readLexemString();
                if (sres.val === null) 
                    return null;
                return sres;
            }
        }
        else if ((((String.fromCharCode(ch)) === '\'' || (String.fromCharCode(ch)) === '\"')) && text) {
            let nres = new PdfName();
            nres.name = ((String.fromCharCode(ch)) === '\'' ? "\'" : "\"");
            this.m_Stream.position = this.m_Stream.position + (1);
            return nres;
        }
        else {
            let str = this.readWord(false);
            switch (str) { 
            case "null": {
                return new PdfNull();
            }
            case "true": {
                let bres1 = new PdfBoolValue();
                bres1.val = true;
                return bres1;
            }
            case "false": {
                let bres2 = new PdfBoolValue();
                bres2.val = false;
                return bres2;
            }
            case "endobj": {
                return null;
            }
            }
            if (text && str !== null) {
                let nres = new PdfName();
                nres.name = str;
                return nres;
            }
            return null;
        }
        res.sourceFile = file;
        if (res instanceof PdfDictionary) 
            res.postParse(this);
        else if (res instanceof PdfArray) 
            res.postParse(this);
        return res;
    }
    
    parseContent() {
        let res = new Array();
        while (true) {
            let i = this.peekSolidByte();
            if (i < 0) 
                break;
            try {
                let pos = this.position;
                let obj = this.parseObject(null, true);
                if (obj !== null) {
                    obj.readPos = pos;
                    obj.readLen = ((this.position) - pos);
                    res.push(obj);
                }
                else 
                    this.readByte();
            } catch (ex246) {
                break;
            }
        }
        return res;
    }
}


module.exports = PdfStream