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

const FileFormat = require("./../unitext/FileFormat");
const FileInArchive = require("./FileInArchive");
const FileFormatsHelper = require("./FileFormatsHelper");
const ZipFile = require("./../unitext/internal/zip/ZipFile");
const UnitextService = require("./../unitext/UnitextService");
const MyZipFile = require("./../unitext/internal/misc/MyZipFile");

/**
 * Работа с архивами и сжатием
 */
class ArchiveHelper {
    
    /**
     * Заархивировать массив байт алгоритмом GZip
     * @param dat исходный массив
     * @return заархивированый массив
     */
    static compressGZip(dat) {
        return Utils.compressGzip(dat);
    }
    
    /**
     * Разархивировать байтовый массив алгоритмом GZip
     * @param zip архивированный массив
     * @return результат
     */
    static decompressGZip(zip) {
        return Utils.decompressGzip(zip);
    }
    
    /**
     * Заархивировать байтовый массив алгоритмом Zlib
     * @param dat исходный массив
     * @return заархивированый массив
     */
    static compressZlib(dat) {
        return Utils.compressZlib(dat);
    }
    
    /**
     * Разархивировать байтовый массив алгоритмом Zlib.
     * @param zip архивированный массив
     * @return результат
     */
    static decompressZlib(zip) {
        if (zip === null || (zip.length < 4)) 
            return null;
        return Utils.decompressZlib(zip);
    }
    
    /**
     * Заархивировать байтовый массив алгоритмом Deflate (без Zlib-заголовка)
     * @param dat исходный массив
     * @return заархивированый массив
     */
    static compressDeflate(dat) {
        let zip = null;
        zip = Utils.compressDeflate(dat);
        return zip;
    }
    
    /**
     * Разархивировать байтовый массив алгоритмом Deflate (без Zlib-заголовка)
     * @param zip архивированный массив
     * @return результат
     */
    static decompressDeflate(zip) {
        if (zip === null || (zip.length < 1)) 
            return null;
        return Utils.decompressDeflate(zip);
    }
    
    /**
     * Создать zip в потоке
     * @param res результирующий поток
     * @param files файлы для архивирования
     */
    static createZipStream(res, files) {
        let streams = new Array();
        try {
            let zip = ZipFile.createStream(res); 
            try {
                zip.beginUpdate0();
                for (const f of files) {
                    let mem = new MemoryStream(f.content);
                    zip.addStream(mem, f.key);
                    streams.push(mem);
                }
                zip.commitUpdate();
            }
            finally {
                zip.close();
            }
        } catch (ex) {
            throw new Error("ZIP Error: " + ex.toString());
        }
        for (const m of streams) {
            m.close();
        }
    }
    
    /**
     * Создать zip-файл
     * @param resName результирующее имя архивного файла
     * @param files файлы для архивирования
     */
    static createZipFile(resName, files) {
        try {
            let zip = ZipFile.createFile(resName); 
            try {
                zip.beginUpdate0();
                for (const f of files) {
                    zip.add(f, path.basename(f));
                }
                zip.commitUpdate();
            }
            finally {
                zip.close();
            }
        } catch (ex) {
            throw new Error("ZIP Error: " + ex.toString());
        }
    }
    
    /**
     * Разархивировать файлы из ZIP-архива
     * @param zipFile имя архива
     * @param outDir результирующая директория
     * @return список полных имён разархивированных файлов
     */
    static unzipFiles(zipFile, outDir) {
        let files = new Array();
        let zip = new MyZipFile(zipFile, null); 
        try {
            for (const e of zip.entries) {
                if (e.isDirectory || e.encrypted || e.uncompressDataSize === 0) 
                    continue;
                let dat = e.getData();
                if (dat === null) 
                    continue;
                let fname = path.join(outDir, path.basename(e.name));
                files.push(fname);
                Utils.writeAllBytes(fname, dat);
            }
        }
        finally {
            zip.close();
        }
        return files;
    }
    
    /**
     * Получить список файлов, содержащихся в архиве (архив может быть ZIP, RAR, TAR и некоторые другие)
     * @param fileName имя архива
     * @param content содержимое файла (если null, то fileName - ссылка на локальный файл)
     * @return список файлов в виде словаря {имя файла, размер файла} или null, если не поддержано
     */
    static getFileNamesFromArchive(fileName, content = null) {
        let frm = FileFormatsHelper.analizeFileFormat(fileName, content);
        let res = new Hashtable();
        if ((frm === FileFormat.ZIP || frm === FileFormat.DOCX || frm === FileFormat.XLSX) || frm === FileFormat.ODT) {
            let zip = new MyZipFile(fileName, content); 
            try {
                for (const e of zip.entries) {
                    if (e.isDirectory || e.encrypted || e.uncompressDataSize === 0) 
                        continue;
                    if (!res.containsKey(e.name)) 
                        res.put(e.name, e.compressDataSize);
                }
            }
            finally {
                zip.close();
            }
            return res;
        }
        try {
            if (UnitextService.EXTENSION !== null) 
                return UnitextService.EXTENSION.getFileNamesFromArchive(fileName, content);
        } catch (ex) {
        }
        return null;
    }
    
    /**
     * Разархивировать файл архива (архив может быть ZIP, RAR, TAR и некоторые другие). 
     * Сами файлы на диск не записываются - всё в памяти.
     * @param fileName имя архива
     * @param content содержимое файла (если null, то fileName - ссылка на локальный файл)
     * @return список файлов FileInArchive или null, если не поддержано
     */
    static getFilesFromArchive(fileName, content = null) {
        let frm = FileFormatsHelper.analizeFileFormat(fileName, content);
        let res = new Array();
        if ((frm === FileFormat.ZIP || frm === FileFormat.DOCX || frm === FileFormat.XLSX) || frm === FileFormat.ODT) {
            let zip = new MyZipFile(fileName, content); 
            try {
                for (const e of zip.entries) {
                    if (e.isDirectory || e.encrypted || e.uncompressDataSize === 0) 
                        continue;
                    let dat = e.getData();
                    res.push(FileInArchive._new648(e.name, dat));
                }
            }
            finally {
                zip.close();
            }
            return res;
        }
        try {
            if (UnitextService.EXTENSION !== null) 
                return UnitextService.EXTENSION.getFilesFromArchive(fileName, content);
        } catch (ex) {
        }
        return null;
    }
    
    /**
     * Извлечь файл из архива (архив может быть ZIP, RAR, TAR и некоторые другие)
     * @param fileName имя архива
     * @param content содержимое файла (если null, то fileName - ссылка на локальный файл)
     * @param internalName внутреннее имя (то, которое возвращалось функцией GetFileNamesFromArchive)
     * @return разархивированный байтовый поток
     */
    static getFileFromArchive(fileName, content, internalName) {
        let frm = FileFormatsHelper.analizeFileFormat(fileName, content);
        if (frm === FileFormat.ZIP || frm === FileFormat.DOCX) {
            let zip = new MyZipFile(fileName, content); 
            try {
                for (const e of zip.entries) {
                    if (e.isDirectory || e.encrypted || e.uncompressDataSize === 0) 
                        continue;
                    if (e.name !== internalName) 
                        continue;
                    return e.getData();
                }
            }
            finally {
                zip.close();
            }
        }
        try {
            if (UnitextService.EXTENSION !== null) 
                return UnitextService.EXTENSION.getFileFromArchive(fileName, content, internalName);
        } catch (ex) {
        }
        return null;
    }
}


module.exports = ArchiveHelper