#pragma once
#include <cstdint>

// Modelo de grafo extendido (issue 0049e). El sentinel `0` se usa en cada
// campo `*_override` para significar "usar el valor del EntityType/RelationType
// indicado por type_id". Asi un nodo nuevo creado con todos los campos a 0
// hereda automaticamente la apariencia del tipo, sin tener que volver a
// escribir color/size/shape.

// --- Bitmask de flags por nodo. NF_VISIBLE es default tras `graph_node()`.
enum NodeFlags : uint8_t {
    NF_NONE     = 0,
    NF_PINNED   = 1 << 0,  // el force layout no mueve este nodo
    NF_VISIBLE  = 1 << 1,  // si esta a 0, el renderer salta el nodo
    NF_SELECTED = 1 << 2,  // marcado por el viewport (click)
    NF_HOVERED  = 1 << 3,  // marcado por el viewport (mouse over)
};

// --- Bitmask de flags por arista. EF_VISIBLE es default tras `graph_edge()`.
enum EdgeFlags : uint8_t {
    EF_NONE     = 0,
    EF_DIRECTED = 1 << 0,  // arista con direccion (futuro: dibujar flecha)
    EF_VISIBLE  = 1 << 1,
};

// --- Shapes per-nodo. Convencion: `shape_override == 0` significa "usa el
// shape del EntityType". Por eso SHAPE_CIRCLE no es 0 — un override que
// exigiera circulo cuando el tipo dice cuadrado seria indistinguible de
// "no override". Issue 0049f anadira el dispatch real; por ahora todos los
// nodos se pintan como circulo.
enum Shape : uint8_t {
    SHAPE_USE_TYPE       = 0,  // sentinel: heredar del EntityType
    SHAPE_CIRCLE         = 1,
    SHAPE_SQUARE         = 2,
    SHAPE_DIAMOND        = 3,
    SHAPE_HEX            = 4,
    SHAPE_TRIANGLE       = 5,
    SHAPE_ROUNDED_SQUARE = 6,
};

// --- Estilos de arista. Misma convencion: 0 = "usa el estilo del
// RelationType". 0049f introducira dashed/dotted reales; ahora EDGE_SOLID
// es lo que sale por la GPU sea cual sea el valor.
enum EdgeStyle : uint8_t {
    EDGE_USE_TYPE = 0,
    EDGE_SOLID    = 1,
    EDGE_DASHED   = 2,
    EDGE_DOTTED   = 3,
};

// --- Graph node ---
// 40 bytes en x86_64. Layout pensado para vertex-pulling: x/y/vx/vy primero
// (4 floats consecutivos = 16 bytes que el TBO RG32F lee tal cual), seguidos
// del paquete denso de overrides + flags + indices.
struct GraphNode {
    float    x, y, vx, vy;
    uint16_t type_id;          // index into GraphData::types
    uint8_t  shape_override;   // SHAPE_*; 0 = usar EntityType
    uint8_t  flags;            // NF_* mask; default NF_VISIBLE tras graph_node()
    uint32_t color_override;   // RGBA8 (R en LSB); 0 = usar EntityType
    float    size_override;    // pixels; <= 0 = usar EntityType
    uint32_t label_idx;        // index en el string pool del consumer (0 = no label)
    uint64_t user_data;        // opaco, app-specific (e.g. id de la BD origen)
};

// --- Graph edge ---
// 24 bytes. weight separado del paquete de uint8_t para mantenerlo alineado.
struct GraphEdge {
    uint32_t source;           // index into GraphData::nodes
    uint32_t target;           // index into GraphData::nodes
    uint16_t type_id;          // index into GraphData::rel_types
    uint8_t  style_override;   // EDGE_*; 0 = usar RelationType
    uint8_t  flags;            // EF_* mask; default EF_VISIBLE tras graph_edge()
    float    weight;
    uint32_t label_idx;
};

// --- Entity type (per-node visual & semantic class) ---
// La tabla EntityType[] vive en GraphData::types. Los nodos referencian via
// `type_id`. El `name` es solo para UI/debug; el renderer no lo lee.
struct EntityType {
    uint32_t color;            // RGBA8 (R en LSB)
    uint8_t  shape;            // SHAPE_* (no SHAPE_USE_TYPE — es el valor base)
    uint16_t icon_id;          // 0 = sin icono (issue 0049f)
    float    default_size;     // pixels
    const char* name;          // C-string propiedad del caller
};

// --- Relation type (per-edge visual & semantic class) ---
struct RelationType {
    uint32_t color;            // RGBA8
    uint8_t  style;            // EDGE_* (no EDGE_USE_TYPE)
    float    width;            // pixels
    const char* name;
};

// --- Graph container ---
// Memoria de `nodes`, `edges`, `types`, `rel_types` es propiedad del caller.
struct GraphData {
    GraphNode*    nodes;       int node_count;     int node_capacity;
    GraphEdge*    edges;       int edge_count;     int edge_capacity;
    EntityType*   types;       int type_count;
    RelationType* rel_types;   int rel_type_count;

    // Bounding box en world space (actualizado por update_bounds / layout)
    float min_x, min_y, max_x, max_y;

    void update_bounds();

    // Busqueda lineal por user_data. -1 si no existe. O(n).
    int find_node_by_user_data(uint64_t user_data) const;
};

// --- Resoluciones de override (puras, exportadas para tests y consumers) ---
// Devuelven el valor "efectivo" combinando el override del nodo/arista con la
// tabla de tipos. Las funciones son seguras frente a tablas vacias o type_id
// fuera de rango: degradan a un default razonable (gris para color, circulo
// para shape, 4 px para size, solid para style, 1 px para width).

inline uint32_t resolve_node_color(const GraphNode& n,
                                   const EntityType* types, int type_count) {
    if (n.color_override != 0) return n.color_override;
    if (types && n.type_id < (uint16_t)type_count) return types[n.type_id].color;
    return 0xFF888888u;
}

inline uint8_t resolve_node_shape(const GraphNode& n,
                                  const EntityType* types, int type_count) {
    if (n.shape_override != SHAPE_USE_TYPE) return n.shape_override;
    if (types && n.type_id < (uint16_t)type_count) return types[n.type_id].shape;
    return SHAPE_CIRCLE;
}

inline float resolve_node_size(const GraphNode& n,
                               const EntityType* types, int type_count) {
    if (n.size_override > 0.0f) return n.size_override;
    if (types && n.type_id < (uint16_t)type_count) return types[n.type_id].default_size;
    return 4.0f;
}

inline uint32_t resolve_edge_color(const GraphEdge& e,
                                   const RelationType* rel_types, int rel_type_count) {
    if (rel_types && e.type_id < (uint16_t)rel_type_count) return rel_types[e.type_id].color;
    return 0xFF888888u;
}

inline uint8_t resolve_edge_style(const GraphEdge& e,
                                  const RelationType* rel_types, int rel_type_count) {
    if (e.style_override != EDGE_USE_TYPE) return e.style_override;
    if (rel_types && e.type_id < (uint16_t)rel_type_count) return rel_types[e.type_id].style;
    return EDGE_SOLID;
}

inline float resolve_edge_width(const GraphEdge& e,
                                const RelationType* rel_types, int rel_type_count) {
    if (rel_types && e.type_id < (uint16_t)rel_type_count) return rel_types[e.type_id].width;
    return 1.0f;
}

// --- Helpers de construccion ergonomicos ---
// Defaults: NF_VISIBLE / EF_VISIBLE encendidos, type_id = 0, sin overrides.
// Asi un nodo recien creado se renderiza con la apariencia del primer
// EntityType (que el caller tiene que registrar) sin tocar nada mas.
inline GraphNode graph_node(float x = 0.0f, float y = 0.0f, uint16_t type_id = 0) {
    GraphNode n{};
    n.x = x; n.y = y;
    n.vx = 0.0f; n.vy = 0.0f;
    n.type_id = type_id;
    n.shape_override = SHAPE_USE_TYPE;
    n.flags = NF_VISIBLE;
    n.color_override = 0u;
    n.size_override = 0.0f;
    n.label_idx = 0u;
    n.user_data = 0ull;
    return n;
}

inline GraphEdge graph_edge(uint32_t source, uint32_t target,
                            float weight = 1.0f, uint16_t type_id = 0) {
    GraphEdge e{};
    e.source = source;
    e.target = target;
    e.type_id = type_id;
    e.style_override = EDGE_USE_TYPE;
    e.flags = EF_VISIBLE;
    e.weight = weight;
    e.label_idx = 0u;
    return e;
}

inline EntityType entity_type(uint32_t color, uint8_t shape = SHAPE_CIRCLE,
                              float default_size = 4.0f, const char* name = nullptr,
                              uint16_t icon_id = 0) {
    return EntityType{ color, shape, icon_id, default_size, name };
}

inline RelationType relation_type(uint32_t color, uint8_t style = EDGE_SOLID,
                                  float width = 1.0f, const char* name = nullptr) {
    return RelationType{ color, style, width, name };
}
