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

const ZipConstants = require("./ZipConstants");
const ZipExtraData = require("./ZipExtraData");
const GeneralBitFlags = require("./GeneralBitFlags");

// This class assists with writing/reading from Zip files.
class ZipHelperStream extends Stream {
    
    constructor(name, stream = null) {
        super();
        this.isOwner_ = false;
        this.stream_ = null;
        if (stream === null) {
            this.stream_ = new FileStream(name, "r+", false);
            this.isOwner_ = true;
        }
        else 
            this.stream_ = stream;
    }
    
    get isStreamOwner() {
        return this.isOwner_;
    }
    set isStreamOwner(value) {
        this.isOwner_ = value;
        return value;
    }
    
    get canRead() {
        return this.stream_.canRead();
    }
    
    get canSeek() {
        return this.stream_.canSeek();
    }
    
    get length() {
        return this.stream_.length;
    }
    
    get position() {
        return this.stream_.position;
    }
    set position(value) {
        this.stream_.position = value;
        return value;
    }
    
    get canWrite() {
        return this.stream_.canWrite();
    }
    
    flush() {
        this.stream_.flush();
    }
    
    seek(offset, origin) {
        return this.stream_.seek(offset, origin);
    }
    
    setLength(value) {
        this.stream_.setLength(value);
    }
    
    read(buffer, offset, count) {
        return this.stream_.read(buffer, offset, count);
    }
    
    write(buffer, offset, count) {
        this.stream_.write(buffer, offset, count);
    }
    
    close() {
        let toClose = this.stream_;
        this.stream_ = null;
        if (this.isOwner_ && (toClose !== null)) {
            this.isOwner_ = false;
            toClose.close();
        }
    }
    
    writeLocalHeader(entry, patchData) {
        let method = entry._CompressionMethod;
        let headerInfoAvailable = true;
        let patchEntryHeader = false;
        this.writeLEInt(ZipConstants.LOCAL_HEADER_SIGNATURE);
        this.writeLEShort(entry.version);
        this.writeLEShort(entry.flags);
        this.writeLEShort(method.value());
        this.writeLEInt(entry.dosTime);
        if (headerInfoAvailable === true) {
            this.writeLEInt(entry.crc);
            if (entry.localHeaderRequiresZip64) {
                this.writeLEInt(-1);
                this.writeLEInt(-1);
            }
            else {
                this.writeLEInt((entry.isCrypted ? (entry.compressedSize) + ZipConstants.CRYPTO_HEADER_SIZE : entry.compressedSize));
                this.writeLEInt(entry.size);
            }
        }
        else {
            if (patchData !== null) 
                patchData.crcPatchOffset = this.stream_.position;
            this.writeLEInt(0);
            if (patchData !== null) 
                patchData.sizePatchOffset = this.stream_.position;
            if (entry.localHeaderRequiresZip64 && patchEntryHeader) {
                this.writeLEInt(-1);
                this.writeLEInt(-1);
            }
            else {
                this.writeLEInt(0);
                this.writeLEInt(0);
            }
        }
        let name = ZipConstants.convertToArray(entry.flags, entry.name);
        if (name.length > 0xFFFF) 
            throw new Error("Entry name too long.");
        let ed = new ZipExtraData(entry.extraData);
        if (entry.localHeaderRequiresZip64 && ((headerInfoAvailable || patchEntryHeader))) {
            ed.startNewEntry();
            if (headerInfoAvailable) {
                ed.addLeLong(entry.size, 0);
                ed.addLeLong(entry.compressedSize, 0);
            }
            else {
                ed.addLeLong(-1, -1);
                ed.addLeLong(-1, -1);
            }
            ed.addNewEntry(1);
            if (!ed.find(1)) 
                throw new Error("Internal error cant find extra data");
            if (patchData !== null) 
                patchData.sizePatchOffset = ed.currentReadIndex;
        }
        else 
            ed._delete(1);
        let extra = ed.getEntryData();
        this.writeLEShort(name.length);
        this.writeLEShort(extra.length);
        if (name.length > 0) 
            this.stream_.write(name, 0, name.length);
        if (entry.localHeaderRequiresZip64 && patchEntryHeader) 
            patchData.sizePatchOffset = patchData.sizePatchOffset + (this.stream_.position);
        if (extra.length > 0) 
            this.stream_.write(extra, 0, extra.length);
    }
    
    locateBlockWithSignature(signature, endLocation, minimumBlockSize, maximumVariableData) {
        let pos = endLocation - minimumBlockSize;
        if (pos < 0) 
            return -1;
        let giveUpMarker = Math.max(pos - maximumVariableData, 0);
        do{
            if (pos < giveUpMarker) 
                return -1;
            this.seek(pos--, 0);
        } while (this.readLEInt() !== signature); 
        return this.position;
    }
    
    writeZip64EndOfCentralDirectory(noOfEntries, sizeEntries, centralDirOffset) {
        let centralSignatureOffset = this.stream_.position;
        this.writeLEInt(ZipConstants.ZIP64CENTRAL_FILE_HEADER_SIGNATURE);
        this.writeLELong(44);
        this.writeLEShort(ZipConstants.VERSION_MADE_BY);
        this.writeLEShort(ZipConstants.VERSION_ZIP64);
        this.writeLEInt(0);
        this.writeLEInt(0);
        this.writeLELong(noOfEntries);
        this.writeLELong(noOfEntries);
        this.writeLELong(sizeEntries);
        this.writeLELong(centralDirOffset);
        this.writeLEInt(ZipConstants.ZIP64CENTRAL_DIR_LOCATOR_SIGNATURE);
        this.writeLEInt(0);
        this.writeLELong(centralSignatureOffset);
        this.writeLEInt(1);
    }
    
    writeEndOfCentralDirectory(noOfEntries, sizeEntries, startOfCentralDirectory, comment) {
        this.writeLEInt(ZipConstants.END_OF_CENTRAL_DIRECTORY_SIGNATURE);
        this.writeLEShort(0);
        this.writeLEShort(0);
        if (noOfEntries >= 0xffff) {
            this.writeLEUshort(0xffff);
            this.writeLEUshort(0xffff);
        }
        else {
            this.writeLEShort(noOfEntries);
            this.writeLEShort(noOfEntries);
        }
        this.writeLEInt(sizeEntries);
        this.writeLEInt(startOfCentralDirectory);
        let commentLength = (comment !== null ? comment.length : 0);
        if (commentLength > 0xffff) 
            throw new Error(("Comment length(" + commentLength + ") is too long can only be 64K"));
        this.writeLEShort(commentLength);
        if (commentLength > 0) 
            this.write(comment, 0, comment.length);
    }
    
    readLEShort() {
        let byteValue1 = this.stream_.readByte();
        if (byteValue1 < 0) 
            throw new Error();
        let byteValue2 = this.stream_.readByte();
        if (byteValue2 < 0) 
            throw new Error();
        return byteValue1 | (byteValue2 << 8);
    }
    
    readLEInt() {
        return this.readLEShort() | (this.readLEShort() << 16);
    }
    
    writeLEShort(value) {
        this.stream_.writeByte((value & 0xff));
        this.stream_.writeByte((((value >> 8)) & 0xff));
    }
    
    writeLEUshort(value) {
        this.stream_.writeByte(((value) & 0xff));
        this.stream_.writeByte(((value) >>> 8));
    }
    
    writeLEInt(value) {
        this.writeLEShort(value);
        this.writeLEShort(value >> 16);
    }
    
    writeLEUint(value) {
        this.writeLEUshort(((value) & 0xffff));
        this.writeLEUshort(((value) >>> 16));
    }
    
    writeLELong(value) {
        this.writeLEInt(value);
        this.writeLEInt((value >> 32));
    }
    
    writeLEUlong(value) {
        this.writeLEUint(((value) & (0xffffffff)));
        this.writeLEUint(((value) >>> 32));
    }
    
    writeDataDescriptor(entry) {
        if (entry === null) 
            throw new Error("entry");
        let result = 0;
        if (((entry.flags & (GeneralBitFlags.DESCRIPTOR.value()))) !== 0) {
            this.writeLEInt(ZipConstants.DATA_DESCRIPTOR_SIGNATURE);
            this.writeLEInt(entry.crc);
            result += 8;
            if (entry.localHeaderRequiresZip64) {
                this.writeLELong(entry.compressedSize);
                this.writeLELong(entry.size);
                result += 16;
            }
            else {
                this.writeLEInt(entry.compressedSize);
                this.writeLEInt(entry.size);
                result += 8;
            }
        }
        return result;
    }
    
    readDataDescriptor(zip64, data) {
        let intValue = this.readLEInt();
        if (intValue !== ZipConstants.DATA_DESCRIPTOR_SIGNATURE) 
            throw new Error("Data descriptor signature not found");
        data.crc = this.readLEInt();
        if (zip64) {
            data.compressedSize = this.readLEInt();
            this.readLEInt();
            data.size = this.readLEInt();
            this.readLEInt();
        }
        else {
            data.compressedSize = this.readLEInt();
            data.size = this.readLEInt();
        }
    }
}


module.exports = ZipHelperStream