#include "document/ppi_import.h"

#include <utility>
#include <span>
#include <vector>

namespace pp::document {

namespace {

[[nodiscard]] pp::foundation::Result<pp::paint::BlendMode> ppi_layer_blend_mode(
    std::uint32_t blend_mode) noexcept
{
    switch (blend_mode) {
    case 0:
        return pp::foundation::Result<pp::paint::BlendMode>::success(pp::paint::BlendMode::normal);
    case 1:
        return pp::foundation::Result<pp::paint::BlendMode>::success(pp::paint::BlendMode::multiply);
    case 2:
        return pp::foundation::Result<pp::paint::BlendMode>::success(pp::paint::BlendMode::screen);
    case 3:
        return pp::foundation::Result<pp::paint::BlendMode>::success(pp::paint::BlendMode::color_dodge);
    case 4:
        return pp::foundation::Result<pp::paint::BlendMode>::success(pp::paint::BlendMode::overlay);
    default:
        return pp::foundation::Result<pp::paint::BlendMode>::failure(
            pp::foundation::Status::invalid_argument("PPI layer blend mode is not supported by pp_document"));
    }
}

[[nodiscard]] pp::foundation::Result<CanvasDocument> document_from_ppi_index(
    const pp::assets::PpiProjectIndex& project)
{
    if (project.body.layers.empty()) {
        return pp::foundation::Result<CanvasDocument>::failure(
            pp::foundation::Status::invalid_argument("PPI project has no layers"));
    }

    const auto& reference_frames = project.body.layers.front().frames;
    if (reference_frames.empty()) {
        return pp::foundation::Result<CanvasDocument>::failure(
            pp::foundation::Status::invalid_argument("PPI project has no frames"));
    }

    std::vector<AnimationFrame> frames;
    frames.reserve(reference_frames.size());
    for (const auto& frame : reference_frames) {
        frames.push_back(AnimationFrame { .duration_ms = frame.duration_ms, .face_pixels = {} });
    }

    std::vector<std::vector<AnimationFrame>> layer_frames;
    layer_frames.reserve(project.body.layers.size());
    std::vector<DocumentLayerConfig> layers;
    layers.reserve(project.body.layers.size());
    for (const auto& layer : project.body.layers) {
        const auto blend_mode = ppi_layer_blend_mode(layer.blend_mode);
        if (!blend_mode) {
            return pp::foundation::Result<CanvasDocument>::failure(blend_mode.status());
        }

        auto& frame_list = layer_frames.emplace_back();
        frame_list.reserve(layer.frames.size());
        for (const auto& frame : layer.frames) {
            frame_list.push_back(AnimationFrame { .duration_ms = frame.duration_ms, .face_pixels = {} });
        }

        layers.push_back(DocumentLayerConfig {
            .name = layer.name,
            .visible = layer.visible,
            .alpha_locked = layer.alpha_locked,
            .opacity = layer.opacity,
            .blend_mode = blend_mode.value(),
            .frames = std::span<const AnimationFrame>(frame_list.data(), frame_list.size()),
        });
    }

    return CanvasDocument::create_from_snapshot(DocumentSnapshotConfig {
        .width = project.body.summary.width,
        .height = project.body.summary.height,
        .layers = layers,
        .frames = frames,
        .selection_masks = {},
    });
}

}

pp::foundation::Result<CanvasDocument> import_ppi_project_document(
    const pp::assets::PpiDecodedProjectImages& project)
{
    auto document = document_from_ppi_index(project.project);
    if (!document) {
        return document;
    }

    auto value = document.value();
    for (const auto& face : project.faces) {
        const auto status = value.set_layer_frame_face_pixels(
            face.layer_index,
            face.frame_index,
            LayerFacePixels {
                .face_index = face.face_index,
                .x = face.descriptor.x0,
                .y = face.descriptor.y0,
                .width = face.image.width,
                .height = face.image.height,
                .rgba8 = face.image.pixels,
            });
        if (!status.ok()) {
            return pp::foundation::Result<CanvasDocument>::failure(status);
        }
    }

    return pp::foundation::Result<CanvasDocument>::success(std::move(value));
}

}
