// shape.cpp - Implementació del sistema de formes vectorials
// © 2026 JailDesigner

#include "core/graphics/shape.hpp"

#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>

namespace Graphics {

Shape::Shape(const std::string& filepath)
    : center_({.x = 0.0F, .y = 0.0F}),
      
      nom_("unnamed") {
    load(filepath);
}

auto Shape::load(const std::string& filepath) -> bool {
    // Llegir file
    std::ifstream file(filepath);
    if (!file.is_open()) {
        std::cerr << "[Shape] Error: no es pot obrir " << filepath << '\n';
        return false;
    }

    // Llegir todo el contingut
    std::stringstream buffer;
    buffer << file.rdbuf();
    std::string contingut = buffer.str();
    file.close();

    // Parsejar
    return parseFile(contingut);
}

auto Shape::parseFile(const std::string& contingut) -> bool {
    std::istringstream iss(contingut);
    std::string line;

    while (std::getline(iss, line)) {
        // Trim whitespace
        line = trim(line);

        // Skip comments and blanks
        if (line.empty() || line[0] == '#') {
            continue;
        }

        // Parse command
        if (startsWith(line, "name:")) {
            nom_ = trim(extractValue(line));
        } else if (startsWith(line, "scale:")) {
            try {
                escala_defecte_ = std::stof(extractValue(line));
            } catch (...) {
                std::cerr << "[Shape] Warning: scale invàlida, usant 1.0" << '\n';
                escala_defecte_ = 1.0F;
            }
        } else if (startsWith(line, "center:")) {
            parseCenter(extractValue(line));
        } else if (startsWith(line, "polyline:")) {
            auto points = parsePoints(extractValue(line));
            if (points.size() >= 2) {
                primitives_.push_back({PrimitiveType::POLYLINE, points});
            } else {
                std::cerr << "[Shape] Warning: polyline con menys de 2 points ignorada"
                          << '\n';
            }
        } else if (startsWith(line, "line:")) {
            auto points = parsePoints(extractValue(line));
            if (points.size() == 2) {
                primitives_.push_back({PrimitiveType::LINE, points});
            } else {
                std::cerr << "[Shape] Warning: line ha de tenir exactament 2 points"
                          << '\n';
            }
        }
        // Comandes desconegudes ignorades silenciosament
    }

    if (primitives_.empty()) {
        std::cerr << "[Shape] Error: sin primitiva carregada" << '\n';
        return false;
    }

    return true;
}

// Helper: trim whitespace
auto Shape::trim(const std::string& str) -> std::string {
    const char* whitespace = " \t\n\r";
    size_t start = str.find_first_not_of(whitespace);
    if (start == std::string::npos) {
        return "";
    }

    size_t end = str.find_last_not_of(whitespace);
    return str.substr(start, end - start + 1);
}

// Helper: startsWith
auto Shape::startsWith(const std::string& str,
    const std::string& prefix) -> bool {
    if (str.length() < prefix.length()) {
        return false;
    }
    return str.starts_with(prefix);
}

// Helper: extract value after ':'
auto Shape::extractValue(const std::string& line) -> std::string {
    size_t colon = line.find(':');
    if (colon == std::string::npos) {
        return "";
    }
    return line.substr(colon + 1);
}

// Helper: parse center "x, y"
void Shape::parseCenter(const std::string& value) {
    std::string val = trim(value);
    size_t comma = val.find(',');
    if (comma != std::string::npos) {
        try {
            center_.x = std::stof(trim(val.substr(0, comma)));
            center_.y = std::stof(trim(val.substr(comma + 1)));
        } catch (...) {
            std::cerr << "[Shape] Warning: centro invàlid, usant (0,0)" << '\n';
            center_ = {.x = 0.0F, .y = 0.0F};
        }
    }
}

// Helper: parse points "x1,y1  x2,y2  x3,y3"
auto Shape::parsePoints(const std::string& str) -> std::vector<Vec2> {
    std::vector<Vec2> points;
    std::istringstream iss(trim(str));
    std::string pair;

    while (iss >> pair) {  // Whitespace-separated
        size_t comma = pair.find(',');
        if (comma != std::string::npos) {
            try {
                float x = std::stof(pair.substr(0, comma));
                float y = std::stof(pair.substr(comma + 1));
                points.push_back({x, y});
            } catch (...) {
                std::cerr << "[Shape] Warning: point invàlid ignorat: " << pair
                          << '\n';
            }
        }
    }

    return points;
}

}  // namespace Graphics
