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

// File System that base on Compound File structure. The class
// provides methods read the structure and the data of the
// Compound File.
// See Microsoft's [MS-CFB] "Compound File Binary File Format"
// reference for more details.
public class CompoundFileSystem implements AutoCloseable {

    private String m_Filename;

    private com.pullenti.unisharp.Stream m_BaseStream;

    private boolean m_Disposed;

    private CompoundFileStorage m_RootStorage = null;

    private CFHeader m_Header;

    public com.pullenti.unisharp.Stream getBaseStream() {
        return m_BaseStream;
    }


    public CFHeader getHeader() {
        return m_Header;
    }


    public boolean isVersion3() {
        return Integer.compareUnsigned(getHeader().majorVersion, (short)3) == 0;
    }


    public java.time.LocalDateTime getCreated() {
        return (com.pullenti.unisharp.Utils.isNullOrEmpty(m_Filename) ? java.time.LocalDateTime.MIN : com.pullenti.unisharp.Utils.getFileCreationTime(new java.io.File(m_Filename)));
    }


    public java.time.LocalDateTime getModified() {
        return (com.pullenti.unisharp.Utils.isNullOrEmpty(m_Filename) ? java.time.LocalDateTime.MIN : com.pullenti.unisharp.Utils.getFileModifiedTime(new java.io.File(m_Filename)));
    }


    public CompoundFileSystem(String filename, byte[] data, com.pullenti.unisharp.Stream stream) throws java.io.FileNotFoundException, java.io.IOException, Exception {
        if (stream != null) 
            this.m_BaseStream = stream;
        else if (data != null) 
            this.m_BaseStream = new com.pullenti.unisharp.MemoryStream(data);
        else {
            this.m_Filename = filename;
            this.m_BaseStream = new com.pullenti.unisharp.FileStream(filename, "r", "");
        }
        this.initialize();
    }

    @Override
    protected void finalize() throws java.io.IOException {
        this.dispose(false);
    }

    private void initialize() throws Exception, java.io.IOException {
        m_Header = ReaderUtils.readHeader(this.getBaseStream());
        ReaderUtils.validateHeader(m_Header);
    }

    @Override
    public void close() {
        try {
            this.dispose(true);
        } catch (Exception ex) {
        }
        ;
    }

    protected void dispose(boolean disposing) throws java.io.IOException {
        if (m_Disposed) 
            return;
        m_Disposed = true;
        m_BaseStream.close();
    }

    public CompoundFileStorage getRootStorage() throws Exception, java.io.IOException {
        if (m_RootStorage == null) {
            int rootDirectoryStreamID = 0;
            m_RootStorage = new CompoundFileStorage(this, (int)rootDirectoryStreamID, java.util.Arrays.asList(com.pullenti.unisharp.Utils.toIntegersArrayObj(new int[0])));
            this.validateRootStorage();
        }
        return m_RootStorage;
    }

    private void validateRootStorage() throws Exception {
        if (m_RootStorage.getObjectType() != CompoundFileObjectType.ROOT) {
            m_RootStorage = null;
            throw new Exception("Invalid root storage");
        }
    }

    public int getSectorOffset(int sectorNumber) {
        return ((((int)sectorNumber) + 1)) << m_Header.sectorShift;
    }

    public int getSectorSize() {
        return 1 << m_Header.sectorShift;
    }

    public int getMiniSectorSize() {
        return 1 << m_Header.miniSectorShift;
    }

    public int getMiniSectorOffset(int sectorNumber) throws Exception, java.io.IOException {
        return this.toPhysicalStreamOffset(this.getRootStorage().getEntry().startingSectorLocation, ((int)sectorNumber) << m_Header.miniSectorShift);
    }

    public int toPhysicalStreamOffset(int firstSector, int offsetWithinLogicalStream) throws Exception, java.io.IOException {
        int sectorIndex = (offsetWithinLogicalStream >> m_Header.sectorShift);
        if (sectorIndex == 0) 
            return this.getSectorOffset(firstSector) + offsetWithinLogicalStream;
        int sector = this.getStreamNextSector(firstSector, sectorIndex);
        int mask = (1 << this.getHeader().sectorShift) - 1;
        return this.getSectorOffset(sector) + ((offsetWithinLogicalStream & mask));
    }

    public int getStreamNextSector(int sector, int iterations) throws Exception, java.io.IOException {
        for (int i = 0; i < iterations; i++) {
            int fatSectorIndex = ((((int)sector) >>> (((Short.toUnsignedInt(this.getHeader().sectorShift)) - 2))));
            int mask = (1 << (((Short.toUnsignedInt(this.getHeader().sectorShift)) - 2))) - 1;
            int entryIndex = (((int)sector) & mask);
            int fatSector = this.getFATSector(fatSectorIndex);
            sector = ReaderUtils.readUInt32(this.getBaseStream(), this.getSectorOffset(fatSector) + (entryIndex << 2));
            if (Integer.compareUnsigned(sector, FATSectorIds.mAXREGSECT) > 0) 
                throw new Exception("Short chain");
        }
        return sector;
    }

    public int getMiniStreamNextSector(int sector, int iterations) throws Exception, java.io.IOException {
        for (int i = 0; i < iterations; i++) {
            int entryOffset = ((int)sector) << 2;
            if ((entryOffset >> getHeader().sectorShift) >= ((int)getHeader().miniFATSectorsCount)) 
                throw new Exception("Mini FAT sector index out of range");
            int offset = this.toPhysicalStreamOffset(this.getHeader().firstMiniFATSectorLocation, entryOffset);
            sector = ReaderUtils.readUInt32(this.getBaseStream(), offset);
            if (Integer.compareUnsigned(sector, FATSectorIds.mAXREGSECT) > 0) 
                throw new Exception("Short chain");
        }
        return sector;
    }

    private int getFATSector(int fatSectorIndex) throws Exception, java.io.IOException {
        if (fatSectorIndex < ReaderUtils.headerDIFATSectorsCount) 
            return this.getHeader().dIFAT[fatSectorIndex];
        else {
            int itemsPerPage = ((this.getSectorSize() >> 2)) - 1;
            int pageIndex = ((fatSectorIndex - ReaderUtils.headerDIFATSectorsCount)) / itemsPerPage;
            if (pageIndex >= ((int)getHeader().fATSectorsCount)) 
                throw new Exception("FAT sector index out of range");
            int currentSector = getHeader().firstDIFATSectorLocation;
            for (int i = 0; i < pageIndex; i++) {
                int offset = this.getSectorOffset(currentSector) + (itemsPerPage << 2);
                currentSector = ReaderUtils.readUInt32(this.getBaseStream(), offset);
            }
            int entryIndex = ((fatSectorIndex - ReaderUtils.headerDIFATSectorsCount)) % itemsPerPage;
            return ReaderUtils.readUInt32(this.getBaseStream(), this.getSectorOffset(currentSector) + (entryIndex << 2));
        }
    }
    public CompoundFileSystem() {
    }
}
