#pragma once

#include "pxl8_types.h"

typedef enum pxl8_graph_op {
    PXL8_OP_CONST,           // param -> out
    PXL8_OP_INPUT_AGE,       // particle normalized age -> out
    PXL8_OP_INPUT_SEED,      // seed -> out
    PXL8_OP_INPUT_TIME,      // time -> out
    PXL8_OP_INPUT_X,         // x coord -> out
    PXL8_OP_INPUT_Y,         // y coord -> out

    PXL8_OP_ABS,             // |a| -> out
    PXL8_OP_CEIL,            // ceil(a) -> out
    PXL8_OP_COS,             // cos(a) -> out
    PXL8_OP_FLOOR,           // floor(a) -> out
    PXL8_OP_FRACT,           // fract(a) -> out
    PXL8_OP_NEGATE,          // -a -> out
    PXL8_OP_SIN,             // sin(a) -> out
    PXL8_OP_SQRT,            // sqrt(a) -> out

    PXL8_OP_ADD,             // a + b -> out
    PXL8_OP_DIV,             // a / b -> out
    PXL8_OP_MAX,             // max(a, b) -> out
    PXL8_OP_MIN,             // min(a, b) -> out
    PXL8_OP_MOD,             // fmod(a, b) -> out
    PXL8_OP_MUL,             // a * b -> out
    PXL8_OP_POW,             // pow(a, b) -> out
    PXL8_OP_SUB,             // a - b -> out

    PXL8_OP_CLAMP,           // clamp(a, min, max) -> out
    PXL8_OP_LERP,            // lerp(a, b, t) -> out
    PXL8_OP_SELECT,          // t > 0 ? a : b -> out
    PXL8_OP_SMOOTHSTEP,      // smoothstep(edge0, edge1, x) -> out

    PXL8_OP_NOISE_FBM,       // fbm(x, y, octaves, scale, persistence) -> out
    PXL8_OP_NOISE_PERLIN,    // perlin noise(x, y, scale) -> out
    PXL8_OP_NOISE_RIDGED,    // ridged(x, y, octaves, scale, persistence) -> out
    PXL8_OP_NOISE_TURBULENCE,// turbulence(x, y, octaves, scale, persistence) -> out
    PXL8_OP_NOISE_VALUE,     // value noise(x, y, scale) -> out

    PXL8_OP_VORONOI_CELL,    // voronoi cell distance(x, y, scale) -> out
    PXL8_OP_VORONOI_EDGE,    // voronoi edge distance(x, y, scale) -> out
    PXL8_OP_VORONOI_ID,      // voronoi cell id(x, y, scale) -> out

    PXL8_OP_GRADIENT_LINEAR, // linear gradient(x, y, angle) -> out
    PXL8_OP_GRADIENT_RADIAL, // radial gradient(x, y, cx, cy) -> out

    PXL8_OP_QUANTIZE,        // quantize to palette: base + floor(a * range) -> out

    PXL8_OP_COUNT
} pxl8_graph_op;

typedef struct pxl8_node {
    u8 in[4];
    u8 op;
    u8 out;
    f32 param;
} pxl8_node;

typedef struct pxl8_graph {
    u32 capacity;
    u32 count;
    pxl8_node* nodes;
    u8 output_reg;
    u32 seed;
} pxl8_graph;

typedef struct pxl8_graph_context {
    f32 regs[256];
    u32 seed;
} pxl8_graph_context;

#ifdef __cplusplus
extern "C" {
#endif

f32 pxl8_graph_eval(const pxl8_graph* graph, pxl8_graph_context* ctx);
void pxl8_graph_eval_texture(const pxl8_graph* graph, u8* buffer, i32 width, i32 height);

u8 pxl8_graph_add_node(pxl8_graph* graph, pxl8_graph_op op, u8 in0, u8 in1, u8 in2, u8 in3, f32 param);
void pxl8_graph_clear(pxl8_graph* graph);
pxl8_graph* pxl8_graph_create(u32 capacity);
void pxl8_graph_destroy(pxl8_graph* graph);
void pxl8_graph_set_output(pxl8_graph* graph, u8 reg);
void pxl8_graph_set_seed(pxl8_graph* graph, u32 seed);

#ifdef __cplusplus
}
#endif
