#include <cstdio>
#include <iostream>
#include <string>

#include <opencv2/core.hpp>

#include "OpenMVS/MVS/Interface.h"

static std::string json_escape(const std::string& s) {
    std::string out;
    out.reserve(s.size());
    for (char c : s) {
        if (c == '\\' || c == '"') {
            out.push_back('\\');
        }
        out.push_back(c);
    }
    return out;
}

static void emit_json(
    int error_code,
    int schema_version,
    std::size_t kept_points,
    const std::string& output_path,
    const std::string& message
) {
    std::cout
        << "{"
        << "\"schema_magic\":\"MVSI\","
        << "\"tool\":\"RL_BRIDGE_SHARD\","
        << "\"schema_version\":" << schema_version << ","
        << "\"error_code\":" << error_code << ","
        << "\"kept_points\":" << kept_points << ","
        << "\"output_path\":\"" << json_escape(output_path) << "\","
        << "\"message\":\"" << json_escape(message) << "\""
        << "}" << std::endl;
}

int main(int argc, char* argv[]) {
    std::string input_path;
    std::string output_path;
    float min_x = 0.f, min_y = 0.f, min_z = 0.f;
    float max_x = 0.f, max_y = 0.f, max_z = 0.f;
    bool has_min = false;
    bool has_max = false;

    for (int i = 1; i < argc; ++i) {
        const std::string arg(argv[i]);
        if (arg == "--input" && (i + 1) < argc) {
            input_path = argv[++i];
        } else if (arg == "--output" && (i + 1) < argc) {
            output_path = argv[++i];
        } else if (arg == "--min" && (i + 1) < argc) {
            const int n = std::sscanf(argv[++i], "%f,%f,%f", &min_x, &min_y, &min_z);
            has_min = (n == 3);
        } else if (arg == "--max" && (i + 1) < argc) {
            const int n = std::sscanf(argv[++i], "%f,%f,%f", &max_x, &max_y, &max_z);
            has_max = (n == 3);
        }
    }

    if (input_path.empty() || output_path.empty() || !has_min || !has_max) {
        emit_json(1, 0, 0, "", "Missing required arguments");
        return 1;
    }

    MVS::Interface input;
    uint32_t input_version = 0;
    if (!MVS::ARCHIVE::SerializeLoad(input, input_path, &input_version)) {
        emit_json(2, 0, 0, "", "Failed to load input .mvs");
        return 2;
    }

    MVS::Interface shard = input;
    shard.vertices.clear();
    shard.verticesNormal.clear();
    shard.verticesColor.clear();
    shard.lines.clear();
    shard.linesNormal.clear();
    shard.linesColor.clear();

    const bool has_normals = !input.verticesNormal.empty();
    const bool has_colors = !input.verticesColor.empty();

    const std::size_t count = input.vertices.size();
    for (std::size_t i = 0; i < count; ++i) {
        const auto& v = input.vertices[i];
        if (v.X.x >= min_x && v.X.x <= max_x &&
            v.X.y >= min_y && v.X.y <= max_y &&
            v.X.z >= min_z && v.X.z <= max_z) {
            shard.vertices.push_back(v);
            if (has_normals) {
                shard.verticesNormal.push_back(input.verticesNormal[i]);
            }
            if (has_colors) {
                shard.verticesColor.push_back(input.verticesColor[i]);
            }
        }
    }

    if (!MVS::ARCHIVE::SerializeSave(shard, output_path, input_version > 0 ? input_version : MVSI_PROJECT_VER)) {
        emit_json(3, static_cast<int>(input_version), 0, output_path, "Failed to save shard");
        return 3;
    }

    emit_json(0, static_cast<int>(input_version), shard.vertices.size(), output_path, "");
    return 0;
}
