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

const DirectoryObjectTypes = require("./DirectoryObjectTypes");
const MiniFATStream = require("./MiniFATStream");
const FATStream = require("./FATStream");
const DirectoryStreamIds = require("./DirectoryStreamIds");
const ExtendedName = require("./ExtendedName");
const CompoundFileObjectType = require("./CompoundFileObjectType");
const ReaderUtils = require("./ReaderUtils");

// Compound File Storage.
class CompoundFileStorage {
    
    get system() {
        return this.m_System;
    }
    
    get entry() {
        return this.m_Entry;
    }
    
    get name() {
        return new ExtendedName(null, this.m_Entry.name, 0, this.m_Entry.name.length - 1);
    }
    
    get length() {
        return this.m_Entry.streamSize;
    }
    
    get objectType() {
        return CompoundFileObjectType.of(this.m_Entry.objectType);
    }
    
    constructor(_system, streamID, ancestors) {
        this.m_System = null;
        this.m_StreamID = 0;
        this.m_Entry = null;
        this.m_Ancestors = null;
        this.m_System = _system;
        this.m_StreamID = streamID;
        this.m_Ancestors = ancestors;
        this.initialize();
    }
    
    initialize() {
        let off = this.getStreamOffset();
        this.m_Entry = ReaderUtils.readDirectoryEntry(this.system.baseStream, off, this.system.isVersion3);
        ReaderUtils.validateDirectoryEntry(this.m_Entry);
    }
    
    getStreamOffset() {
        let offsetWithinLogicalStream = (this.m_StreamID) * ReaderUtils.directoryEntrySize;
        if (!this.system.isVersion3 && ((offsetWithinLogicalStream >> this.system.header.sectorShift)) >= (this.system.header.directorySectorsCount)) 
            throw new Error("Stream ID out of range");
        return this.system.toPhysicalStreamOffset(this.system.header.firstDirectorySectorLocation, offsetWithinLogicalStream);
    }
    
    appendChild(collection, streamID, processed, ancestors) {
        if (processed.containsKey(streamID)) 
            throw new Error("Circular structure: siblings");
        let item = new CompoundFileStorage(this.system, streamID, ancestors);
        processed.put(streamID, item);
        if (Utils.compareUnsigned(item.entry.leftSiblingID, DirectoryStreamIds.nOSTREAM) != 0) 
            this.appendChild(collection, item.entry.leftSiblingID, processed, ancestors);
        if (item.objectType !== CompoundFileObjectType.UNKNOWN) 
            collection.push(item);
        if (Utils.compareUnsigned(item.entry.rightSiblingID, DirectoryStreamIds.nOSTREAM) != 0) 
            this.appendChild(collection, item.entry.rightSiblingID, processed, ancestors);
    }
    
    getStorages() {
        let entries = new Array();
        let processed = new Hashtable();
        let childID = this.m_Entry.childID;
        if (Utils.compareUnsigned(childID, DirectoryStreamIds.nOSTREAM) != 0) {
            let newAncestors = new Array();
            newAncestors.push(this.m_StreamID);
            newAncestors.splice(newAncestors.length, 0, ...this.m_Ancestors);
            if (newAncestors.includes(childID)) 
                throw new Error("Circular structure: ancestors");
            this.appendChild(entries, childID, processed, this.m_Ancestors);
        }
        return entries;
    }
    
    findStorage(_name) {
        for (const storage of this.getStorages()) {
            if (ExtendedName.ooEq(storage.name, _name)) 
                return storage;
        }
        return null;
    }
    
    createStream() {
        if (this.m_Entry.objectType === DirectoryObjectTypes.stream) {
            if (Utils.compareUnsigned(this.m_Entry.streamSize, this.system.baseStream.length) > 0) 
                throw new Error("Stream length");
            if (Utils.compareUnsigned(this.m_Entry.streamSize, this.system.header.miniStreamCutoffSize) < 0) 
                return new MiniFATStream(this);
            else 
                return new FATStream(this);
        }
        else 
            throw new Error();
    }
}


module.exports = CompoundFileStorage