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

const ITaggedData = require("./ITaggedData");
const RawTaggedData = require("./RawTaggedData");
const ExtendedUnixData = require("./ExtendedUnixData");
const NTTaggedData = require("./NTTaggedData");

// A class to handle the extra data field for Zip entries
class ZipExtraData {
    
    constructor(data = null) {
        this._index = 0;
        this._readValueStart = 0;
        this._readValueLength = 0;
        this._newEntry = null;
        this.m_Data = null;
        if (data === null) 
            this.m_Data = new Uint8Array(0);
        else 
            this.m_Data = data;
    }
    
    getEntryData() {
        if (Utils.compareUnsigned(this.length, 65535) > 0) 
            throw new Error("Data exceeds maximum length");
        if (this.m_Data === null) 
            return null;
        let res = new Uint8Array(this.m_Data.length);
        for(let iii = 0; iii < (this.m_Data.length); iii++) res[iii] = this.m_Data[iii];
        return res;
    }
    
    clear() {
        if ((this.m_Data === null) || (this.m_Data.length !== 0)) 
            this.m_Data = new Uint8Array(0);
    }
    
    get length() {
        return this.m_Data.length;
    }
    
    getStreamForTag(tag) {
        let result = null;
        if (this.find(tag)) 
            result = new MemoryStream(this.m_Data, this._index, this._readValueLength);
        return result;
    }
    
    getData(tag) {
        let result = null;
        if (this.find(tag)) 
            result = ZipExtraData.create(tag, this.m_Data, this._readValueStart, this._readValueLength);
        return result;
    }
    
    static create(tag, data, offset, count) {
        let result = null;
        switch (tag) { 
        case 0x000A: {
            result = new NTTaggedData();
            break;
        }
        case 0x5455: {
            result = new ExtendedUnixData();
            break;
        }
        default: {
            result = new RawTaggedData(tag);
            break;
        }
        }
        result.setData(data, offset, count);
        return result;
    }
    
    get valueLength() {
        return this._readValueLength;
    }
    
    get currentReadIndex() {
        return this._index;
    }
    
    get unreadCount() {
        if ((this._readValueStart > this.m_Data.length) || ((this._readValueStart < 4))) 
            throw new Error("Find must be called before calling a Read method");
        return (this._readValueStart + this._readValueLength) - this._index;
    }
    
    find(headerID) {
        this._readValueStart = this.m_Data.length;
        this._readValueLength = 0;
        this._index = 0;
        let localLength = this._readValueStart;
        let localTag = headerID - 1;
        while ((localTag !== headerID) && ((this._index < (this.m_Data.length - 3)))) {
            localTag = this.readShortInternal();
            localLength = this.readShortInternal();
            if (localTag !== headerID) 
                this._index += localLength;
        }
        let result = (localTag === headerID) && (((this._index + localLength)) <= this.m_Data.length);
        if (result) {
            this._readValueStart = this._index;
            this._readValueLength = localLength;
        }
        return result;
    }
    
    addEntry(taggedData) {
        if (taggedData === null) 
            throw new Error("taggedData");
        this.addEntry2(taggedData.tagID, taggedData.getData());
    }
    
    addEntry2(headerID, fieldData) {
        if ((Utils.compareUnsigned(headerID, 65535) > 0) || ((headerID < 0))) 
            throw new Error("headerID");
        let addLength = (fieldData === null ? 0 : fieldData.length);
        if (Utils.compareUnsigned(addLength, 65535) > 0) 
            throw new Error("fieldData", "exceeds maximum length");
        let newLength = this.m_Data.length + addLength + 4;
        if (this.find(headerID)) 
            newLength -= ((this.valueLength + 4));
        if (Utils.compareUnsigned(newLength, 65535) > 0) 
            throw new Error("Data exceeds maximum length");
        this._delete(headerID);
        let newData = new Uint8Array(newLength);
        for(let iii = 0; iii < (this.m_Data.length); iii++) newData[(0) + iii] = this.m_Data[(0) + iii];
        let index = this.m_Data.length;
        this.m_Data = newData;
        let wrapindex560 = new RefOutArgWrapper(index);
        this.setShort(wrapindex560, headerID);
        index = wrapindex560.value;
        let wrapindex559 = new RefOutArgWrapper(index);
        this.setShort(wrapindex559, addLength);
        index = wrapindex559.value;
        if (fieldData !== null) 
            for(let iii = 0; iii < (fieldData.length); iii++) newData[(index) + iii] = fieldData[(0) + iii];
    }
    
    startNewEntry() {
        this._newEntry = new MemoryStream();
    }
    
    addNewEntry(headerID) {
        let newData = this._newEntry.toByteArray();
        this._newEntry = null;
        this.addEntry2(headerID, newData);
    }
    
    addData0(data) {
        this._newEntry.writeByte(data);
    }
    
    addData(data) {
        if (data === null) 
            throw new Error("data");
        this._newEntry.write(data, 0, data.length);
    }
    
    addLeShort(toAdd) {
        /* unchecked C# block !!! */ {
            this._newEntry.writeByte(toAdd);
            this._newEntry.writeByte((toAdd >> 8));
        }
    }
    
    addLeInt(toAdd) {
        /* unchecked C# block !!! */ {
            this.addLeShort(toAdd);
            this.addLeShort((toAdd >> 16));
        }
    }
    
    addLeLong(toAdd1, toAdd2) {
        this.addLeInt(toAdd1);
        this.addLeInt(toAdd2);
    }
    
    _delete(headerID) {
        let result = false;
        if (this.find(headerID)) {
            result = true;
            let trueStart = this._readValueStart - 4;
            let newData = new Uint8Array(this.m_Data.length - ((this.valueLength + 4)));
            for(let iii = 0; iii < (trueStart); iii++) newData[(0) + iii] = this.m_Data[(0) + iii];
            let trueEnd = trueStart + this.valueLength + 4;
            for(let iii = 0; iii < (this.m_Data.length - trueEnd); iii++) newData[(trueStart) + iii] = this.m_Data[(trueEnd) + iii];
            this.m_Data = newData;
        }
        return result;
    }
    
    readLong() {
        this.readCheck(8);
        let res = this.readInt();
        this.readInt();
        return res;
    }
    
    readInt() {
        this.readCheck(4);
        let result = ((this.m_Data[this._index]) + ((this.m_Data[this._index + 1]) << 8) + ((this.m_Data[this._index + 2]) << 16)) + ((this.m_Data[this._index + 3]) << 24);
        this._index += 4;
        return result;
    }
    
    readShort() {
        this.readCheck(2);
        let result = (this.m_Data[this._index]) + ((this.m_Data[this._index + 1]) << 8);
        this._index += 2;
        return result;
    }
    
    readByte() {
        let result = -1;
        if (((this._index < this.m_Data.length)) && ((this._readValueStart + this._readValueLength) > this._index)) {
            result = this.m_Data[this._index];
            this._index += 1;
        }
        return result;
    }
    
    skip(amount) {
        this.readCheck(amount);
        this._index += amount;
    }
    
    readCheck(_length) {
        if ((this._readValueStart > this.m_Data.length) || ((this._readValueStart < 4))) 
            throw new Error("Find must be called before calling a Read method");
        if (this._index > ((this._readValueStart + this._readValueLength) - _length)) 
            throw new Error("End of extra data");
        if ((this._index + _length) < 4) 
            throw new Error("Cannot read before start of tag");
    }
    
    readShortInternal() {
        if (this._index > (this.m_Data.length - 2)) 
            throw new Error("End of extra data");
        let result = (this.m_Data[this._index]) + ((this.m_Data[this._index + 1]) << 8);
        this._index += 2;
        return result;
    }
    
    setShort(index, source) {
        this.m_Data[index.value] = source;
        this.m_Data[index.value + 1] = (source >> 8);
        index.value += 2;
    }
    
    dispose() {
        try {
            if (this._newEntry !== null) 
                this._newEntry.close();
        } catch (ex) {
        }
    }
}


module.exports = ZipExtraData