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

// This is an InflaterInputStream that reads the files baseInputStream an zip archive
// one after another.  It has a special method to get the zip entry of
// the next file.  The zip entry contains information about the file name
// size, compressed size, Crc, etc.
// It includes support for Stored and Deflated entries.
public class ZipInputStream extends InflaterInputStream {

    /**
     * Delegate for reading bytes from a stream.
     */
    public static interface ReadDataHandler {
    
        int call(byte[] b, int offset, int _length);
    }


    private ReadDataHandler internalReader;

    private Crc32 m_Crc = new Crc32();

    private ZipEntry m_Entry;

    private int m_Size;

    private int m_Method;

    private int m_Flags;

    private String m_Password;

    public ZipInputStream(com.pullenti.unisharp.Stream _baseInputStream, int bufferSize) throws IllegalArgumentException, IndexOutOfBoundsException {
        super(_baseInputStream, new Inflater(true), bufferSize);
        if(_globalInstance == null) return;
        _ReadDataHandler_ReadingNotAvailable = new ReadDataHandler_ReadingNotAvailable(this);
        _ReadDataHandler_InitialRead = new ReadDataHandler_InitialRead(this);
        _ReadDataHandler_ReadingNotSupported = new ReadDataHandler_ReadingNotSupported(this);
        _ReadDataHandler_BodyRead = new ReadDataHandler_BodyRead(this);
        internalReader = _ReadDataHandler_ReadingNotAvailable;
    }

    public String getPassword() {
        return m_Password;
    }

    public String setPassword(String value) {
        m_Password = value;
        return value;
    }


    public boolean canDecompressEntry() throws Exception {
        return (m_Entry != null) && m_Entry.canDecompress();
    }


    public ZipEntry getNextEntry() throws UnsupportedOperationException, Exception, java.io.IOException, IndexOutOfBoundsException, IllegalArgumentException, IllegalArgumentException, UnsupportedOperationException {
        if (m_Crc == null) 
            throw new UnsupportedOperationException("Closed.");
        if (m_Entry != null) 
            this.closeEntry();
        int header = inputBuffer.readLeInt();
        if ((header == ZipConstants.CENTRALHEADERSIGNATURE || header == ZipConstants.ENDOFCENTRALDIRECTORYSIGNATURE || header == ZipConstants.CENTRALHEADERDIGITALSIGNATURE) || header == ZipConstants.ARCHIVEEXTRADATASIGNATURE || header == ZipConstants.ZIP64CENTRALFILEHEADERSIGNATURE) {
            this.close();
            return null;
        }
        if ((header == ZipConstants.SPANNINGTEMPSIGNATURE) || (header == ZipConstants.SPANNINGSIGNATURE)) 
            header = inputBuffer.readLeInt();
        if (header != ZipConstants.LOCALHEADERSIGNATURE) 
            throw new Exception("Wrong Local header signature: 0x" + (String.format("%X", header)));
        short versionRequiredToExtract = (short)inputBuffer.readLeShort();
        m_Flags = inputBuffer.readLeShort();
        m_Method = inputBuffer.readLeShort();
        int dostime = (int)inputBuffer.readLeInt();
        int crc2 = inputBuffer.readLeInt();
        csize = inputBuffer.readLeInt();
        m_Size = inputBuffer.readLeInt();
        int nameLen = inputBuffer.readLeShort();
        int extraLen = inputBuffer.readLeShort();
        boolean isCrypted = ((m_Flags & 1)) == 1;
        byte[] buffer = new byte[nameLen];
        inputBuffer.readRawBuffer(buffer);
        String name = ZipConstants.convertToStringExt0(m_Flags, buffer);
        m_Entry = new ZipEntry(name, (int)versionRequiredToExtract, ZipConstants.VERSIONMADEBY, CompressionMethod.DEFLATED);
        m_Entry.flags = m_Flags;
        m_Entry.set_CompressionMethod(CompressionMethod.of(m_Method));
        if (((m_Flags & 8)) == 0) {
            m_Entry.setCrc((int)crc2);
            m_Entry.setSize(m_Size);
            m_Entry.setCompressedSize(csize);
            m_Entry.setCryptoCheckValue((byte)((((crc2 >> 24)) & 0xff)));
        }
        else {
            if (crc2 != 0) 
                m_Entry.setCrc((int)crc2);
            if (m_Size != 0) 
                m_Entry.setSize(m_Size);
            if (csize != 0) 
                m_Entry.setCompressedSize(csize);
            m_Entry.setCryptoCheckValue((byte)((((((int)dostime) >>> 8)) & 0xff)));
        }
        m_Entry.setDosTime(dostime);
        if (extraLen > 0) {
            byte[] extra = new byte[extraLen];
            inputBuffer.readRawBuffer(extra);
            m_Entry.setExtraData(extra);
        }
        m_Entry.processExtraData(true);
        if (m_Entry.getCompressedSize() >= 0) 
            csize = m_Entry.getCompressedSize();
        if (m_Entry.getSize() >= 0) 
            m_Size = m_Entry.getSize();
        if (m_Method == (CompressionMethod.STORED.value()) && (((!isCrypted && csize != m_Size) || ((isCrypted && (csize - ZipConstants.CRYPTOHEADERSIZE) != m_Size))))) 
            throw new Exception("Stored, but compressed != uncompressed");
        if (m_Entry.isCompressionMethodSupported()) 
            internalReader = _ReadDataHandler_InitialRead;
        else 
            internalReader = _ReadDataHandler_ReadingNotSupported;
        return m_Entry;
    }

    private void readDataDescriptor() throws Exception, java.io.IOException {
        if (inputBuffer.readLeInt() != ZipConstants.DATADESCRIPTORSIGNATURE) 
            throw new Exception("Data descriptor signature not found");
        m_Entry.setCrc((int)inputBuffer.readLeInt());
        if (m_Entry.getLocalHeaderRequiresZip64()) {
            csize = inputBuffer.readLeInt();
            inputBuffer.readLeInt();
            m_Size = inputBuffer.readLeInt();
            inputBuffer.readLeInt();
        }
        else {
            csize = inputBuffer.readLeInt();
            m_Size = inputBuffer.readLeInt();
        }
        m_Entry.setCompressedSize(csize);
        m_Entry.setSize(m_Size);
    }

    private void completeCloseEntry(boolean testCrc) throws Exception, java.io.IOException {
        if (((m_Flags & 8)) != 0) 
            this.readDataDescriptor();
        m_Size = 0;
        if (testCrc && (Integer.compareUnsigned(m_Crc.getValue(), m_Entry.getCrc()) != 0)) 
            throw new Exception("CRC mismatch");
        m_Crc.reset();
        if (m_Method == (CompressionMethod.DEFLATED.value())) 
            inf.reset();
        m_Entry = null;
    }

    public void closeEntry() throws UnsupportedOperationException, Exception, IndexOutOfBoundsException, java.io.IOException {
        if (m_Crc == null) 
            throw new UnsupportedOperationException("Closed");
        if (m_Entry == null) 
            return;
        if (m_Method == (CompressionMethod.DEFLATED.value())) {
            if (((m_Flags & 8)) != 0) {
                byte[] tmp = new byte[4096];
                while (this.read(tmp, 0, tmp.length) > 0) {
                }
                return;
            }
            csize -= inf.getTotalIn();
            inputBuffer.setAvailable(inputBuffer.getAvailable() + inf.getRemainingInput());
        }
        if ((inputBuffer.getAvailable() > csize) && (csize >= 0)) 
            inputBuffer.setAvailable((int)((((long)inputBuffer.getAvailable()) - ((long)csize))));
        else {
            csize -= inputBuffer.getAvailable();
            inputBuffer.setAvailable(0);
            while (csize != 0) {
                int skipped = super.skip(csize);
                if (skipped <= 0) 
                    throw new Exception("Zip archive ends early.");
                csize -= skipped;
            }
        }
        this.completeCloseEntry(false);
    }

    @Override
    public int getAvailable() {
        return (m_Entry != null ? 1 : 0);
    }


    @Override
    public long length() {
        if (m_Entry != null) {
            if (m_Entry.getSize() >= 0) 
                return (long)m_Entry.getSize();
            else 
                return 0L;
        }
        else 
            return 0L;
    }


    @Override
    public int read() {
        byte[] b = new byte[1];
        if (this.read(b, 0, 1) <= 0) 
            return -1;
        return (Byte.toUnsignedInt(b[0])) & 0xff;
    }

    private int readingNotAvailable(byte[] destination, int offset, int count) throws UnsupportedOperationException {
        throw new UnsupportedOperationException("Unable to read from this stream");
    }

    private int readingNotSupported(byte[] destination, int offset, int count) throws Exception {
        throw new Exception("The compression method for this entry is not supported");
    }

    private int initialRead(byte[] destination, int offset, int count) throws Exception, IllegalArgumentException, IndexOutOfBoundsException, UnsupportedOperationException, IllegalArgumentException, java.io.IOException {
        if (!canDecompressEntry()) 
            throw new Exception("Library cannot extract this entry. Version required is (" + m_Entry.getVersion() + ")");
        if ((csize > 0) || (((m_Flags & (GeneralBitFlags.DESCRIPTOR.value()))) != 0)) {
            if ((m_Method == (CompressionMethod.DEFLATED.value())) && (inputBuffer.getAvailable() > 0)) 
                inputBuffer.setInflaterInput(inf);
            internalReader = _ReadDataHandler_BodyRead;
            return this.bodyRead(destination, offset, count);
        }
        else {
            internalReader = _ReadDataHandler_ReadingNotAvailable;
            return 0;
        }
    }

    @Override
    public int read(byte[] buffer, int offset, int count) {
        try {
            if (buffer == null) 
                throw new java.io.IOException("null buffer");
            if (offset < 0) 
                throw new Exception("bad offset");
            if (count < 0) 
                throw new Exception("bad count");
            if (((buffer.length - offset)) < count) 
                throw new java.io.IOException("Invalid offset/count combination");
            try {
                return (int)internalReader.call(buffer, offset, count);
            } catch (Exception ex) {
                throw new java.io.IOException(ex.getMessage(), ex);
            }
        } catch (Exception ex123) {
            return 0;
        }
    }

    private int bodyRead(byte[] buffer, int offset, int count) throws UnsupportedOperationException, IllegalArgumentException, Exception, java.io.IOException, IndexOutOfBoundsException {
        if (m_Crc == null) 
            throw new UnsupportedOperationException("Closed");
        if ((m_Entry == null) || (count <= 0)) 
            return 0;
        if ((offset + count) > buffer.length) 
            throw new IllegalArgumentException("Offset + count exceeds buffer size");
        boolean finished = false;
        switch (m_Method) { 
        case 8: // CompressionMethod.DEFLATED.value() 
            count = super.read(buffer, offset, count);
            if (count <= 0) {
                if (!inf.isFinished()) 
                    throw new Exception("Inflater not finished!");
                inputBuffer.setAvailable(inf.getRemainingInput());
                if (((m_Flags & 8)) == 0 && (((inf.getTotalIn() != csize && csize != 0xFFFFFFFF && csize != -1) || inf.getTotalOut() != m_Size))) 
                    throw new Exception(((("Size mismatch: " + (((Integer)csize).toString()) + ";") + (((Integer)m_Size).toString()) + " <-> ") + (((Integer)inf.getTotalIn()).toString()) + ";") + (((Integer)inf.getTotalOut()).toString()));
                inf.reset();
                finished = true;
            }
            break;
        case 0: // CompressionMethod.STORED.value() 
            if ((count > csize) && (csize >= 0)) 
                count = csize;
            if (count > 0) {
                count = inputBuffer.readClearTextBuffer(buffer, offset, count);
                if (count > 0) {
                    csize -= count;
                    m_Size -= count;
                }
            }
            if (csize == 0) 
                finished = true;
            else if (count < 0) 
                throw new Exception("EOF in stored block");
            break;
    }
    if (count > 0) 
        m_Crc.updateByBufEx(buffer, offset, count);
    if (finished) 
        this.completeCloseEntry(true);
    return count;
    }

    @Override
    public void close() throws java.io.IOException {
        internalReader = _ReadDataHandler_ReadingNotAvailable;
        m_Crc = null;
        m_Entry = null;
        super.close();
    }

    public static class ReadDataHandler_ReadingNotAvailable implements com.pullenti.unitext.internal.zip.ZipInputStream.ReadDataHandler {
    
        private com.pullenti.unitext.internal.zip.ZipInputStream m_Source;
    
        private ReadDataHandler_ReadingNotAvailable(com.pullenti.unitext.internal.zip.ZipInputStream src) {
            super();
            m_Source = src;
        }
    
        @Override
        public int call(byte[] destination, int offset, int count) {
            try {
                return m_Source.readingNotAvailable(destination, offset, count);
            } catch (Exception ex123) {
                return 0;
            }
        }
        public ReadDataHandler_ReadingNotAvailable() {
        }
    }


    private ReadDataHandler_ReadingNotAvailable _ReadDataHandler_ReadingNotAvailable;

    public static class ReadDataHandler_InitialRead implements com.pullenti.unitext.internal.zip.ZipInputStream.ReadDataHandler {
    
        private com.pullenti.unitext.internal.zip.ZipInputStream m_Source;
    
        private ReadDataHandler_InitialRead(com.pullenti.unitext.internal.zip.ZipInputStream src) {
            super();
            m_Source = src;
        }
    
        @Override
        public int call(byte[] destination, int offset, int count) {
            try {
                return m_Source.initialRead(destination, offset, count);
            } catch (Exception ex123) {
                return 0;
            }
        }
        public ReadDataHandler_InitialRead() {
        }
    }


    private ReadDataHandler_InitialRead _ReadDataHandler_InitialRead;

    public static class ReadDataHandler_ReadingNotSupported implements com.pullenti.unitext.internal.zip.ZipInputStream.ReadDataHandler {
    
        private com.pullenti.unitext.internal.zip.ZipInputStream m_Source;
    
        private ReadDataHandler_ReadingNotSupported(com.pullenti.unitext.internal.zip.ZipInputStream src) {
            super();
            m_Source = src;
        }
    
        @Override
        public int call(byte[] destination, int offset, int count) {
            try {
                return m_Source.readingNotSupported(destination, offset, count);
            } catch (Exception ex123) {
                return 0;
            }
        }
        public ReadDataHandler_ReadingNotSupported() {
        }
    }


    private ReadDataHandler_ReadingNotSupported _ReadDataHandler_ReadingNotSupported;

    public static class ReadDataHandler_BodyRead implements com.pullenti.unitext.internal.zip.ZipInputStream.ReadDataHandler {
    
        private com.pullenti.unitext.internal.zip.ZipInputStream m_Source;
    
        private ReadDataHandler_BodyRead(com.pullenti.unitext.internal.zip.ZipInputStream src) {
            super();
            m_Source = src;
        }
    
        @Override
        public int call(byte[] buffer, int offset, int count) {
            try {
                return m_Source.bodyRead(buffer, offset, count);
            } catch (Exception ex123) {
                return 0;
            }
        }
        public ReadDataHandler_BodyRead() {
        }
    }


    private ReadDataHandler_BodyRead _ReadDataHandler_BodyRead;
    public ZipInputStream() throws IllegalArgumentException, IndexOutOfBoundsException {
        super();
        if(_globalInstance == null) return;
        _ReadDataHandler_ReadingNotAvailable = new ReadDataHandler_ReadingNotAvailable(this);
        _ReadDataHandler_InitialRead = new ReadDataHandler_InitialRead(this);
        _ReadDataHandler_ReadingNotSupported = new ReadDataHandler_ReadingNotSupported(this);
        _ReadDataHandler_BodyRead = new ReadDataHandler_BodyRead(this);
    }
    public static ZipInputStream _globalInstance;
    
    static {
        try { _globalInstance = new ZipInputStream(); } 
        catch(Exception e) { }
    }
}
