#include "Format.hpp"
#include <vector>
#include "../includes.hpp"
#include "debug/log/Logger.hpp"
#include "../macros.hpp"
#include <xf86drm.h>
#include <drm_fourcc.h>

inline const std::vector<SPixelFormat> GLES3_FORMATS = {
    {
        .drmFormat        = DRM_FORMAT_ARGB8888,
        .glInternalFormat = GL_RGBA8,
        .glFormat         = GL_RGBA,
        .glType           = GL_UNSIGNED_BYTE,
        .withAlpha        = true,
        .alphaStripped    = DRM_FORMAT_XRGB8888,
        .bytesPerBlock    = 4,
        .swizzle          = {SWIZZLE_BGRA},
    },
    {
        .drmFormat        = DRM_FORMAT_XRGB8888,
        .glInternalFormat = GL_RGBA8,
        .glFormat         = GL_RGBA,
        .glType           = GL_UNSIGNED_BYTE,
        .withAlpha        = false,
        .alphaStripped    = DRM_FORMAT_XRGB8888,
        .bytesPerBlock    = 4,
        .swizzle          = {SWIZZLE_BGR1},
    },
    {
        .drmFormat        = DRM_FORMAT_XBGR8888,
        .glInternalFormat = GL_RGBA8,
        .glFormat         = GL_RGBA,
        .glType           = GL_UNSIGNED_BYTE,
        .withAlpha        = false,
        .alphaStripped    = DRM_FORMAT_XBGR8888,
        .bytesPerBlock    = 4,
        .swizzle          = {SWIZZLE_RGB1},
    },
    {
        .drmFormat        = DRM_FORMAT_ABGR8888,
        .glInternalFormat = GL_RGBA8,
        .glFormat         = GL_RGBA,
        .glType           = GL_UNSIGNED_BYTE,
        .withAlpha        = true,
        .alphaStripped    = DRM_FORMAT_XBGR8888,
        .bytesPerBlock    = 4,
        .swizzle          = {SWIZZLE_RGBA},
    },
    {
        .drmFormat        = DRM_FORMAT_BGR888,
        .glInternalFormat = GL_RGB8,
        .glFormat         = GL_RGB,
        .glType           = GL_UNSIGNED_BYTE,
        .withAlpha        = false,
        .alphaStripped    = DRM_FORMAT_BGR888,
        .bytesPerBlock    = 3,
        .swizzle          = {SWIZZLE_RGB1},
    },
    {
        .drmFormat        = DRM_FORMAT_RGBX4444,
        .glInternalFormat = GL_RGBA4,
        .glFormat         = GL_RGBA,
        .glType           = GL_UNSIGNED_SHORT_4_4_4_4,
        .withAlpha        = false,
        .alphaStripped    = DRM_FORMAT_RGBX4444,
        .bytesPerBlock    = 2,
        .swizzle          = {SWIZZLE_RGB1},
    },
    {
        .drmFormat        = DRM_FORMAT_RGBA4444,
        .glInternalFormat = GL_RGBA4,
        .glFormat         = GL_RGBA,
        .glType           = GL_UNSIGNED_SHORT_4_4_4_4,
        .withAlpha        = true,
        .alphaStripped    = DRM_FORMAT_RGBX4444,
        .bytesPerBlock    = 2,
        .swizzle          = {SWIZZLE_RGBA},
    },
    {
        .drmFormat        = DRM_FORMAT_RGBX5551,
        .glInternalFormat = GL_RGB5_A1,
        .glFormat         = GL_RGBA,
        .glType           = GL_UNSIGNED_SHORT_5_5_5_1,
        .withAlpha        = false,
        .alphaStripped    = DRM_FORMAT_RGBX5551,
        .bytesPerBlock    = 2,
        .swizzle          = {SWIZZLE_RGB1},
    },
    {
        .drmFormat        = DRM_FORMAT_RGBA5551,
        .glInternalFormat = GL_RGB5_A1,
        .glFormat         = GL_RGBA,
        .glType           = GL_UNSIGNED_SHORT_5_5_5_1,
        .withAlpha        = true,
        .alphaStripped    = DRM_FORMAT_RGBX5551,
        .bytesPerBlock    = 2,
        .swizzle          = {SWIZZLE_RGBA},
    },
    {
        .drmFormat        = DRM_FORMAT_RGB565,
        .glInternalFormat = GL_RGB565,
        .glFormat         = GL_RGB,
        .glType           = GL_UNSIGNED_SHORT_5_6_5,
        .withAlpha        = false,
        .alphaStripped    = DRM_FORMAT_RGB565,
        .bytesPerBlock    = 2,
        .swizzle          = {SWIZZLE_RGB1},
    },
    {
        .drmFormat        = DRM_FORMAT_XBGR2101010,
        .glInternalFormat = GL_RGB10_A2,
        .glFormat         = GL_RGBA,
        .glType           = GL_UNSIGNED_INT_2_10_10_10_REV,
        .withAlpha        = false,
        .alphaStripped    = DRM_FORMAT_XBGR2101010,
        .bytesPerBlock    = 4,
        .swizzle          = {SWIZZLE_RGB1},
    },
    {
        .drmFormat        = DRM_FORMAT_ABGR2101010,
        .glInternalFormat = GL_RGB10_A2,
        .glFormat         = GL_RGBA,
        .glType           = GL_UNSIGNED_INT_2_10_10_10_REV,
        .withAlpha        = true,
        .alphaStripped    = DRM_FORMAT_XBGR2101010,
        .bytesPerBlock    = 4,
        .swizzle          = {SWIZZLE_RGBA},
    },
    {
        .drmFormat        = DRM_FORMAT_XRGB2101010,
        .glInternalFormat = GL_RGB10_A2,
        .glFormat         = GL_RGBA,
        .glType           = GL_UNSIGNED_INT_2_10_10_10_REV,
        .withAlpha        = false,
        .alphaStripped    = DRM_FORMAT_XRGB2101010,
        .bytesPerBlock    = 4,
        .swizzle          = {SWIZZLE_BGR1},
    },
    {
        .drmFormat        = DRM_FORMAT_ARGB2101010,
        .glInternalFormat = GL_RGB10_A2,
        .glFormat         = GL_RGBA,
        .glType           = GL_UNSIGNED_INT_2_10_10_10_REV,
        .withAlpha        = true,
        .alphaStripped    = DRM_FORMAT_XRGB2101010,
        .bytesPerBlock    = 4,
        .swizzle          = {SWIZZLE_BGRA},
    },
    {
        .drmFormat        = DRM_FORMAT_XBGR16161616F,
        .glInternalFormat = GL_RGBA16F,
        .glFormat         = GL_RGBA,
        .glType           = GL_HALF_FLOAT,
        .withAlpha        = false,
        .alphaStripped    = DRM_FORMAT_XBGR16161616F,
        .bytesPerBlock    = 8,
        .swizzle          = {SWIZZLE_RGB1},
    },
    {
        .drmFormat        = DRM_FORMAT_ABGR16161616F,
        .glInternalFormat = GL_RGBA16F,
        .glFormat         = GL_RGBA,
        .glType           = GL_HALF_FLOAT,
        .withAlpha        = true,
        .alphaStripped    = DRM_FORMAT_XBGR16161616F,
        .bytesPerBlock    = 8,
        .swizzle          = {SWIZZLE_RGBA},
    },
    {
        .drmFormat        = DRM_FORMAT_XBGR16161616,
        .glInternalFormat = GL_RGBA16UI,
        .glFormat         = GL_RGBA_INTEGER,
        .glType           = GL_UNSIGNED_SHORT,
        .withAlpha        = false,
        .alphaStripped    = DRM_FORMAT_XBGR16161616,
        .bytesPerBlock    = 8,
        .swizzle          = {SWIZZLE_RGBA},
    },
    {
        .drmFormat        = DRM_FORMAT_ABGR16161616,
        .glInternalFormat = GL_RGBA16UI,
        .glFormat         = GL_RGBA_INTEGER,
        .glType           = GL_UNSIGNED_SHORT,
        .withAlpha        = true,
        .alphaStripped    = DRM_FORMAT_XBGR16161616,
        .bytesPerBlock    = 8,
        .swizzle          = {SWIZZLE_RGBA},
    },
    {
        .drmFormat     = DRM_FORMAT_YVYU,
        .bytesPerBlock = 4,
        .blockSize     = {2, 1},
    },
    {
        .drmFormat     = DRM_FORMAT_VYUY,
        .bytesPerBlock = 4,
        .blockSize     = {2, 1},
    },
    {
        .drmFormat        = DRM_FORMAT_R8,
        .glInternalFormat = GL_R8,
        .glFormat         = GL_RED,
        .glType           = GL_UNSIGNED_BYTE,
        .bytesPerBlock    = 1,
        .swizzle          = {SWIZZLE_R001},
    },
    {
        .drmFormat        = DRM_FORMAT_GR88,
        .glInternalFormat = GL_RG8,
        .glFormat         = GL_RG,
        .glType           = GL_UNSIGNED_BYTE,
        .bytesPerBlock    = 2,
        .swizzle          = {SWIZZLE_RG01},
    },
    {
        .drmFormat        = DRM_FORMAT_RGB888,
        .glInternalFormat = GL_RGB8,
        .glFormat         = GL_RGB,
        .glType           = GL_UNSIGNED_BYTE,
        .bytesPerBlock    = 3,
        .swizzle          = {SWIZZLE_BGR1},
    },
};

SHMFormat NFormatUtils::drmToShm(DRMFormat drm) {
    switch (drm) {
        case DRM_FORMAT_XRGB8888: return WL_SHM_FORMAT_XRGB8888;
        case DRM_FORMAT_ARGB8888: return WL_SHM_FORMAT_ARGB8888;
        default: return drm;
    }

    return drm;
}

DRMFormat NFormatUtils::shmToDRM(SHMFormat shm) {
    switch (shm) {
        case WL_SHM_FORMAT_XRGB8888: return DRM_FORMAT_XRGB8888;
        case WL_SHM_FORMAT_ARGB8888: return DRM_FORMAT_ARGB8888;
        default: return shm;
    }

    return shm;
}

const SPixelFormat* NFormatUtils::getPixelFormatFromDRM(DRMFormat drm) {
    for (auto const& fmt : GLES3_FORMATS) {
        if (fmt.drmFormat == drm)
            return &fmt;
    }

    return nullptr;
}

const SPixelFormat* NFormatUtils::getPixelFormatFromGL(uint32_t glFormat, uint32_t glType, bool alpha) {
    for (auto const& fmt : GLES3_FORMATS) {
        if (fmt.glFormat == sc<int>(glFormat) && fmt.glType == sc<int>(glType) && fmt.withAlpha == alpha)
            return &fmt;
    }

    return nullptr;
}

bool NFormatUtils::isFormatYUV(uint32_t drmFormat) {
    switch (drmFormat) {
        case DRM_FORMAT_YUYV:
        case DRM_FORMAT_YVYU:
        case DRM_FORMAT_UYVY:
        case DRM_FORMAT_VYUY:
        case DRM_FORMAT_AYUV:
        case DRM_FORMAT_NV12:
        case DRM_FORMAT_NV21:
        case DRM_FORMAT_NV16:
        case DRM_FORMAT_NV61:
        case DRM_FORMAT_YUV410:
        case DRM_FORMAT_YUV411:
        case DRM_FORMAT_YUV420:
        case DRM_FORMAT_YUV422:
        case DRM_FORMAT_YUV444: return true;
        default: return false;
    }
}

bool NFormatUtils::isFormatOpaque(DRMFormat drm) {
    const auto FMT = NFormatUtils::getPixelFormatFromDRM(drm);
    if (!FMT)
        return false;

    return !FMT->withAlpha;
}

int NFormatUtils::pixelsPerBlock(const SPixelFormat* const fmt) {
    return fmt->blockSize.x * fmt->blockSize.y > 0 ? fmt->blockSize.x * fmt->blockSize.y : 1;
}

int NFormatUtils::minStride(const SPixelFormat* const fmt, int32_t width) {
    return std::ceil((width * fmt->bytesPerBlock) / pixelsPerBlock(fmt));
}

uint32_t NFormatUtils::drmFormatToGL(DRMFormat drm) {
    switch (drm) {
        case DRM_FORMAT_XRGB8888:
        case DRM_FORMAT_XBGR8888: return GL_RGBA; // doesn't matter, opengl is gucci in this case.
        case DRM_FORMAT_XRGB2101010:
        case DRM_FORMAT_XBGR2101010: return GL_RGB10_A2;
        default: return GL_RGBA;
    }
    UNREACHABLE();
    return GL_RGBA;
}

uint32_t NFormatUtils::glFormatToType(uint32_t gl) {
    return gl != GL_RGBA ? GL_UNSIGNED_INT_2_10_10_10_REV : GL_UNSIGNED_BYTE;
}

std::string NFormatUtils::drmFormatName(DRMFormat drm) {
    auto n = drmGetFormatName(drm);

    if (!n)
        return "unknown";

    std::string name = n;
    free(n); // NOLINT(cppcoreguidelines-no-malloc,-warnings-as-errors)
    return name;
}

std::string NFormatUtils::drmModifierName(uint64_t mod) {
    auto n = drmGetFormatModifierName(mod);

    if (!n)
        return "unknown";

    std::string name = n;
    free(n); // NOLINT(cppcoreguidelines-no-malloc,-warnings-as-errors)
    return name;
}

DRMFormat NFormatUtils::alphaFormat(DRMFormat prevFormat) {
    switch (prevFormat) {
        case DRM_FORMAT_XRGB8888: return DRM_FORMAT_ARGB8888;
        case DRM_FORMAT_XBGR8888: return DRM_FORMAT_ABGR8888;
        case DRM_FORMAT_BGRX8888: return DRM_FORMAT_BGRA8888;
        case DRM_FORMAT_RGBX8888: return DRM_FORMAT_RGBA8888;
        case DRM_FORMAT_XRGB2101010: return DRM_FORMAT_ARGB2101010;
        case DRM_FORMAT_XBGR2101010: return DRM_FORMAT_ABGR2101010;
        case DRM_FORMAT_RGBX1010102: return DRM_FORMAT_RGBA1010102;
        case DRM_FORMAT_BGRX1010102: return DRM_FORMAT_BGRA1010102;
        default: return 0;
    }
}
