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

const FileFormatClass = require("./../unitext/FileFormatClass");
const MiscHelper = require("./MiscHelper");
const FileFormat = require("./../unitext/FileFormat");

/**
 * Хелпер работы с форматами файлов
 */
class FileFormatsHelper {
    
    /**
     * Проанализировать формат файла по расширению и небольшому начальному фрагменту
     * @param fileExt расширение файла (может быть null)
     * @param fileContext начальный фрагмент файла (может быть null), но можно и весь файл целиком, если он уже в памяти.
     * @return формат
     */
    static analizeFormat(fileExt, fileContext) {
        const TextHelper = require("./TextHelper");
        if (fileExt !== null) {
            fileExt = fileExt.toLowerCase();
            if (!fileExt.startsWith(".")) 
                fileExt = "." + fileExt;
        }
        if (fileContext !== null && fileContext.length > 6) {
            if (fileContext[0] === (0xD0) && fileContext[1] === (0xCF)) {
                if ((fileExt === ".doc" || fileExt === ".docx" || fileExt === ".rtf") || fileExt === ".txt" || fileExt === ".dot") 
                    return FileFormat.DOC;
                if (fileExt === ".xls" || fileExt === ".xlsx" || fileExt === ".xlsb") 
                    return FileFormat.XLS;
                if (fileExt === ".msg") 
                    return FileFormat.MSG;
                if (fileExt === null || fileExt === ".") 
                    return FileFormat.DOC;
            }
            if (((String.fromCharCode(fileContext[0])) === '0' && (String.fromCharCode(fileContext[1])) === 'M' && (String.fromCharCode(fileContext[2])) === '8') && (String.fromCharCode(fileContext[3])) === 'R') {
                if (fileExt === ".msg") 
                    return FileFormat.MSG;
            }
            if (fileContext[0] === (1) && fileContext[1] === (0) && fileContext.length > 0x30) {
                if (((String.fromCharCode(fileContext[0x28])) === ' ' && (String.fromCharCode(fileContext[0x29])) === 'E' && (String.fromCharCode(fileContext[0x2A])) === 'M') && (String.fromCharCode(fileContext[0x2B])) === 'F') 
                    return FileFormat.EMF;
            }
            if (((fileContext[0] === (0x53) && fileContext[1] === (0x51) && fileContext[2] === (0x4C)) && fileContext[3] === (0x69) && fileContext[4] === (0x74)) && fileContext[5] === (0x65)) 
                return FileFormat.SQLIGHT;
            if (fileContext[0] === (0x50) && fileContext[1] === (0x4B)) {
                if ((fileExt === ".doc" || fileExt === ".docx" || fileExt === ".rtf") || fileExt === ".dotm") 
                    return FileFormat.DOCX;
                else if (fileExt === ".pptx") 
                    return FileFormat.PPTX;
                else if (fileExt === ".vsdx") 
                    return FileFormat.UNKNOWN;
                else if (fileExt === ".xlsb") 
                    return FileFormat.XLSB;
                else if (fileExt !== null && fileExt.startsWith(".xls")) 
                    return FileFormat.XLSX;
                else if (fileExt === ".odt") 
                    return FileFormat.ODT;
                else if (fileExt === ".ods") 
                    return FileFormat.ODS;
                else if (fileExt === ".epub") 
                    return FileFormat.EPUB;
                else if (fileExt === ".fb3") 
                    return FileFormat.FB3;
                else if (fileExt === ".xps") 
                    return FileFormat.XPS;
                else {
                    for (let k = 0; k < 5; k++) {
                        let tag = (k === 0 ? "workbook.bin" : (k === 1 ? "workbook.xml" : (k === 2 ? "word/document.xml" : (k === 3 ? "word/" : "[Content_Types].xml"))));
                        for (let i = 0; i < ((fileContext.length - tag.length)); i++) {
                            let j = 0;
                            for (j = 0; j < tag.length; j++) {
                                if (fileContext[i + j] !== (tag.charCodeAt(j))) 
                                    break;
                            }
                            if (j >= tag.length) 
                                return (k === 0 ? FileFormat.XLSB : (k === 1 ? FileFormat.XLSX : FileFormat.DOCX));
                        }
                    }
                    let odtTag = "oasis.opendocument.text";
                    for (let i = 0; i < (fileContext.length - odtTag.length); i++) {
                        let j = 0;
                        for (j = 0; j < odtTag.length; j++) {
                            if (fileContext[i + j] !== (odtTag.charCodeAt(j))) 
                                break;
                        }
                        if (j >= odtTag.length) 
                            return FileFormat.ODT;
                    }
                    return FileFormat.ZIP;
                }
            }
            if (fileContext[0] === (0x52) && fileContext[1] === (0x61) && fileContext[2] === (0x72)) 
                return FileFormat.RAR;
            if (fileContext[0] === (0x37) && fileContext[1] === (0x7A) && fileContext[2] === (0xDC)) 
                return FileFormat.ZIP7;
            if (fileContext[0] === (0x1F) && fileContext[1] === (0x8B)) 
                return FileFormat.GZIP;
            if ((fileContext[0] === (0x25) && fileContext[1] === (0x50) && fileContext[2] === (0x44)) && fileContext[3] === (0x46) && fileContext[4] === (0x2D)) 
                return FileFormat.PDF;
            if ((fileContext[0] === (0x7B) && fileContext[1] === (0x5C) && fileContext[2] === (0x72)) && fileContext[3] === (0x74) && fileContext[4] === (0x66)) 
                return FileFormat.RTF;
            if ((String.fromCharCode(fileContext[0])) === 'B' && (String.fromCharCode(fileContext[1])) === 'M') {
                if (fileExt === ".bmp" || fileExt === "." || fileExt === null) 
                    return FileFormat.BMP;
            }
            if ((String.fromCharCode(fileContext[0])) === 'I' && (String.fromCharCode(fileContext[1])) === 'I') {
                if ((fileExt === ".tif" || fileExt === ".tiff" || fileExt === ".") || fileExt === null) 
                    return FileFormat.TIF;
            }
            if ((String.fromCharCode(fileContext[1])) === 'P' && (String.fromCharCode(fileContext[2])) === 'N' && (String.fromCharCode(fileContext[3])) === 'G') {
                if (fileExt === ".png" || fileExt === "." || fileExt === null) 
                    return FileFormat.PNG;
            }
            if ((String.fromCharCode(fileContext[0])) === 'G' && (String.fromCharCode(fileContext[1])) === 'I' && (String.fromCharCode(fileContext[2])) === 'F') {
                if (fileExt === ".gif" || fileExt === "." || fileExt === null) 
                    return FileFormat.GIF;
            }
            if ((fileContext.length > 20 && (String.fromCharCode(fileContext[6])) === 'J' && (String.fromCharCode(fileContext[7])) === 'F') && (String.fromCharCode(fileContext[8])) === 'I') {
                if ((fileExt === ".jpg" || fileExt === ".jpeg" || fileExt === ".") || fileExt === null) 
                    return FileFormat.JPG;
            }
            if (((fileContext.length > 10 && fileContext[0] === (0) && fileContext[1] === (0)) && fileContext[2] === (0) && fileContext[3] === (0xC)) && fileContext[4] === (0x6A) && fileContext[5] === (0x50)) 
                return FileFormat.JPG2000;
            let enc = MiscHelper.decodeStringAscii(fileContext, 0, (fileContext.length < 1000 ? fileContext.length : 1000));
            if (fileContext[0] === (0xEF) && fileContext[1] === (0xBB) && fileContext[2] === (0xBF)) 
                enc = enc.substring(3);
            enc = enc.toUpperCase().trim();
            if (enc.startsWith("<HTML") || enc.startsWith("<!DOCTYPE HTML") || enc.startsWith("<DIV>")) 
                return FileFormat.HTML;
            if (enc.startsWith("AT&TFORM")) 
                return FileFormat.DJVU;
            if (Utils.startsWithString(enc, "<?xml", true)) {
                if (enc.includes("<FUNCTIONBOOK") || enc.includes("<FICTIONBOOK")) 
                    return FileFormat.FB2;
                if (enc.includes("<WORKBOOK ") || enc.includes("<?mso-application progid=\"Excel.Sheet\"".toUpperCase())) 
                    return FileFormat.XLSXML;
                if (enc.includes("<?mso-application progid=\"Word.Document\"".toUpperCase())) 
                    return FileFormat.DOCXML;
                if (enc.includes("<HTML")) 
                    return FileFormat.HTML;
            }
            if ((enc.startsWith("DELIVERED-TO:") || enc.startsWith("RECEIVED:") || enc.startsWith("RECEIVED-FROM:")) || enc.startsWith("DELIVERED:")) 
                return FileFormat.EML;
            let ii = enc.indexOf("CONTENT-TYPE:");
            if (ii > 0) {
                if (fileExt === ".eml") 
                    return FileFormat.EML;
                if (enc.indexOf("MULTIPART/RELATED") > ii) 
                    return FileFormat.MHT;
            }
            if (fileContext[0] === (0xFF) && fileContext[1] === (0xFE)) {
                let txt = MiscHelper.decodeStringUnicode(fileContext, 0, -1);
                if (txt.includes("<htm")) 
                    return FileFormat.HTML;
                return FileFormat.TXT;
            }
            if (fileContext[0] === (0xFE) && fileContext[1] === (0xFF)) {
                let txt = MiscHelper.decodeStringUnicodeBE(fileContext, 0, -1);
                if (txt.includes("<htm")) 
                    return FileFormat.HTML;
                return FileFormat.TXT;
            }
            if (Utils.startsWithString(enc, "<?xml", true)) 
                return FileFormat.XML;
            if (fileExt === ".") {
                let txt = TextHelper.readStringFromBytes(fileContext, false, false);
                if (txt !== null) 
                    return FileFormat.TXT;
            }
        }
        else {
            if (fileExt === ".doc" || fileExt === ".dot") 
                return FileFormat.DOC;
            if (fileExt === ".odt") 
                return FileFormat.ODT;
            if (fileExt === ".docx" || fileExt === ".dotx") 
                return FileFormat.DOCX;
            if (fileExt === ".pptx") 
                return FileFormat.PPTX;
            if (fileExt === ".zip") 
                return FileFormat.ZIP;
            if (fileExt === ".rar") 
                return FileFormat.RAR;
            if (fileExt === ".xlsx") 
                return FileFormat.XLSX;
            if (fileExt === ".xlsb") 
                return FileFormat.XLSB;
            if (fileExt === ".xls") 
                return FileFormat.XLS;
            if (fileExt === ".msg") 
                return FileFormat.MSG;
            if (fileExt === ".eml") 
                return FileFormat.EML;
            if (fileExt === ".djvu") 
                return FileFormat.DJVU;
        }
        if (fileExt === ".tar") 
            return FileFormat.TAR;
        if (fileExt === ".gzip" || fileExt === ".gz") 
            return FileFormat.GZIP;
        if (fileExt === ".7z") 
            return FileFormat.ZIP7;
        if (fileExt === ".xml") 
            return FileFormat.XML;
        if (fileExt === ".json") 
            return FileFormat.JSON;
        if (fileExt === ".pdf") 
            return FileFormat.PDF;
        if (fileExt === ".djvu") 
            return FileFormat.DJVU;
        if (fileExt === ".xls") 
            return FileFormat.XLS;
        if (fileExt === ".txt") 
            return FileFormat.TXT;
        if (fileExt === ".csv") 
            return FileFormat.CSV;
        if (fileExt === ".rtf") 
            return FileFormat.RTF;
        if (fileExt === ".htm" || fileExt === ".html") 
            return FileFormat.HTML;
        if (fileExt === ".doc") 
            return FileFormat.TXT;
        if (fileExt === ".mht" || fileExt === ".mhtml") 
            return FileFormat.MHT;
        if (fileExt === ".fb2") 
            return FileFormat.FB2;
        if (fileExt === ".fb3") 
            return FileFormat.FB3;
        if (fileExt === ".epub") 
            return FileFormat.EPUB;
        if (fileExt === ".bmp") 
            return FileFormat.BMP;
        if (fileExt === ".jpg" || fileExt === ".jpeg") 
            return FileFormat.JPG;
        if (fileExt === ".jp2") 
            return FileFormat.JPG2000;
        if (fileExt === ".gif") 
            return FileFormat.GIF;
        if (fileExt === ".png") 
            return FileFormat.PNG;
        if (fileExt === ".emf") 
            return FileFormat.EMF;
        if (fileExt === ".dbf") 
            return FileFormat.DBF;
        if (fileExt === ".tif" || fileExt === ".tiff") 
            return FileFormat.TIF;
        return FileFormat.UNKNOWN;
    }
    
    /**
     * Получить класс формата. Пригодится для определения типа файла.
     * @param frm формат
     * @return класс формата
     */
    static getFormatClass(frm) {
        if ((frm === FileFormat.ZIP || frm === FileFormat.ZIP7 || frm === FileFormat.RAR) || frm === FileFormat.TAR || frm === FileFormat.GZIP) 
            return FileFormatClass.ARCHIVE;
        if (((frm === FileFormat.BMP || frm === FileFormat.JPG || frm === FileFormat.JPG2000) || frm === FileFormat.PNG || frm === FileFormat.TIF) || frm === FileFormat.GIF || frm === FileFormat.EMF) 
            return FileFormatClass.IMAGE;
        if ((((frm === FileFormat.DOC || frm === FileFormat.DOCX || frm === FileFormat.XLS) || frm === FileFormat.XLSX || frm === FileFormat.PPTX) || frm === FileFormat.RTF || frm === FileFormat.ODT) || frm === FileFormat.DOCXML || frm === FileFormat.XLSXML) 
            return FileFormatClass.OFFICE;
        if (frm === FileFormat.PDF || frm === FileFormat.DJVU || frm === FileFormat.XPS) 
            return FileFormatClass.PAGELAYOUT;
        return FileFormatClass.UNDEFINED;
    }
    
    /**
     * Получить расширение для формата
     * @param frm формат
     * @return расширение с точкой (null - если не знает)
     */
    static getFormatExt(frm) {
        if (frm === FileFormat.DOC) 
            return ".doc";
        if (frm === FileFormat.DOCX) 
            return ".docx";
        if (frm === FileFormat.DOCXML) 
            return ".doc";
        if (frm === FileFormat.PPTX) 
            return ".pptx";
        if (frm === FileFormat.HTML) 
            return ".htm";
        if (frm === FileFormat.PDF) 
            return ".pdf";
        if (frm === FileFormat.RTF) 
            return ".rtf";
        if (frm === FileFormat.TXT) 
            return ".txt";
        if (frm === FileFormat.XLS || frm === FileFormat.XLSXML) 
            return ".xls";
        if (frm === FileFormat.XLSX) 
            return ".xlsx";
        if (frm === FileFormat.EPUB) 
            return ".epub";
        if (frm === FileFormat.FB2) 
            return ".fb2";
        if (frm === FileFormat.FB3) 
            return ".fb3";
        if (frm === FileFormat.CSV) 
            return ".csv";
        if (frm === FileFormat.MSG) 
            return ".msg";
        if (frm === FileFormat.EML) 
            return ".eml";
        if (frm === FileFormat.JPG) 
            return ".jpg";
        if (frm === FileFormat.JPG2000) 
            return ".jp2";
        if (frm === FileFormat.BMP) 
            return ".bmp";
        if (frm === FileFormat.GIF) 
            return ".gif";
        if (frm === FileFormat.TIF) 
            return ".tif";
        if (frm === FileFormat.PNG) 
            return ".png";
        if (frm === FileFormat.EMF) 
            return ".emf";
        if (frm === FileFormat.XML) 
            return ".xml";
        if (frm === FileFormat.ZIP) 
            return ".zip";
        if (frm === FileFormat.RAR) 
            return ".rar";
        if (frm === FileFormat.TAR) 
            return ".tar";
        if (frm === FileFormat.GZIP) 
            return ".gz";
        if (frm === FileFormat.ZIP7) 
            return ".7z";
        if (frm === FileFormat.XPS) 
            return ".xps";
        if (frm === FileFormat.DJVU) 
            return ".djvu";
        return null;
    }
    
    /**
     * Проанализировать формат файла или байтового потока.
     * @param fileName имя файла, может быть null, если есть содержимое
     * @param content содержимое, может быть null, тогда файл указывает на файл локальной файловой системы
     * @return 
     */
    static analizeFileFormat(fileName, content = null) {
        let head = new Uint8Array(10000);
        if (content !== null) {
            if (content.length < 2) 
                return FileFormat.UNKNOWN;
            for (let i = 0; ((i < head.length) && (i < content.length)); i++) {
                head[i] = content[i];
            }
        }
        else {
            if (!fs.existsSync(fileName) && fs.statSync(fileName).isFile()) 
                return FileFormat.UNKNOWN;
            let f = new FileStream(fileName, "r", false); 
            try {
                if (f.read(head, 0, head.length) < 2) 
                    return FileFormat.UNKNOWN;
            }
            finally {
                f.close();
            }
        }
        let ext = null;
        try {
            ext = (fileName === null ? "." : path.extname(fileName));
        } catch (ex) {
            ext = ".";
        }
        return FileFormatsHelper.analizeFormat(ext, head);
    }
}


module.exports = FileFormatsHelper