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

const TreeColors = require("./TreeColors");
const DirectoryStreamIds = require("./DirectoryStreamIds");
const DirectoryObjectTypes = require("./DirectoryObjectTypes");
const CFHeader = require("./CFHeader");
const CFDirectoryEntry = require("./CFDirectoryEntry");

class ReaderUtils {
    
    static readHeader(s) {
        s.position = 0;
        let headerBytes = new Uint8Array(ReaderUtils.headerSize);
        let headerRead = s.read(headerBytes, 0, ReaderUtils.headerSize);
        if (headerRead !== ReaderUtils.headerSize) 
            throw new Error("Invalid header: eof of file");
        let header = new CFHeader();
        header.signature1 = Utils.bytesToObject(headerBytes, 0, 'uint', 4);
        header.signature2 = Utils.bytesToObject(headerBytes, 4, 'uint', 4);
        header.minorVersion = Utils.bytesToObject(headerBytes, 24, 'ushort', 2);
        header.majorVersion = Utils.bytesToObject(headerBytes, 26, 'ushort', 2);
        header.byteOrder = Utils.bytesToObject(headerBytes, 28, 'ushort', 2);
        header.sectorShift = Utils.bytesToObject(headerBytes, 30, 'ushort', 2);
        header.miniSectorShift = Utils.bytesToObject(headerBytes, 32, 'ushort', 2);
        header.reserved = ReaderUtils.getByteArrayPortion(headerBytes, 34, 6);
        header.directorySectorsCount = Utils.bytesToObject(headerBytes, 40, 'uint', 4);
        header.fATSectorsCount = Utils.bytesToObject(headerBytes, 44, 'uint', 4);
        header.firstDirectorySectorLocation = Utils.bytesToObject(headerBytes, 48, 'uint', 4);
        header.transactionSignatureNumber = Utils.bytesToObject(headerBytes, 52, 'uint', 4);
        header.miniStreamCutoffSize = Utils.bytesToObject(headerBytes, 56, 'uint', 4);
        header.firstMiniFATSectorLocation = Utils.bytesToObject(headerBytes, 60, 'uint', 4);
        header.miniFATSectorsCount = Utils.bytesToObject(headerBytes, 64, 'uint', 4);
        header.firstDIFATSectorLocation = Utils.bytesToObject(headerBytes, 68, 'uint', 4);
        header.dIFATSectorsCount = Utils.bytesToObject(headerBytes, 72, 'uint', 4);
        header.dIFAT = Array(ReaderUtils.headerDIFATSectorsCount).fill(0);
        for (let i = 0; i < ReaderUtils.headerDIFATSectorsCount; i++) {
            header.dIFAT[i] = Utils.bytesToObject(headerBytes, 76 + (i * 4), 'uint', 4);
        }
        return header;
    }
    
    static readFragment(s, streamOffset, length) {
        let data = new Uint8Array(length);
        s.position = streamOffset;
        let read = s.read(data, 0, length);
        if (read <= 0) 
            throw new Error("Unexcpected eof of file");
        return data;
    }
    
    static readUInt32(s, streamOffset) {
        return Utils.bytesToObject(ReaderUtils.readFragment(s, streamOffset, 4), 0, 'uint', 4);
    }
    
    static readArrayOfUInt32(s, streamOffset, count) {
        let array = Array(count).fill(0);
        let data = ReaderUtils.readFragment(s, streamOffset, 4 * count);
        for (let i = 0; i < count; i++) {
            array[i] = Utils.bytesToObject(data, 4 * i, 'uint', 4);
        }
        return array;
    }
    
    static readDirectoryEntry(s, streamOffset, isVersion3) {
        let data = ReaderUtils.readFragment(s, streamOffset, ReaderUtils.directoryEntrySize);
        let entry = new CFDirectoryEntry();
        entry.nameLength = Utils.bytesToObject(data, 64, 'ushort', 2);
        let nameLength = Math.min(32, Utils.intDiv(entry.nameLength, 2));
        entry.name = Array(nameLength).fill('\0');
        for (let i = 0; i < nameLength; i++) {
            entry.name[i] = Utils.bytesToObject(data, 2 * i, 'char', 2);
        }
        entry.objectType = data[66];
        entry.colorFlag = data[67];
        entry.leftSiblingID = Utils.bytesToObject(data, 68, 'uint', 4);
        entry.rightSiblingID = Utils.bytesToObject(data, 72, 'uint', 4);
        entry.childID = Utils.bytesToObject(data, 76, 'uint', 4);
        entry.stateBits = Utils.bytesToObject(data, 96, 'uint', 4);
        entry.startingSectorLocation = Utils.bytesToObject(data, 116, 'uint', 4);
        entry.streamSize = Utils.bytesToObject(data, 120, 'uint', 4);
        if (isVersion3) 
            entry.streamSize &= (0xFFFFFFFF);
        return entry;
    }
    
    static validateHeader(header) {
        if (Utils.compareUnsigned(header.signature1, CFHeader.defaultSignature1) != 0 || Utils.compareUnsigned(header.signature2, CFHeader.defaultSignature2) != 0) 
            throw new Error("Invalid header: signature");
        if (header.cLSID !== "00000000-0000-0000-0000-000000000000") {
        }
        if (Utils.compareUnsigned(header.minorVersion, 0x3E) != 0) {
        }
        if (Utils.compareUnsigned(header.majorVersion, 3) != 0 && Utils.compareUnsigned(header.majorVersion, 4) != 0) 
            throw new Error("Invalid header: Major version");
        if (Utils.compareUnsigned(header.byteOrder, 0xFFFE) != 0) 
            throw new Error("Invalid header: Byte order");
        if (Utils.compareUnsigned(header.majorVersion, 3) == 0 && Utils.compareUnsigned(header.sectorShift, 0x0009) != 0) 
            throw new Error("Invalid header: Sector shirt for v3");
        if (Utils.compareUnsigned(header.majorVersion, 4) == 0 && Utils.compareUnsigned(header.sectorShift, 0x000c) != 0) 
            throw new Error("Invalid header: Sector shirt for v4");
        if (Utils.compareUnsigned(header.miniSectorShift, 0x0006) != 0) 
            throw new Error("Invalid header: Mini sector shirt");
        if (((header.reserved[0] !== (0) || header.reserved[1] !== (0) || header.reserved[2] !== (0)) || header.reserved[3] !== (0) || header.reserved[4] !== (0)) || header.reserved[5] !== (0)) 
            throw new Error("Invalid header: Reserved");
        if (Utils.compareUnsigned(header.majorVersion, 3) == 0 && Utils.compareUnsigned(header.directorySectorsCount, 0) != 0) 
            throw new Error("Invalid header: Directory sectors for v3");
        if (Utils.compareUnsigned(header.miniStreamCutoffSize, 0x1000) != 0) 
            throw new Error("Invalid header: Mini stream cutoff size");
    }
    
    static validateDirectoryEntry(entry) {
        let len = entry.nameLength;
        if ((entry.name.length * 2) !== len || len === 0) 
            throw new Error("Invalid directory entry: name length");
        if ((entry.name[entry.name.length - 1].charCodeAt(0)) !== 0) 
            throw new Error("Invalid directory entry: name null termination");
        for (const ch of entry.name) {
            if ((ch === '/' || ch === '\\' || ch === ':') || ch === '!') 
                throw new Error("Invalid directory entry: illegal char in name");
        }
        if ((entry.objectType !== DirectoryObjectTypes.unknown && entry.objectType !== DirectoryObjectTypes.storage && entry.objectType !== DirectoryObjectTypes.stream) && entry.objectType !== DirectoryObjectTypes.rootStorage) 
            throw new Error("Invalid directory entry: object type");
        if (entry.colorFlag !== TreeColors.red && entry.colorFlag !== TreeColors.black) 
            throw new Error("Invalid directory entry: color flag");
        if (Utils.compareUnsigned(entry.leftSiblingID, DirectoryStreamIds.mAXREGSID) > 0 && Utils.compareUnsigned(entry.leftSiblingID, DirectoryStreamIds.nOSTREAM) != 0) 
            throw new Error("Invalid directory entry: left sibling");
        if (Utils.compareUnsigned(entry.rightSiblingID, DirectoryStreamIds.mAXREGSID) > 0 && Utils.compareUnsigned(entry.rightSiblingID, DirectoryStreamIds.nOSTREAM) != 0) 
            throw new Error("Invalid directory entry: right sibling");
        if (Utils.compareUnsigned(entry.childID, DirectoryStreamIds.mAXREGSID) > 0 && Utils.compareUnsigned(entry.childID, DirectoryStreamIds.nOSTREAM) != 0) 
            throw new Error("Invalid directory entry: child");
    }
    
    static getByteArrayPortion(source, offset, length) {
        let bytes = new Uint8Array(length);
        for(let iii = 0; iii < (length); iii++) bytes[(0) + iii] = source[(offset) + iii];
        return bytes;
    }
    
    static static_constructor() {
        ReaderUtils.headerDIFATSectorsCount = 109;
        ReaderUtils.directoryEntrySize = 128;
        ReaderUtils.headerSize = 512;
    }
}


ReaderUtils.static_constructor();

module.exports = ReaderUtils