#pragma once

#include "assets/image_pixels.h"
#include "foundation/result.h"

#include <array>
#include <cstddef>
#include <cstdint>
#include <span>
#include <string>
#include <string_view>
#include <vector>

namespace pp::assets {

constexpr std::size_t ppi_header_size = 40;
constexpr std::uint32_t max_ppi_canvas_dimension = 131072;
constexpr std::uint32_t max_ppi_layer_count = 1024;
constexpr std::uint32_t max_ppi_frame_count = 100000;
constexpr std::size_t max_ppi_layer_name_length = 128;
constexpr std::uint64_t max_ppi_face_payload_bytes = 1024ULL * 1024ULL * 1024ULL;

struct PpiVersion {
    std::uint32_t major = 0;
    std::uint32_t minor = 0;
};

struct PpiSoftwareVersion {
    std::uint32_t major = 0;
    std::uint32_t minor = 0;
    std::uint32_t fix = 0;
    std::uint32_t build = 0;
};

struct PpiThumbnailInfo {
    std::uint32_t width = 0;
    std::uint32_t height = 0;
    std::uint32_t components = 0;
};

struct PpiHeaderInfo {
    PpiVersion document_version;
    PpiSoftwareVersion software_version;
    PpiThumbnailInfo thumbnail;
};

struct PpiProjectLayout {
    PpiHeaderInfo header;
    std::size_t thumbnail_offset = 0;
    std::size_t thumbnail_bytes = 0;
    std::size_t body_offset = 0;
    std::size_t body_bytes = 0;
};

struct PpiBodySummary {
    std::uint32_t width = 0;
    std::uint32_t height = 0;
    std::uint32_t layer_count = 0;
    std::uint32_t declared_frame_count = 0;
    std::uint32_t total_layer_frames = 0;
    std::uint32_t dirty_face_count = 0;
    std::uint32_t rgba_face_payload_count = 0;
    std::uint64_t compressed_face_bytes = 0;
    std::uint32_t info_bytes = 0;
};

struct PpiProjectSummary {
    PpiProjectLayout layout;
    PpiBodySummary body;
};

struct PpiFacePayloadSummary {
    bool has_data = false;
    std::uint32_t x0 = 0;
    std::uint32_t y0 = 0;
    std::uint32_t x1 = 0;
    std::uint32_t y1 = 0;
    std::uint32_t body_payload_offset = 0;
    std::uint32_t payload_bytes = 0;
    std::uint32_t png_width = 0;
    std::uint32_t png_height = 0;
};

struct PpiFrameSummary {
    std::uint32_t duration_ms = 100;
    std::array<PpiFacePayloadSummary, 6> faces;
};

struct PpiLayerSummary {
    std::uint32_t stored_order = 0;
    std::string name;
    float opacity = 1.0F;
    std::uint32_t blend_mode = 0;
    bool alpha_locked = false;
    bool visible = true;
    std::vector<PpiFrameSummary> frames;
};

struct PpiBodyIndex {
    PpiBodySummary summary;
    std::vector<PpiLayerSummary> layers;
};

struct PpiProjectIndex {
    PpiProjectLayout layout;
    PpiBodyIndex body;
};

struct PpiDecodedFacePayload {
    std::uint32_t layer_index = 0;
    std::uint32_t frame_index = 0;
    std::uint32_t face_index = 0;
    PpiFacePayloadSummary descriptor;
    Rgba8Image image;
};

struct PpiDecodedProjectImages {
    PpiProjectIndex project;
    std::vector<PpiDecodedFacePayload> faces;
};

struct PpiDirtyFacePayloadConfig {
    std::uint32_t layer_index = 0;
    std::uint32_t frame_index = 0;
    std::uint32_t face_index = 0;
    std::uint32_t x = 0;
    std::uint32_t y = 0;
    std::uint32_t width = 0;
    std::uint32_t height = 0;
    std::span<const std::byte> png_rgba8;
};

struct PpiLayerMetadataConfig {
    float opacity = 1.0F;
    std::uint32_t blend_mode = 0;
    bool alpha_locked = false;
    bool visible = true;
};

struct PpiFrameConfig {
    std::uint32_t duration_ms = 100;
};

struct PpiLayerConfig {
    std::string_view name;
    PpiLayerMetadataConfig metadata;
    std::span<const PpiFrameConfig> frames;
};

struct PpiProjectConfig {
    std::uint32_t width = 0;
    std::uint32_t height = 0;
    std::span<const PpiLayerConfig> layers;
    std::span<const PpiDirtyFacePayloadConfig> dirty_faces;
};

struct PpiMinimalProjectConfig {
    std::uint32_t width = 0;
    std::uint32_t height = 0;
    std::string layer_name;
    PpiLayerMetadataConfig layer_metadata;
    std::uint32_t layer_count = 1;
    std::uint32_t frame_count = 1;
    std::uint32_t frame_duration_ms = 100;
    std::span<const PpiDirtyFacePayloadConfig> dirty_faces;
};

[[nodiscard]] pp::foundation::Result<PpiHeaderInfo> parse_ppi_header(
    std::span<const std::byte> bytes) noexcept;

[[nodiscard]] pp::foundation::Result<std::size_t> ppi_thumbnail_byte_size(PpiThumbnailInfo thumbnail) noexcept;

[[nodiscard]] pp::foundation::Result<PpiProjectLayout> parse_ppi_project_layout(
    std::span<const std::byte> bytes) noexcept;

[[nodiscard]] pp::foundation::Result<PpiBodySummary> parse_ppi_body_summary(
    PpiHeaderInfo header,
    std::span<const std::byte> body) noexcept;

[[nodiscard]] pp::foundation::Result<PpiBodyIndex> parse_ppi_body_index(
    PpiHeaderInfo header,
    std::span<const std::byte> body);

[[nodiscard]] pp::foundation::Result<PpiProjectSummary> parse_ppi_project_summary(
    std::span<const std::byte> bytes) noexcept;

[[nodiscard]] pp::foundation::Result<PpiProjectIndex> parse_ppi_project_index(
    std::span<const std::byte> bytes);

[[nodiscard]] pp::foundation::Result<PpiDecodedProjectImages> decode_ppi_project_images(
    std::span<const std::byte> bytes);

[[nodiscard]] pp::foundation::Result<std::vector<std::byte>> create_ppi_project(
    PpiProjectConfig config);

[[nodiscard]] pp::foundation::Result<std::vector<std::byte>> create_minimal_ppi_project(
    PpiMinimalProjectConfig config);

}
