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

const InflaterInputBuffer = require("./InflaterInputBuffer");
const Inflater = require("./Inflater");

// This filter stream is used to decompress data compressed using the "deflate"
// format. The "deflate" format is described in RFC 1951.
// This stream may form the basis for other decompression filters, such
// as the <see cref="ICSharpCode.SharpZipLib.GZip.GZipInputStream">GZipInputStream</see>.
class InflaterInputStream extends Stream {
    
    constructor(_baseInputStream, inflater = null, bufferSize = 4096) {
        super();
        this.inf = null;
        this.inputBuffer = null;
        this.baseInputStream = null;
        this.csize = 0;
        this.isClosed = false;
        this.m_IsStreamOwner = true;
        if (inflater === null) 
            inflater = new Inflater();
        if (_baseInputStream === null) 
            throw new Error("baseInputStream");
        if (inflater === null) 
            throw new Error("inflater");
        if (bufferSize <= 0) 
            throw new Error("bufferSize");
        this.baseInputStream = _baseInputStream;
        this.inf = inflater;
        this.inputBuffer = new InflaterInputBuffer(_baseInputStream, bufferSize);
    }
    
    get isStreamOwner() {
        return this.m_IsStreamOwner;
    }
    set isStreamOwner(value) {
        this.m_IsStreamOwner = value;
        return value;
    }
    
    skip(count) {
        if (count <= 0) 
            throw new Error("count");
        if (this.baseInputStream.canSeek()) {
            this.baseInputStream.seek(count, 1);
            return count;
        }
        else {
            let _length = 2048;
            if (count < _length) 
                _length = count;
            let tmp = new Uint8Array(_length);
            let readCount = 1;
            let toSkip = count;
            while ((toSkip > 0) && (readCount > 0)) {
                if (toSkip < _length) 
                    _length = toSkip;
                readCount = this.baseInputStream.read(tmp, 0, _length);
                toSkip -= readCount;
            }
            return count - toSkip;
        }
    }
    
    get available() {
        return (this.inf.isFinished ? 0 : 1);
    }
    
    fill() {
        if (this.inputBuffer.available <= 0) {
            this.inputBuffer.fill();
            if (this.inputBuffer.available <= 0) 
                throw new Error("Unexpected EOF");
        }
        this.inputBuffer.setInflaterInput(this.inf);
    }
    
    get canRead() {
        return this.baseInputStream.canRead();
    }
    
    get canSeek() {
        return false;
    }
    
    get canWrite() {
        return false;
    }
    
    get length() {
        return this.inputBuffer.rawLength;
    }
    
    get position() {
        return this.baseInputStream.position;
    }
    set position(value) {
        return value;
    }
    
    flush() {
        this.baseInputStream.flush();
    }
    
    seek(offset, origin) {
        return 0;
    }
    
    setLength(value) {
        throw new Error("InflaterInputStream SetLength not supported");
    }
    
    write(buffer, offset, count) {
        throw new Error("InflaterInputStream Write not supported");
    }
    
    writeByte(value) {
        throw new Error("InflaterInputStream WriteByte not supported");
    }
    
    close() {
        if (!this.isClosed) {
            this.isClosed = true;
            if (this.m_IsStreamOwner) 
                this.baseInputStream.close();
        }
    }
    
    read(buffer, offset, count) {
        if (this.inf.isNeedingDictionary) 
            throw new Error("Need a dictionary");
        let remainingBytes = count;
        while (true) {
            let bytesRead = 0;
            try {
                bytesRead = this.inf.inflateEx(buffer, offset, remainingBytes);
            } catch (ex) {
                throw new Error(ex.message, ex);
            }
            offset += bytesRead;
            remainingBytes -= bytesRead;
            if (remainingBytes === 0 || this.inf.isFinished) 
                break;
            if (this.inf.isNeedingInput) {
                try {
                    this.fill();
                } catch (ex) {
                    throw new Error(ex.message, ex);
                }
            }
            else if (bytesRead === 0) 
                throw new Error("Dont know what to do");
        }
        return count - remainingBytes;
    }
}


module.exports = InflaterInputStream