#ifndef UTILS_H
#define UTILS_H

#include "enums.h"
#include "QtZlib/zlib.h"
#include "qdir.h"
#include "qicon.h"

#include <QMetaEnum>
#include <QPainter>
#include <QCryptographicHash>

class Utils : public QObject {
    Q_OBJECT
public:
    enum AssetType {
        ASSET_NONE                          = 0x00,
        ASSET_RAWFILE                       = 0x01,
        ASSET_SCRIPT_PARSE_TREE             = 0x02,
        ASSET_FX                            = 0x03,
        ASSET_SOUND_CURVE                   = 0x04,
        ASSET_Animation                     = 0x05,
        ASSET_COLLISION_MAP                 = 0x06,
        ASSET_STRINGTABLE                   = 0x07,
        ASSET_XMODEL_SURFS                  = 0x08,
        ASSET_TECHNIQUE_SET                 = 0x09,
        ASSET_GFX_MAP                       = 0x11,
        ASSET_D3DBSP                        = 0x15,
        ASSET_GAME_MAP_SP                   = 0x17,
        ASSET_COL_MAP_SP                    = 0x18,
        ASSET_PHYSPRESET                    = 0x19,
        ASSET_DESTRUCTIBLE                  = 0x20,
        ASSET_LOCALIZE_ENTRY                = 0x21,
        ASSET_SHADER                        = 0x22,
        ASSET_MP_MAP                        = 0x23,
        ASSET_SP_MAP                        = 0x24,
        ASSET_SNDDRIVER_GLOBALS             = 0x26,
        ASSET_AI_TYPE                       = 0x27,
        ASSET_COMPUTE_SHADER_SET            = 0x29,
        ASSET_LIGHT_DESCRIPTION             = 0x30,
        ASSET_BIT_FIELD                     = 0x31,
        ASSET_STRUCTURED_TABLE              = 0x32,
        ASSET_LEADERBOARD_DEF               = 0x33,
        ASSET_DDL                           = 0x34,
        ASSET_KEY_VALUE_PAIRS               = 0x35,
        ASSET_SCRIPT_BUNDLE                 = 0x36,
        ASSET_SCRIPT_BUNDLE_LIST            = 0x37,
        ASSET_MAP_TABLE                     = 0x38,
        ASSET_MAP_TABLE_LOADING_IMAGES      = 0x39,
        ASSET_SURFACE_SOUND_DEF             = 0x40,
        ASSET_SURFACE_FX_TABLE              = 0x41,
        ASSET_RUMBLE                        = 0x42,
        ASSET_AIM_TABLE                     = 0x43,
        ASSET_MEDAL                         = 0x44,
        ASSET_MEDAL_TABLE                   = 0x45,
        ASSET_OBJECTIVE                     = 0x46,
        ASSET_OBJECTIVE_LIST                = 0x47,
        ASSET_LASER                         = 0x48,
        ASSET_BEAM                          = 0x49,
        ASSET_STREAMER_HINT                 = 0x50,
        ASSET_ANIM_SELECTOR_TABLE           = 0x51,
        ASSET_ANIM_MAPPING_TABLE            = 0x52,
        ASSET_ANIM_STATE_MACHINE            = 0x53,
        ASSET_BEHAVIOR_TREE                 = 0x54,
        ASSET_BEHAVIOR_STATE_MACHINE        = 0x55,
        ASSET_FOOTSTEP_TABLE                = 0x56,
        ASSET_ENTITY_FX_IMPACTS             = 0x57,
        ASSET_ENTITY_SOUND_IMPACTS          = 0x58,
        ASSET_VEHICLE_FX_DEF                = 0x59,
        ASSET_VEHICLE_SOUND_DEF             = 0x60,
        ASSET_VEHICLE                       = 0x61,
        ASSET_VEHICLE_TRACER                = 0x62,
        ASSET_PLAYER_SOUNDS_TABLE           = 0x63,
        ASSET_PLAYER_FX_TABLE               = 0x64,
        ASSET_SHARED_WEAPON_SOUNDS          = 0x65,
        ASSET_ATTACHMENT                    = 0x66,
        ASSET_ATTACHMENT_UNIQUE             = 0x67,
        ASSET_WEAPON_CAMO                   = 0x68,
        ASSET_CUSTOMIZATION_TABLE           = 0x69,
        ASSET_CUSTOMIZATION_TABLE_FEIMAGES  = 0x70,
        ASSET_CUSTOMIZATION_TABLE_COLOR     = 0x71,
        ASSET_PHYS_CONSTRAINTS              = 0x72,
        ASSET_DESTRUCTIBLE_DEF              = 0x73,
        ASSET_XMODEL_MESH                   = 0x74,
        ASSET_S_ANIM                        = 0x75,
        ASSET_FONT_ICON                     = 0x76,
        ASSET_CG_MEDIA_TABLE                = 0x77,
        ASSET_SHOCK_FILE                    = 0x78,
        ASSET_ZONE_FILE                     = 0x79,
        ASSET_FAST_FILE                     = 0x80,
        ASSET_SOUND_DRIVER_GLOBALS          = 0x81,
        ASSET_XMODELPIECES                  = 0x82,
        ASSET_XMODEL                        = 0x85,
        ASSET_MATERIAL                      = 0x86,
        ASSET_PIXELSHADER                   = 0x87,
        ASSET_IMAGE                         = 0x89,
        ASSET_SOUND                         = 0x90,
        ASSET_LOADED_SOUND                  = 0x92,
        ASSET_CLIPMAP                       = 0x93,
        ASSET_CLIPMAP_PVS                   = 0x94,
        ASSET_COMWORLD                      = 0x95,
        ASSET_GAMEWORLD_SP                  = 0x96,
        ASSET_GAMEWORLD_MP                  = 0x97,
        ASSET_MAP_ENTS                      = 0x98,
        ASSET_GFXWORLD                      = 0x99,
        ASSET_LIGHT_DEF                     = 0x100,
        ASSET_UI_MAP                        = 0x101,
        ASSET_FONT                          = 0x102,
        ASSET_MENULIST                      = 0x103,
        ASSET_MENU                          = 0x104,
        ASSET_WEAPON                        = 0x106,
        ASSET_IMPACT_FX                     = 0x109,
        ASSET_AITYPE                        = 0x110,
        ASSET_MPTYPE                        = 0x111,
        ASSET_CHARACTER                     = 0x112,
        ASSET_XMODELALIAS                   = 0x113,
        ASSET_COUNT                         = 0x116,
        ASSET_STRING                        = 0x117,
        ASSET_ASSETLIST                     = 0x118,
        ASSET_PHYSCOLLMAP                   = 0x119,
        ASSET_CLIPMAP_SP                    = 0x120,
        ASSET_CLIPMAP_MP                    = 0x121,
        ASSET_FXWORLD                       = 0x122,
        ASSET_LEADERBOARD                   = 0x123,
        ASSET_STRUCTURED_DATA_DEF           = 0x124,
        ASSET_TRACER                        = 0x125,
        ASSET_ADDON_MAP_ENTS                = 0x126,
        ASSET_GLASSWORLD                    = 0x127,
        ASSET_PATHDATA                      = 0x128,
        ASSET_VEHICLE_TRACK                 = 0x129,
        ASSET_SURFACE_FX                    = 0x130,
        ASSET_SCRIPTFILE                    = 0x131,
        ASSET_SOUND_SUBMIX                  = 0x132,
        ASSET_SOUND_EVENT                   = 0x133,
        ASSET_LPF_CURVE                     = 0x134,
        ASSET_REVERB_CURVE                  = 0x135,
        ASSET_GFXWORLD_TRANSIENT_ZONE       = 0x136,
        ASSET_ANIMCLASS                     = 0x137,
        ASSET_NET_CONST_STRINGS             = 0x138,
        ASSET_REVERB_PRESET                 = 0x139,
        ASSET_LUA_FILE                      = 0x140,
        ASSET_SCRIPTABLE                    = 0x141,
        ASSET_EQUIPMENT_SND_TABLE           = 0x142,
        ASSET_DOPPLER_PRESET                = 0x143,
        ASSET_SKELETON_SCRIPT               = 0x144,
        ASSET_PHYSCONSTRAINTS               = 0x145,
        ASSET_DESTRUCTIBLEDEF               = 0x146,
        ASSET_SOUND_PATCH                   = 0x147,
        ASSET_WEAPONDEF                     = 0x148,
        ASSET_WEAPON_VARIANT                = 0x149,
        ASSET_MPBODY                        = 0x150,
        ASSET_MPHEAD                        = 0x151,
        ASSET_PACK_INDEX                    = 0x152,
        ASSET_XGLOBALS                      = 0x153,
        ASSET_GLASSES                       = 0x154,
        ASSET_EMBLEMSET                     = 0x155
    };
    Q_ENUM(AssetType)

    static QString AssetTypeToStr(Utils::AssetType aAssetType) {
        const QMetaObject &mo = Utils::staticMetaObject;
        int index = mo.indexOfEnumerator("AssetType");
        QMetaEnum metaEnum = mo.enumerator(index);
        return QString::fromLatin1(metaEnum.valueToKey(aAssetType));
    }

    static bool ExportData(const QString aFileName, const QByteArray aData) {
        QDir workingDir = QDir::currentPath();
        workingDir.mkdir("exports");

        QFile testFile("exports/" + aFileName);
        if(!testFile.open(QIODevice::WriteOnly)) {
            return false;
        }
        testFile.write(aData);
        testFile.close();
        return true;
    }
    static quint8 ReverseBits(quint8 b) {
        b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
        b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
        b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
        return b;
    }
    static QIcon CreateAssetIcon(Utils::AssetType aAssetType, QColor color = QColor()) {
        const QString assetTypeStr = AssetTypeToStr(aAssetType);

        QString name;
        const QStringList parts = assetTypeStr.split('_').mid(1);
        foreach (const QString part, parts) {
            name += part[0];
        }
        if (parts.size() == 1) {
            name += parts.first()[1];
        }
        return CreateAssetIcon(name, color);
    }
    static QIcon CreateAssetIcon(const QString& name, QColor color = QColor()) {
        constexpr int iconSize = 32;
        constexpr int padding = 4;

        QImage result(iconSize, iconSize, QImage::Format_ARGB32);
        result.fill(Qt::transparent);

        if (!color.isValid()) {
            color = Utils::StringToColor(name);
        }

        QPainter painter(&result);
        painter.setRenderHint(QPainter::Antialiasing, true);
        painter.setRenderHint(QPainter::TextAntialiasing, true);
        painter.setRenderHint(QPainter::SmoothPixmapTransform, true);

        // Draw background
        QBrush brush(color);
        brush.setStyle(Qt::SolidPattern);
        painter.setBrush(color);
        painter.setPen(Qt::NoPen);
        painter.drawRoundedRect(0, 0, iconSize, iconSize, 4, 4);

        // Set base font
        QFont font("Heavitas");
        font.setPixelSize(iconSize); // Start large
        painter.setFont(font);

        // Adjust font size to fit text width (only reduce, not increase)
        QFontMetrics fm(font);
        int textWidth = fm.horizontalAdvance(name);
        while (textWidth > iconSize - 2 * padding && font.pixelSize() > 1) {
            font.setPixelSize(font.pixelSize() - 1);
            painter.setFont(font);
            fm = QFontMetrics(font);
            textWidth = fm.horizontalAdvance(name);
        }

        // Calculate vertical scaling factor
        qreal scaleY = 1.2 * iconSize / fm.height();

        // Apply transform: scale vertically, center align
        painter.save();
        painter.translate(iconSize / 2.0, iconSize / 2.0);
        painter.scale(1.0, scaleY);
        painter.translate(-iconSize / 2.0, -iconSize / 2.0);

        QRect textRect(0, 0, iconSize, iconSize);

        // Draw stroke
        painter.setPen(Qt::black);
        for (int dx = 0; dx <= 1; ++dx) {
            for (int dy = 0; dy <= 1; ++dy) {
                if (dx || dy)
                    painter.drawText(textRect.translated(dx, dy), Qt::AlignCenter, name);
            }
        }

        // Draw main text
        painter.setPen(Qt::white);
        painter.drawText(textRect, Qt::AlignCenter, name);
        painter.restore();

        // Debug output
        QDir().mkdir(QDir().absoluteFilePath(".") + "/icons/");
        result.save(QDir().absoluteFilePath(".") + "/icons/" + name + ".png");

        return QIcon(QPixmap::fromImage(result));
    }

    static QIcon CreateGameIcon(const int gameNum, QColor color = QColor()) {
        constexpr int size = 32;
        constexpr int padding = 4; // pixels of padding on all sides
        const int contentSize = size - 2 * padding;

        QImage result(size, size, QImage::Format_ARGB32);
        result.fill(Qt::transparent);

        if (!color.isValid()) {
            color = Utils::StringToColor(QString("COD%1").arg(gameNum));
        }

        QPainter painter(&result);
        painter.setRenderHint(QPainter::Antialiasing, false);
        painter.setRenderHint(QPainter::TextAntialiasing, false);
        painter.setRenderHint(QPainter::SmoothPixmapTransform, false);

        // Draw background
        painter.setBrush(color);
        painter.setPen(Qt::NoPen);
        painter.drawRoundedRect(0, 0, size, size, 4, 4);

        // === Font Setup ===
        QFont codFont("Heavitas");
        codFont.setPixelSize(contentSize * 0.40);

        QFont numFont("Heavitas");
        numFont.setPixelSize(contentSize);

        // === Layout Areas (inside padding) ===
        const QRect contentRect(padding, padding, contentSize, contentSize);
        const int col1 = contentRect.left();
        const int col2 = contentRect.left() + contentRect.width() / 3;

        const int thirdH = contentRect.height() / 3;
        const QRect codRects[] = {
            QRect(col1, 2 + contentRect.top(), contentRect.width() / 3, thirdH),
            QRect(col1, contentRect.top() + thirdH, contentRect.width() / 3, thirdH),
            QRect(col1, -2 + contentRect.top() + 2 * thirdH, contentRect.width() / 3, thirdH),
        };

        const QRect numRect(col2, contentRect.top(), contentRect.width() * 2 / 3, contentRect.height());

        const QString codLetters[] = { "C", "O", "D" };
        const QString numText = QString::number(gameNum);

        // === Stroke pass ===
        for (int dx = -1; dx <= 2; ++dx) {
            for (int dy = -1; dy <= 2; ++dy) {
                if (dx == 0 && dy == 0) continue;

                painter.setPen(Qt::black);
                painter.setFont(codFont);
                for (int i = 0; i < 3; ++i)
                    painter.drawText(codRects[i].translated(dx, dy), Qt::AlignCenter, codLetters[i]);

                painter.setFont(numFont);
                painter.drawText(numRect.translated(dx, dy), Qt::AlignCenter, numText);
            }
        }

        // === Fill pass ===
        painter.setPen(Qt::white);
        painter.setFont(codFont);
        for (int i = 0; i < 3; ++i)
            painter.drawText(codRects[i], Qt::AlignCenter, codLetters[i]);

        painter.setFont(numFont);
        painter.drawText(numRect, Qt::AlignCenter, numText);

        // Save & return icon
        QDir().mkdir(QDir().absoluteFilePath(".") + "/icons/");
        result.save(QDir().absoluteFilePath(".") + QString("/icons/COD%1.png").arg(gameNum));

        return QIcon(QPixmap::fromImage(result));
    }


    static QColor StringToColor(const QString& str) {
        // 1. Hash the string using Qt's built-in hash (MD5, SHA1, or any)
        QByteArray hash = QCryptographicHash::hash(str.toUtf8(), QCryptographicHash::Md5);

        // 2. Use first 3 bytes of hash for RGB
        //    This guarantees same string = same color every time
        int r = static_cast<unsigned char>(hash[0]);
        int g = static_cast<unsigned char>(hash[1]);
        int b = static_cast<unsigned char>(hash[2]);

        // 3. Optionally adjust brightness or saturation to avoid too dark/light colors
        QColor color(r, g, b);
        if (color.value() < 128) {  // brighten if too dark
            color = color.lighter(150);
        }

        return color;
    }
    static bool ReadUntilString(QDataStream* stream, const QString& targetString) {
        if (!stream || targetString.isEmpty()) {
            return false; // Invalid input
        }

        QByteArray buffer;
        QByteArray targetBytes = targetString.toUtf8(); // Handle multibyte characters
        const int targetLength = targetBytes.size();
        qDebug() << targetBytes << targetLength;

        // Read as unsigned bytes to handle all possible values (0-255)
        unsigned char byte;
        while (!stream->atEnd()) {
            // Read one byte at a time
            *stream >> byte;
            buffer.append(static_cast<char>(byte)); // Append as char for QByteArray

            // Keep buffer size limited to the target length
            if (buffer.size() > targetLength) {
                buffer.remove(0, 1);
            }

            // Check if the buffer matches the target string in raw bytes
            if (buffer == targetBytes) {
                // Backup to the start of the matched string
                stream->device()->seek(stream->device()->pos() - targetLength);
                return true;
            }
        }

        // Target string not found
        return false;
    }

    static bool ReadUntilHex(QDataStream* stream, const QString& hexString) {
        if (!stream || hexString.isEmpty() || hexString.size() % 2 != 0) {
            return false; // Invalid input
        }

        // Convert hex string to byte array
        QByteArray targetBytes = QByteArray::fromHex(hexString.toUtf8());
        const int targetLength = targetBytes.size();

        QByteArray buffer;
        unsigned char byte;

        while (!stream->atEnd()) {
            // Read one byte at a time
            *stream >> byte;
            buffer.append(static_cast<char>(byte)); // Append as char for QByteArray

            // Keep buffer size limited to the target length
            if (buffer.size() > targetLength) {
                buffer.remove(0, 1);
            }

            // Check if the buffer matches the target byte sequence
            if (buffer == targetBytes) {
                // Backup to the start of the matched sequence
                stream->device()->seek(stream->device()->pos() - targetLength);
                return true;
            }
        }

        // Target sequence not found
        return false;
    }

    /*
    LumpTypeToString()

    Convert BSP lump type to desc string
*/
    static QString LumpTypeToString(LUMP_TYPE aLumpType) {
        switch (aLumpType) {
        case LUMP_MATERIALS: return "LUMP_MATERIALS";
        case LUMP_LIGHTBYTES: return "LUMP_LIGHTBYTES";
        case LUMP_LIGHTGRIDENTRIES: return "LUMP_LIGHTGRIDENTRIES";
        case LUMP_LIGHTGRIDCOLORS: return "LUMP_LIGHTGRIDCOLORS";
        case LUMP_PLANES: return "LUMP_PLANES";
        case LUMP_BRUSHSIDES: return "LUMP_BRUSHSIDES";
        case LUMP_BRUSHSIDEEDGECOUNTS: return "LUMP_BRUSHSIDEEDGECOUNTS";
        case LUMP_BRUSHEDGES: return "LUMP_BRUSHEDGES";
        case LUMP_BRUSHES: return "LUMP_BRUSHES";
        case LUMP_TRIANGLES: return "LUMP_TRIANGLES";
        case LUMP_DRAWVERTS: return "LUMP_DRAWVERTS";
        case LUMP_DRAWINDICES: return "LUMP_DRAWINDICES";
        case LUMP_CULLGROUPS: return "LUMP_CULLGROUPS";
        case LUMP_CULLGROUPINDICES: return "LUMP_CULLGROUPINDICES";
        case LUMP_OBSOLETE_1: return "LUMP_OBSOLETE_1";
        case LUMP_OBSOLETE_2: return "LUMP_OBSOLETE_2";
        case LUMP_OBSOLETE_3: return "LUMP_OBSOLETE_3";
        case LUMP_OBSOLETE_4: return "LUMP_OBSOLETE_4";
        case LUMP_OBSOLETE_5: return "LUMP_OBSOLETE_5";
        case LUMP_PORTALVERTS: return "LUMP_PORTALVERTS";
        case LUMP_OBSOLETE_6: return "LUMP_OBSOLETE_6";
        case LUMP_UINDS: return "LUMP_UINDS";
        case LUMP_BRUSHVERTSCOUNTS: return "LUMP_BRUSHVERTSCOUNTS";
        case LUMP_BRUSHVERTS: return "LUMP_BRUSHVERTS";
        case LUMP_AABBTREES: return "LUMP_AABBTREES";
        case LUMP_CELLS: return "LUMP_CELLS";
        case LUMP_PORTALS: return "LUMP_PORTALS";
        case LUMP_NODES: return "LUMP_NODES";
        case LUMP_LEAFS: return "LUMP_LEAFS";
        case LUMP_LEAFBRUSHES: return "LUMP_LEAFBRUSHES";
        case LUMP_LEAFSURFACES: return "LUMP_LEAFSURFACES";
        case LUMP_COLLISIONVERTS: return "LUMP_COLLISIONVERTS";
        case LUMP_COLLISIONTRIS: return "LUMP_COLLISIONTRIS";
        case LUMP_COLLISIONEDGEWALKABLE: return "LUMP_COLLISIONEDGEWALKABLE";
        case LUMP_COLLISIONBORDERS: return "LUMP_COLLISIONBORDERS";
        case LUMP_COLLISIONPARTITIONS: return "LUMP_COLLISIONPARTITIONS";
        case LUMP_COLLISIONAABBS: return "LUMP_COLLISIONAABBS";
        case LUMP_MODELS: return "LUMP_MODELS";
        case LUMP_VISIBILITY: return "LUMP_VISIBILITY";
        case LUMP_ENTITIES: return "LUMP_ENTITIES";
        case LUMP_PATHCONNECTIONS: return "LUMP_PATHCONNECTIONS";
        case LUMP_REFLECTION_PROBES: return "LUMP_REFLECTION_PROBES";
        case LUMP_VERTEX_LAYER_DATA: return "LUMP_VERTEX_LAYER_DATA";
        case LUMP_PRIMARY_LIGHTS: return "LUMP_PRIMARY_LIGHTS";
        case LUMP_LIGHTGRIDHEADER: return "LUMP_LIGHTGRIDHEADER";
        case LUMP_LIGHTGRIDROWS: return "LUMP_LIGHTGRIDROWS";
        case LUMP_OBSOLETE_10: return "LUMP_OBSOLETE_10";
        case LUMP_UNLAYERED_TRIANGLES: return "LUMP_UNLAYERED_TRIANGLES";
        case LUMP_UNLAYERED_DRAWVERTS: return "LUMP_UNLAYERED_DRAWVERTS";
        case LUMP_UNLAYERED_DRAWINDICES: return "LUMP_UNLAYERED_DRAWINDICES";
        case LUMP_UNLAYERED_CULLGROUPS: return "LUMP_UNLAYERED_CULLGROUPS";
        case LUMP_UNLAYERED_AABBTREES: return "LUMP_UNLAYERED_AABBTREES";
        case LUMP_WATERHEADER: return "LUMP_WATERHEADER";
        case LUMP_WATERCELLS: return "LUMP_WATERCELLS";
        case LUMP_WATERCELLDATA: return "LUMP_WATERCELLDATA";
        case LUMP_BURNABLEHEADER: return "LUMP_BURNABLEHEADER";
        case LUMP_BURNABLECELLS: return "LUMP_BURNABLECELLS";
        case LUMP_BURNABLECELLDATA: return "LUMP_BURNABLECELLDATA";
        case LUMP_SIMPLELIGHTMAPBYTES: return "LUMP_SIMPLELIGHTMAPBYTES";
        case LUMP_LODCHAINS: return "LUMP_LODCHAINS";
        case LUMP_LODINFOS: return "LUMP_LODINFOS";
        case LUMP_LODSURFACES: return "LUMP_LODSURFACES";
        case LUMP_LIGHTREGIONS: return "LUMP_LIGHTREGIONS";
        case LUMP_LIGHTREGION_HULLS: return "LUMP_LIGHTREGION_HULLS";
        case LUMP_LIGHTREGION_AXES: return "LUMP_LIGHTREGION_AXES";
        case LUMP_WIILIGHTGRID: return "LUMP_WIILIGHTGRID";
        case LUMP_LIGHTGRID2D_LIGHTS: return "LUMP_LIGHTGRID2D_LIGHTS";
        case LUMP_LIGHTGRID2D_INDICES: return "LUMP_LIGHTGRID2D_INDICES";
        case LUMP_LIGHTGRID2D_POINTS: return "LUMP_LIGHTGRID2D_POINTS";
        case LUMP_LIGHTGRID2D_CELLS: return "LUMP_LIGHTGRID2D_CELLS";
        case LUMP_LIGHT_CORONAS: return "LUMP_LIGHT_CORONAS";
        case LUMP_SHADOWMAP_VOLUMES: return "LUMP_SHADOWMAP_VOLUMES";
        case LUMP_SHADOWMAP_VOLUME_PLANES: return "LUMP_SHADOWMAP_VOLUME_PLANES";
        case LUMP_EXPOSURE_VOLUMES: return "LUMP_EXPOSURE_VOLUMES";
        case LUMP_EXPOSURE_VOLUME_PLANES: return "LUMP_EXPOSURE_VOLUME_PLANES";
        case LUMP_OCCLUDERS: return "LUMP_OCCLUDERS";
        case LUMP_OUTDOORBOUNDS: return "LUMP_OUTDOORBOUNDS";
        case LUMP_HERO_ONLY_LIGHTS: return "LUMP_HERO_ONLY_LIGHTS";
        default: return "Unknown Lump Type";
        }
        return "Unknown Lump Type";
    }

    static QString ZLibErrorToString(int result) {
        switch (result) {
        case Z_ERRNO:
            return "Encountered file operation error!";
        case Z_STREAM_ERROR:
            return "Stream was not initialized properly!";
        case Z_DATA_ERROR:
            return "The input data was corrupted or incomplete!";
        case Z_MEM_ERROR:
            return "There was not enough memory!";
        case Z_BUF_ERROR:
            return "There was not enough room in the output buffer!";
        case Z_VERSION_ERROR:
            return "Linked libraries are out of date!";
        }
        return "";
    }

    static quint32 PadInt4(quint32 size) {
        return (size + 3) & ~3; // Align size to 4 bytes
    }

    static quint32 PaddingSize(quint32 size) {
        return PadInt4(size) - size;
    }

    static QString CompanyEnumToStr(FF_COMPANY aCompany) {
        switch (aCompany) {
        case COMPANY_NONE:
            return "None";
        case COMPANY_INFINITY_WARD:
            return "Infinity Ward";
        case COMPANY_TREYARCH:
            return "Treyarch";
        case COMPANY_SLEDGEHAMMER:
            return "Sledgehammer";
        case COMPANY_NEVERSOFT:
            return "Neversoft";
        }
        return "None";
    }

    static QString FileTypeEnumToStr(FF_FILETYPE aFileType) {
        switch (aFileType) {
        case FILETYPE_NONE:
            return "None";
        case FILETYPE_FAST_FILE:
            return "Fast File";
        }
        return "None";
    }

    static QString SignageEnumToStr(FF_SIGNAGE aSignage) {
        switch (aSignage) {
        case SIGNAGE_NONE:
            return "None";
        case SIGNAGE_SIGNED:
            return "Signed";
        case SIGNAGE_UNSIGNED:
            return "Unsigned";
        }
        return "None";
    }

    static QString MenuVAlignToStr(MENU_V_ALIGNMENT align) {
        if (align == VERTICAL_ALIGN_SUBTOP) {
            return "VERTICAL_ALIGN_SUBTOP";
        } else if (align == VERTICAL_ALIGN_TOP) {
            return "VERTICAL_ALIGN_TOP";
        } else if (align == VERTICAL_ALIGN_CENTER) {
            return "VERTICAL_ALIGN_CENTER";
        } else if (align == VERTICAL_ALIGN_BOTTOM) {
            return "VERTICAL_ALIGN_BOTTOM";
        } else if (align == VERTICAL_ALIGN_FULLSCREEN) {
            return "VERTICAL_ALIGN_FULLSCREEN";
        } else if (align == VERTICAL_ALIGN_NOSCALE) {
            return "VERTICAL_ALIGN_NOSCALE";
        } else if (align == VERTICAL_ALIGN_TO480) {
            return "VERTICAL_ALIGN_TO480";
        } else if (align == VERTICAL_ALIGN_CENTER_SAFEAREA) {
            return "VERTICAL_ALIGN_CENTER_SAFEAREA";
        } else if (align == VERTICAL_ALIGN_MAX) {
            return "VERTICAL_ALIGN_CENTER_SAFEAREA";
        } else if (align == VERTICAL_ALIGN_DEFAULT) {
            return "VERTICAL_ALIGN_SUBTOP";
        }
        return "VERTICAL_ALIGN_SUBTOP";
    }

    static QString MenuHAlignToStr(MENU_H_ALIGNMENT align) {
        if (align == HORIZONTAL_ALIGN_SUBLEFT) {
            return "HORIZONTAL_ALIGN_SUBLEFT";
        } else if (align == HORIZONTAL_ALIGN_LEFT) {
            return "HORIZONTAL_ALIGN_LEFT";
        } else if (align == HORIZONTAL_ALIGN_CENTER) {
            return "HORIZONTAL_ALIGN_CENTER";
        } else if (align == HORIZONTAL_ALIGN_RIGHT) {
            return "HORIZONTAL_ALIGN_RIGHT";
        } else if (align == HORIZONTAL_ALIGN_FULLSCREEN) {
            return "HORIZONTAL_ALIGN_FULLSCREEN";
        } else if (align == HORIZONTAL_ALIGN_NOSCALE) {
            return "HORIZONTAL_ALIGN_NOSCALE";
        } else if (align == HORIZONTAL_ALIGN_TO640) {
            return "HORIZONTAL_ALIGN_TO640";
        } else if (align == HORIZONTAL_ALIGN_CENTER_SAFEAREA) {
            return "HORIZONTAL_ALIGN_CENTER_SAFEAREA";
        } else if (align == HORIZONTAL_ALIGN_MAX) {
            return "HORIZONTAL_ALIGN_CENTER_SAFEAREA";
        } else if (align == HORIZONTAL_ALIGN_DEFAULT) {
            return "HORIZONTAL_ALIGN_SUBLEFT";
        }
        return "HORIZONTAL_ALIGN_SUBLEFT";
    }

    static QColor ColorFromNormalized(float r, float g, float b, float a) {
        // Ensure values are clamped between 0 and 1
        r = qBound(0.0f, r, 1.0f);
        g = qBound(0.0f, g, 1.0f);
        b = qBound(0.0f, b, 1.0f);
        a = qBound(0.0f, a, 1.0f);

        // Convert to 0-255 scale
        int red = static_cast<int>(r * 255);
        int green = static_cast<int>(g * 255);
        int blue = static_cast<int>(b * 255);
        int alpha = static_cast<int>(a * 255);

        return QColor(red, green, blue, alpha);
    }
};

#endif // UTILS_H
