#include "assets/image_format.h"

#include <array>
#include <cstdint>

namespace pp::assets {

namespace {

constexpr std::array png_signature {
    std::byte { 0x89 },
    std::byte { 0x50 },
    std::byte { 0x4e },
    std::byte { 0x47 },
    std::byte { 0x0d },
    std::byte { 0x0a },
    std::byte { 0x1a },
    std::byte { 0x0a },
};

[[nodiscard]] bool starts_with(std::span<const std::byte> bytes, std::span<const std::byte> prefix) noexcept
{
    if (bytes.size() < prefix.size()) {
        return false;
    }

    for (std::size_t i = 0; i < prefix.size(); ++i) {
        if (bytes[i] != prefix[i]) {
            return false;
        }
    }

    return true;
}

[[nodiscard]] bool is_prefix_of(std::span<const std::byte> bytes, std::span<const std::byte> signature) noexcept
{
    if (bytes.size() >= signature.size()) {
        return false;
    }

    for (std::size_t i = 0; i < bytes.size(); ++i) {
        if (bytes[i] != signature[i]) {
            return false;
        }
    }

    return true;
}

}

pp::foundation::Result<ImageFormat> detect_image_format(std::span<const std::byte> bytes) noexcept
{
    if (bytes.empty()) {
        return pp::foundation::Result<ImageFormat>::failure(
            pp::foundation::Status::invalid_argument("image data must not be empty"));
    }

    if (starts_with(bytes, png_signature)) {
        return pp::foundation::Result<ImageFormat>::success(ImageFormat::png);
    }

    if (is_prefix_of(bytes, png_signature)) {
        return pp::foundation::Result<ImageFormat>::failure(
            pp::foundation::Status::out_of_range("image data is a truncated PNG signature"));
    }

    if (bytes.size() < 3U) {
        return pp::foundation::Result<ImageFormat>::failure(
            pp::foundation::Status::out_of_range("image data is too short to identify"));
    }

    if (bytes[0] == std::byte { 0xff } && bytes[1] == std::byte { 0xd8 } && bytes[2] == std::byte { 0xff }) {
        return pp::foundation::Result<ImageFormat>::success(ImageFormat::jpeg);
    }

    return pp::foundation::Result<ImageFormat>::failure(
        pp::foundation::Status::invalid_argument("unsupported image signature"));
}

const char* image_format_name(ImageFormat format) noexcept
{
    switch (format) {
    case ImageFormat::png:
        return "png";
    case ImageFormat::jpeg:
        return "jpeg";
    }

    return "unknown";
}

}
