/*
 * 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
 */

package com.pullenti.unitext.internal.zip;

// A class to handle the extra data field for Zip entries
public class ZipExtraData {

    public ZipExtraData(byte[] data) {
        if (data == null) 
            m_Data = new byte[0];
        else 
            m_Data = data;
    }

    public byte[] getEntryData() throws Exception {
        if (Long.compareUnsigned(Integer.toUnsignedLong(getLength()), Integer.toUnsignedLong(65535)) > 0) 
            throw new Exception("Data exceeds maximum length");
        if (m_Data == null) 
            return null;
        byte[] res = new byte[m_Data.length];
        for(int iii = 0; iii < (m_Data.length); iii++) res[iii] = m_Data[iii];
        return res;
    }

    public void clear() {
        if ((m_Data == null) || (m_Data.length != 0)) 
            m_Data = new byte[0];
    }

    public int getLength() {
        return m_Data.length;
    }


    public com.pullenti.unisharp.Stream getStreamForTag(int tag) throws Exception {
        com.pullenti.unisharp.Stream result = null;
        if (this.find(tag)) 
            result = new com.pullenti.unisharp.MemoryStream(m_Data, _index, _readValueLength);
        return result;
    }

    private ITaggedData getData(short tag) throws Exception {
        ITaggedData result = null;
        if (this.find((int)tag)) 
            result = create(tag, m_Data, _readValueStart, _readValueLength);
        return result;
    }

    private static ITaggedData create(short tag, byte[] data, int offset, int count) {
        ITaggedData 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;
    }

    public int getValueLength() {
        return _readValueLength;
    }


    public int getCurrentReadIndex() {
        return _index;
    }


    public int getUnreadCount() throws Exception {
        if ((_readValueStart > m_Data.length) || ((_readValueStart < 4))) 
            throw new Exception("Find must be called before calling a Read method");
        return (_readValueStart + _readValueLength) - _index;
    }


    public boolean find(int headerID) throws Exception {
        _readValueStart = m_Data.length;
        _readValueLength = 0;
        _index = 0;
        int localLength = _readValueStart;
        int localTag = headerID - 1;
        while ((localTag != headerID) && ((_index < (m_Data.length - 3)))) {
            localTag = this.readShortInternal();
            localLength = this.readShortInternal();
            if (localTag != headerID) 
                _index += localLength;
        }
        boolean result = (localTag == headerID) && (((_index + localLength)) <= m_Data.length);
        if (result) {
            _readValueStart = _index;
            _readValueLength = localLength;
        }
        return result;
    }

    public void addEntry(ITaggedData taggedData) throws IllegalArgumentException, IndexOutOfBoundsException, Exception {
        if (taggedData == null) 
            throw new IllegalArgumentException("taggedData");
        this.addEntry2((int)taggedData.getTagID(), taggedData.getData());
    }

    public void addEntry2(int headerID, byte[] fieldData) throws IndexOutOfBoundsException, Exception {
        if ((Long.compareUnsigned(Integer.toUnsignedLong(headerID), Integer.toUnsignedLong(65535)) > 0) || ((headerID < 0))) 
            throw new IndexOutOfBoundsException("headerID");
        int addLength = (fieldData == null ? 0 : fieldData.length);
        if (Long.compareUnsigned(Integer.toUnsignedLong(addLength), Integer.toUnsignedLong(65535)) > 0) 
            throw new IndexOutOfBoundsException("fieldData" + " " + "exceeds maximum length");
        int newLength = m_Data.length + addLength + 4;
        if (this.find(headerID)) 
            newLength -= ((this.getValueLength() + 4));
        if (Long.compareUnsigned(Integer.toUnsignedLong(newLength), Integer.toUnsignedLong(65535)) > 0) 
            throw new Exception("Data exceeds maximum length");
        this.delete(headerID);
        byte[] newData = new byte[newLength];
        for(int iii = 0; iii < (m_Data.length); iii++) newData[(0) + iii] = m_Data[(0) + iii];
        int index = m_Data.length;
        m_Data = newData;
        com.pullenti.unisharp.Outargwrapper<Integer> wrapindex561 = new com.pullenti.unisharp.Outargwrapper<Integer>(index);
        this.setShort(wrapindex561, headerID);
        index = (wrapindex561.value != null ? wrapindex561.value : 0);
        com.pullenti.unisharp.Outargwrapper<Integer> wrapindex560 = new com.pullenti.unisharp.Outargwrapper<Integer>(index);
        this.setShort(wrapindex560, addLength);
        index = (wrapindex560.value != null ? wrapindex560.value : 0);
        if (fieldData != null) 
            for(int iii = 0; iii < (fieldData.length); iii++) newData[(index) + iii] = fieldData[(0) + iii];
    }

    public void startNewEntry() {
        _newEntry = new com.pullenti.unisharp.MemoryStream();
    }

    public void addNewEntry(int headerID) throws IndexOutOfBoundsException, Exception {
        byte[] newData = _newEntry.toByteArray();
        _newEntry = null;
        this.addEntry2(headerID, newData);
    }

    public void addData0(byte data) throws java.io.IOException {
        _newEntry.write(data);
    }

    public void addData(byte[] data) throws IllegalArgumentException, java.io.IOException {
        if (data == null) 
            throw new IllegalArgumentException("data");
        _newEntry.write(data, 0, data.length);
    }

    public void addLeShort(int toAdd) throws java.io.IOException {
        /* unchecked C# block !!! */ {
            _newEntry.write((byte)toAdd);
            _newEntry.write((byte)((toAdd >> 8)));
        }
    }

    public void addLeInt(int toAdd) throws java.io.IOException {
        /* unchecked C# block !!! */ {
            this.addLeShort((int)((short)toAdd));
            this.addLeShort((int)((short)((toAdd >> 16))));
        }
    }

    public void addLeLong(int toAdd1, int toAdd2) throws java.io.IOException {
        this.addLeInt(toAdd1);
        this.addLeInt(toAdd2);
    }

    public boolean delete(int headerID) throws Exception {
        boolean result = false;
        if (this.find(headerID)) {
            result = true;
            int trueStart = _readValueStart - 4;
            byte[] newData = new byte[m_Data.length - ((this.getValueLength() + 4))];
            for(int iii = 0; iii < (trueStart); iii++) newData[(0) + iii] = m_Data[(0) + iii];
            int trueEnd = trueStart + getValueLength() + 4;
            for(int iii = 0; iii < (m_Data.length - trueEnd); iii++) newData[(trueStart) + iii] = m_Data[(trueEnd) + iii];
            m_Data = newData;
        }
        return result;
    }

    public int readLong() throws Exception {
        this.readCheck(8);
        int res = this.readInt();
        this.readInt();
        return res;
    }

    public int readInt() throws Exception {
        this.readCheck(4);
        int result = ((Byte.toUnsignedInt(m_Data[_index])) + ((Byte.toUnsignedInt(m_Data[_index + 1])) << 8) + ((Byte.toUnsignedInt(m_Data[_index + 2])) << 16)) + ((Byte.toUnsignedInt(m_Data[_index + 3])) << 24);
        _index += 4;
        return result;
    }

    public int readShort() throws Exception {
        this.readCheck(2);
        int result = (Byte.toUnsignedInt(m_Data[_index])) + ((Byte.toUnsignedInt(m_Data[_index + 1])) << 8);
        _index += 2;
        return result;
    }

    public int readByte() {
        int result = -1;
        if (((_index < m_Data.length)) && ((_readValueStart + _readValueLength) > _index)) {
            result = Byte.toUnsignedInt(m_Data[_index]);
            _index += 1;
        }
        return result;
    }

    public void skip(int amount) throws Exception {
        this.readCheck(amount);
        _index += amount;
    }

    private void readCheck(int _length) throws Exception {
        if ((_readValueStart > m_Data.length) || ((_readValueStart < 4))) 
            throw new Exception("Find must be called before calling a Read method");
        if (_index > ((_readValueStart + _readValueLength) - _length)) 
            throw new Exception("End of extra data");
        if ((_index + _length) < 4) 
            throw new Exception("Cannot read before start of tag");
    }

    private int readShortInternal() throws Exception {
        if (_index > (m_Data.length - 2)) 
            throw new Exception("End of extra data");
        int result = (Byte.toUnsignedInt(m_Data[_index])) + ((Byte.toUnsignedInt(m_Data[_index + 1])) << 8);
        _index += 2;
        return result;
    }

    private void setShort(com.pullenti.unisharp.Outargwrapper<Integer> index, int source) {
        m_Data[index.value] = (byte)source;
        m_Data[index.value + 1] = (byte)((source >> 8));
        index.value += 2;
    }

    public void dispose() {
        try {
            if (_newEntry != null) 
                _newEntry.close();
        } catch (Exception ex) {
        }
    }

    private int _index;

    private int _readValueStart;

    private int _readValueLength;

    private com.pullenti.unisharp.MemoryStream _newEntry;

    private byte[] m_Data;
    public ZipExtraData() {
        this(null);
    }
}
