/*
 * 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 class represents an entry in a zip archive.  This can be a file
// or a directory
// ZipFile and ZipInputStream will give you instances of this class as
// information about the members in an archive.  ZipOutputStream
// uses an instance of this class when creating an entry in a Zip file.
public class ZipEntry {

    public ZipEntry(String _name, int versionRequiredToExtract, int madeByInfo, CompressionMethod method) throws IllegalArgumentException, IllegalArgumentException, IndexOutOfBoundsException {
        if(_globalInstance == null) return;
        if (_name == null) 
            throw new IllegalArgumentException("name");
        if (_name.length() > 0xffff) 
            throw new IllegalArgumentException("Name is too long" + " " + "name");
        if ((versionRequiredToExtract != 0) && ((versionRequiredToExtract < 10))) 
            throw new IndexOutOfBoundsException("versionRequiredToExtract");
        this.setDateTime(java.time.LocalDateTime.now());
        this.m_Name = _name;
        this.m_VersionMadeBy = (short)madeByInfo;
        this.versionToExtract = (short)versionRequiredToExtract;
        this.m_Method = method;
    }

    public boolean getHasCrc() {
        return ((byte)((m_Known.value()) & (Known.CRC.value()))) != ((byte)0);
    }


    public boolean isCrypted() {
        return ((flags & 1)) != 0;
    }

    public boolean setCrypted(boolean value) {
        if (value) 
            flags |= 1;
        else 
            flags &= (~1);
        return value;
    }


    public boolean isUnicodeText() {
        return ((flags & (GeneralBitFlags.UNICODETEXT.value()))) != 0;
    }

    public boolean setUnicodeText(boolean value) {
        if (value) 
            flags |= (GeneralBitFlags.UNICODETEXT.value());
        else 
            flags &= (~(GeneralBitFlags.UNICODETEXT.value()));
        return value;
    }


    public byte getCryptoCheckValue() {
        return m_CryptoCheckValue;
    }

    public byte setCryptoCheckValue(byte value) {
        m_CryptoCheckValue = value;
        return value;
    }


    public int getZipFileIndex() {
        return m_ZipFileIndex;
    }

    public int setZipFileIndex(int value) {
        m_ZipFileIndex = value;
        return value;
    }


    public int getOffset() {
        return m_Ooffset;
    }

    public int setOffset(int value) {
        m_Ooffset = value;
        return value;
    }


    public int getExternalFileAttributes() {
        if (((byte)((m_Known.value()) & (Known.EXTERNALATTRIBUTES.value()))) == ((byte)0)) 
            return -1;
        else 
            return m_ExternalFileAttributes;
    }

    public int setExternalFileAttributes(int value) {
        m_ExternalFileAttributes = value;
        m_Known = Known.of((byte)((m_Known.value()) | (Known.EXTERNALATTRIBUTES.value())));
        return value;
    }


    public int getVersionMadeBy() {
        return ((Short.toUnsignedInt(m_VersionMadeBy)) & 0xff);
    }


    public boolean isDOSEntry() {
        return ((this.getHostSystem() == (HostSystemID.MSDOS.value())) || (this.getHostSystem() == (HostSystemID.WINDOWSNT.value())));
    }


    private boolean hasDosAttributes(int attributes) {
        boolean result = false;
        if (((byte)((m_Known.value()) & (Known.EXTERNALATTRIBUTES.value()))) != ((byte)0)) {
            if ((((this.getHostSystem() == (HostSystemID.MSDOS.value())) || (this.getHostSystem() == (HostSystemID.WINDOWSNT.value())))) && ((this.getExternalFileAttributes() & attributes)) == attributes) 
                result = true;
        }
        return result;
    }

    public int getHostSystem() {
        return (((Short.toUnsignedInt(m_VersionMadeBy)) >>> 8)) & 0xff;
    }

    public int setHostSystem(int value) {
        m_VersionMadeBy &= ((short)0xff);
        m_VersionMadeBy |= ((short)(((value & 0xff)) << 8));
        return value;
    }


    public int getVersion() throws Exception {
        if (Integer.compareUnsigned(versionToExtract, (short)0) != 0) 
            return Short.toUnsignedInt(versionToExtract);
        else {
            int result = 10;
            if (getAESKeySize() > 0) 
                result = ZipConstants.VERSION_AES;
            else if (getCentralHeaderRequiresZip64()) 
                result = ZipConstants.VERSIONZIP64;
            else if (CompressionMethod.DEFLATED == m_Method) 
                result = 20;
            else if (isDirectory() == true) 
                result = 20;
            else if (isCrypted() == true) 
                result = 20;
            else if (this.hasDosAttributes(0x08)) 
                result = 11;
            return result;
        }
    }


    public boolean canDecompress() throws Exception {
        return (this.getVersion() <= ZipConstants.VERSIONMADEBY) && ((((this.getVersion() == 10) || (this.getVersion() == 11) || (this.getVersion() == 20)) || (this.getVersion() == 45) || (this.getVersion() == 51))) && this.isCompressionMethodSupported();
    }


    public void forceZip64() {
        forceZip64_ = true;
    }

    public boolean isZip64Forced() {
        return forceZip64_;
    }

    public boolean getLocalHeaderRequiresZip64() {
        boolean result = forceZip64_;
        if (!result) {
            int trueCompressedSize = m_CompressedSize;
            if ((Integer.compareUnsigned(versionToExtract, (short)0) == 0) && isCrypted()) 
                trueCompressedSize += ZipConstants.CRYPTOHEADERSIZE;
            result = ((Integer.compareUnsigned(versionToExtract, (short)0) == 0) || ((Short.toUnsignedInt(versionToExtract)) >= ZipConstants.VERSIONZIP64));
        }
        return result;
    }


    public boolean getCentralHeaderRequiresZip64() {
        return getLocalHeaderRequiresZip64();
    }


    public int getDosTime() {
        if (((byte)((m_Known.value()) & (Known.TIME.value()))) == ((byte)0)) 
            return (int)0;
        else 
            return m_DosTime;
    }

    public int setDosTime(int value) {
        m_DosTime = value;
        m_Known = Known.of((byte)((m_Known.value()) | (Known.TIME.value())));
        return value;
    }


    public java.time.LocalDateTime getDateTime() {
        int sec = (int)Math.min(59, 2 * ((((int)m_DosTime) & 0x1f)));
        int min = (int)Math.min(59, ((((int)m_DosTime) >>> 5)) & 0x3f);
        int hrs = (int)Math.min(23, ((((int)m_DosTime) >>> 11)) & 0x1f);
        int mon = (int)Math.max(1, Math.min(12, (((((int)m_DosTime) >>> 21)) & 0xf)));
        int year = (int)(((((((int)m_DosTime) >>> 25)) & 0x7f)) + 1980);
        int day = Math.max(1, Math.min(com.pullenti.unisharp.Utils.daysInMonth((int)year, (int)mon), (((((int)m_DosTime) >>> 16)) & 0x1f)));
        return java.time.LocalDateTime.of((int)year, (int)mon, day, (int)hrs, (int)min, (int)sec);
    }

    public java.time.LocalDateTime setDateTime(java.time.LocalDateTime value) {
        int year = (int)value.getYear();
        int month = (int)value.getMonthValue();
        int day = (int)value.getDayOfMonth();
        int hour = (int)value.getHour();
        int minute = (int)value.getMinute();
        int second = (int)value.getSecond();
        if (Integer.compareUnsigned(year, (int)1980) < 0) {
            year = (int)1980;
            month = (int)1;
            day = (int)1;
            hour = (int)0;
            minute = (int)0;
            second = (int)0;
        }
        else if (Integer.compareUnsigned(year, (int)2107) > 0) {
            year = (int)2107;
            month = (int)12;
            day = (int)31;
            hour = (int)23;
            minute = (int)59;
            second = (int)59;
        }
        setDosTime((int)(((((((((int)year) - 1980)) & 0x7f)) << 25 | (((int)month) << 21) | (((int)day) << 16)) | (((int)hour) << 11) | (((int)minute) << 5)) | ((((int)second) >>> 1))));
        return value;
    }


    public String getName() {
        return m_Name;
    }


    public int getSize() {
        return (((byte)((m_Known.value()) & (Known.SIZE.value()))) != ((byte)0) ? m_Size : -1);
    }

    public int setSize(int value) {
        this.m_Size = value;
        this.m_Known = Known.of((byte)((this.m_Known.value()) | (Known.SIZE.value())));
        return value;
    }


    public int getCompressedSize() {
        return (((byte)((m_Known.value()) & (Known.COMPRESSEDSIZE.value()))) != ((byte)0) ? m_CompressedSize : -1);
    }

    public int setCompressedSize(int value) {
        this.m_CompressedSize = value;
        this.m_Known = Known.of((byte)((this.m_Known.value()) | (Known.COMPRESSEDSIZE.value())));
        return value;
    }


    public int getCrc() {
        return (((byte)((m_Known.value()) & (Known.CRC.value()))) != ((byte)0) ? m_Crc : (int)0);
    }

    public int setCrc(int value) {
        this.m_Crc = value;
        this.m_Known = Known.of((byte)((this.m_Known.value()) | (Known.CRC.value())));
        return value;
    }


    public boolean getCrcOk() {
        return ((byte)((m_Known.value()) & (Known.CRC.value()))) != ((byte)0);
    }


    public CompressionMethod get_CompressionMethod() {
        return m_Method;
    }

    public CompressionMethod set_CompressionMethod(CompressionMethod value) throws UnsupportedOperationException {
        if (!isCompressionMethodSupportedEx(value)) 
            throw new UnsupportedOperationException("Compression method not supported");
        this.m_Method = value;
        return value;
    }


    public CompressionMethod getCompressionMethodForHeader() throws Exception {
        return (this.getAESKeySize() > 0 ? CompressionMethod.WINZIPAES : m_Method);
    }


    public byte[] getExtraData() {
        return m_Extra;
    }

    public byte[] setExtraData(byte[] value) throws IndexOutOfBoundsException {
        if (value == null) 
            m_Extra = null;
        else {
            if (value.length > 0xffff) 
                throw new IndexOutOfBoundsException("value");
            m_Extra = new byte[value.length];
            for(int iii = 0; iii < (value.length); iii++) m_Extra[(0) + iii] = value[(0) + iii];
        }
        return value;
    }


    public int getAESKeySize() throws Exception {
        switch (_aesEncryptionStrength) { 
        case 0:
            return 0;
        case 1:
            return 128;
        case 2:
            return 192;
        case 3:
            return 256;
        default: 
            throw new Exception("Invalid AESEncryptionStrength " + (((Integer)_aesEncryptionStrength).toString()));
    }
    }

    public int setAESKeySize(int value) throws Exception {
        switch (value) { 
        case 0:
            _aesEncryptionStrength = 0;
            break;
        case 128:
            _aesEncryptionStrength = 1;
            break;
        case 256:
            _aesEncryptionStrength = 3;
            break;
        default: 
            throw new Exception("AESKeySize must be 0, 128 or 256: " + (((Integer)value).toString()));
    }
    return value;
    }


    public byte getAESEncryptionStrength() {
        return (byte)_aesEncryptionStrength;
    }


    public int getAESSaltLen() throws Exception {
        return getAESKeySize() / 16;
    }


    public int getAESOverheadSize() throws Exception {
        return 12 + getAESSaltLen();
    }


    public void processExtraData(boolean localHeader) throws Exception {
        ZipExtraData _extraData = new ZipExtraData(this.m_Extra);
        if (_extraData.find(0x0001)) {
            forceZip64_ = true;
            if (_extraData.getValueLength() < 4) 
                return;
            if (localHeader || (m_Size == Integer.MAX_VALUE)) 
                m_Size = _extraData.readLong();
            if (localHeader || (m_CompressedSize == Integer.MAX_VALUE)) 
                m_CompressedSize = _extraData.readLong();
            if (!localHeader && (m_Ooffset == Integer.MAX_VALUE)) 
                m_Ooffset = _extraData.readLong();
        }
        else if (((((Short.toUnsignedInt(versionToExtract)) & 0xff)) >= ZipConstants.VERSIONZIP64) && (((m_Size == Integer.MAX_VALUE) || (m_CompressedSize == Integer.MAX_VALUE)))) 
            throw new Exception("Zip64 Extended information required but is missing.");
        if (_extraData.find(10)) {
            if (_extraData.getValueLength() < 4) 
                throw new Exception("NTFS Extra data invalid");
            _extraData.readInt();
            while (_extraData.getUnreadCount() >= 4) {
                int ntfsTag = _extraData.readShort();
                int ntfsLength = _extraData.readShort();
                if (ntfsTag == 1) {
                    if (ntfsLength >= 24) {
                        int lastModification = _extraData.readLong();
                        int lastAccess = _extraData.readLong();
                        int createTime = _extraData.readLong();
                    }
                    break;
                }
                else 
                    _extraData.skip(ntfsLength);
            }
        }
        else if (_extraData.find(0x5455)) {
            int length = _extraData.getValueLength();
            int _flags = _extraData.readByte();
            if ((((_flags & 1)) != 0) && (length >= 5)) {
                int iTime = _extraData.readInt();
                setDateTime((java.time.LocalDateTime.of(1970, 1, 1, 0, 0, 0)).plusSeconds(iTime));
            }
        }
        if (m_Method == CompressionMethod.WINZIPAES) 
            this.processAESExtraData(_extraData);
    }

    private void processAESExtraData(ZipExtraData _extraData) throws Exception {
        if (_extraData.find(0x9901)) {
            versionToExtract = (short)ZipConstants.VERSION_AES;
            flags = flags | (GeneralBitFlags.STRONGENCRYPTION.value());
            int length = _extraData.getValueLength();
            if (length < 7) 
                throw new Exception("AES Extra Data Length " + (((Integer)length).toString()) + " invalid.");
            int ver = _extraData.readShort();
            int vendorId = _extraData.readShort();
            int encrStrength = _extraData.readByte();
            int actualCompress = _extraData.readShort();
            _aesVer = ver;
            _aesEncryptionStrength = encrStrength;
            m_Method = CompressionMethod.of(actualCompress);
        }
        else 
            throw new Exception("AES Extra Data missing");
    }

    public String getComment() {
        return m_Comment;
    }

    public String setComment(String value) throws IndexOutOfBoundsException {
        if ((value != null) && (value.length() > 0xffff)) 
            throw new IndexOutOfBoundsException("value" + " " + "cannot exceed 65535");
        m_Comment = value;
        return value;
    }


    public boolean isDirectory() {
        int nameLength = m_Name.length();
        boolean result = (((nameLength > 0) && (((m_Name.charAt(nameLength - 1) == '/') || (m_Name.charAt(nameLength - 1) == '\\'))))) || this.hasDosAttributes(16);
        return result;
    }


    public boolean isFile() {
        return !isDirectory() && !this.hasDosAttributes(8);
    }


    public boolean isCompressionMethodSupported() {
        return isCompressionMethodSupportedEx(this.get_CompressionMethod());
    }

    public Object clone() {
        return this;
    }

    @Override
    public String toString() {
        return (m_Name + ": " + m_CompressedSize);
    }

    public static boolean isCompressionMethodSupportedEx(CompressionMethod method) {
        return (method == CompressionMethod.DEFLATED) || (method == CompressionMethod.STORED);
    }

    private Known m_Known = Known.NONE;

    private int m_ExternalFileAttributes = -1;

    private short m_VersionMadeBy;

    private String m_Name;

    private int m_Size;

    private int m_CompressedSize;

    private short versionToExtract;

    private int m_Crc;

    private int m_DosTime;

    private CompressionMethod m_Method = CompressionMethod.DEFLATED;

    private byte[] m_Extra;

    private String m_Comment;

    public int flags;

    private int m_ZipFileIndex = -1;

    private int m_Ooffset;

    private boolean forceZip64_;

    private byte m_CryptoCheckValue;

    private int _aesVer;

    private int _aesEncryptionStrength;

    public static class Known implements Comparable<Known> {
    
        public static final Known NONE; // (byte)0
    
        public static final Known SIZE; // (byte)0x01
    
        public static final Known COMPRESSEDSIZE; // (byte)0x02
    
        public static final Known CRC; // (byte)0x04
    
        public static final Known TIME; // (byte)0x08
    
        public static final Known EXTERNALATTRIBUTES; // (byte)0x10
    
    
        public byte value() { return m_val; }
        private byte m_val;
        private String m_str;
        private Known(byte val, String str) { m_val = val; m_str = str; }
        @Override
        public String toString() {
            if(m_str != null) return m_str;
            return ((Byte)m_val).toString();
        }
        @Override
        public int hashCode() {
            return (int)m_val;
        }
        @Override
        public int compareTo(Known v) {
            if(m_val < v.m_val) return -1;
            if(m_val > v.m_val) return 1;
            return 0;
        }
        private static java.util.HashMap<Byte, Known> mapIntToEnum; 
        private static java.util.HashMap<String, Known> mapStringToEnum; 
        private static Known[] m_Values; 
        private static java.util.Collection<Byte> m_Keys; 
        public static Known of(byte val) {
            if (mapIntToEnum.containsKey(val)) return mapIntToEnum.get(val);
            Known item = new Known(val, ((Byte)val).toString());
            mapIntToEnum.put(val, item);
            mapStringToEnum.put(item.m_str.toUpperCase(), item);
            return item; 
        }
        public static Known of(String str) {
            str = str.toUpperCase(); 
            if (mapStringToEnum.containsKey(str)) return mapStringToEnum.get(str);
            return null; 
        }
        public static boolean isDefined(Object val) {
            if(val instanceof Byte) return m_Keys.contains((Byte)val); 
            return false; 
        }
        public static Known[] getValues() {
            return m_Values; 
        }
        static {
            mapIntToEnum = new java.util.HashMap<Byte, Known>();
            mapStringToEnum = new java.util.HashMap<String, Known>();
            NONE = new Known((byte)((byte)0), "NONE");
            mapIntToEnum.put(NONE.value(), NONE);
            mapStringToEnum.put(NONE.m_str.toUpperCase(), NONE);
            SIZE = new Known((byte)((byte)0x01), "SIZE");
            mapIntToEnum.put(SIZE.value(), SIZE);
            mapStringToEnum.put(SIZE.m_str.toUpperCase(), SIZE);
            COMPRESSEDSIZE = new Known((byte)((byte)0x02), "COMPRESSEDSIZE");
            mapIntToEnum.put(COMPRESSEDSIZE.value(), COMPRESSEDSIZE);
            mapStringToEnum.put(COMPRESSEDSIZE.m_str.toUpperCase(), COMPRESSEDSIZE);
            CRC = new Known((byte)((byte)0x04), "CRC");
            mapIntToEnum.put(CRC.value(), CRC);
            mapStringToEnum.put(CRC.m_str.toUpperCase(), CRC);
            TIME = new Known((byte)((byte)0x08), "TIME");
            mapIntToEnum.put(TIME.value(), TIME);
            mapStringToEnum.put(TIME.m_str.toUpperCase(), TIME);
            EXTERNALATTRIBUTES = new Known((byte)((byte)0x10), "EXTERNALATTRIBUTES");
            mapIntToEnum.put(EXTERNALATTRIBUTES.value(), EXTERNALATTRIBUTES);
            mapStringToEnum.put(EXTERNALATTRIBUTES.m_str.toUpperCase(), EXTERNALATTRIBUTES);
            java.util.Collection<Known> col = mapIntToEnum.values();
            m_Values = new Known[col.size()];
            col.toArray(m_Values);
            m_Keys = new java.util.ArrayList<Byte>(mapIntToEnum.keySet());
        }
    }

    public ZipEntry() {
    }
    public static ZipEntry _globalInstance;
    
    static {
        try { _globalInstance = new ZipEntry(); } 
        catch(Exception e) { }
    }
}
