local ffi = require("ffi")
local C = ffi.C
local core = require("pxl8.core")

local procgen = {}

procgen.OP_CONST = C.PXL8_OP_CONST
procgen.OP_INPUT_AGE = C.PXL8_OP_INPUT_AGE
procgen.OP_INPUT_SEED = C.PXL8_OP_INPUT_SEED
procgen.OP_INPUT_TIME = C.PXL8_OP_INPUT_TIME
procgen.OP_INPUT_X = C.PXL8_OP_INPUT_X
procgen.OP_INPUT_Y = C.PXL8_OP_INPUT_Y

procgen.OP_ABS = C.PXL8_OP_ABS
procgen.OP_ADD = C.PXL8_OP_ADD
procgen.OP_CEIL = C.PXL8_OP_CEIL
procgen.OP_CLAMP = C.PXL8_OP_CLAMP
procgen.OP_COS = C.PXL8_OP_COS
procgen.OP_DIV = C.PXL8_OP_DIV
procgen.OP_FLOOR = C.PXL8_OP_FLOOR
procgen.OP_FRACT = C.PXL8_OP_FRACT
procgen.OP_GRADIENT_LINEAR = C.PXL8_OP_GRADIENT_LINEAR
procgen.OP_GRADIENT_RADIAL = C.PXL8_OP_GRADIENT_RADIAL
procgen.OP_LERP = C.PXL8_OP_LERP
procgen.OP_MAX = C.PXL8_OP_MAX
procgen.OP_MIN = C.PXL8_OP_MIN
procgen.OP_MOD = C.PXL8_OP_MOD
procgen.OP_MUL = C.PXL8_OP_MUL
procgen.OP_NEGATE = C.PXL8_OP_NEGATE
procgen.OP_NOISE_FBM = C.PXL8_OP_NOISE_FBM
procgen.OP_NOISE_PERLIN = C.PXL8_OP_NOISE_PERLIN
procgen.OP_NOISE_RIDGED = C.PXL8_OP_NOISE_RIDGED
procgen.OP_NOISE_TURBULENCE = C.PXL8_OP_NOISE_TURBULENCE
procgen.OP_NOISE_VALUE = C.PXL8_OP_NOISE_VALUE
procgen.OP_POW = C.PXL8_OP_POW
procgen.OP_QUANTIZE = C.PXL8_OP_QUANTIZE
procgen.OP_SELECT = C.PXL8_OP_SELECT
procgen.OP_SIN = C.PXL8_OP_SIN
procgen.OP_SMOOTHSTEP = C.PXL8_OP_SMOOTHSTEP
procgen.OP_SQRT = C.PXL8_OP_SQRT
procgen.OP_SUB = C.PXL8_OP_SUB
procgen.OP_VORONOI_CELL = C.PXL8_OP_VORONOI_CELL
procgen.OP_VORONOI_EDGE = C.PXL8_OP_VORONOI_EDGE
procgen.OP_VORONOI_ID = C.PXL8_OP_VORONOI_ID

local Graph = {}
Graph.__index = Graph

function Graph.new(capacity)
    capacity = capacity or 64
    local ptr = C.pxl8_graph_create(capacity)
    if ptr == nil then
        return nil
    end
    return setmetatable({ _ptr = ptr }, Graph)
end

function Graph:destroy()
    if self._ptr then
        C.pxl8_graph_destroy(self._ptr)
        self._ptr = nil
    end
end

function Graph:clear()
    C.pxl8_graph_clear(self._ptr)
end

function Graph:add_node(op, in0, in1, in2, in3, param)
    return C.pxl8_graph_add_node(self._ptr, op, in0 or 0, in1 or 0, in2 or 0, in3 or 0, param or 0)
end

function Graph:set_output(reg)
    C.pxl8_graph_set_output(self._ptr, reg)
end

function Graph:set_seed(seed)
    C.pxl8_graph_set_seed(self._ptr, seed)
end

function Graph:eval_texture(width, height)
    width = width or 64
    height = height or 64
    local buffer = ffi.new("u8[?]", width * height)
    C.pxl8_graph_eval_texture(self._ptr, buffer, width, height)
    local tex_id = C.pxl8_gfx_create_texture(core.gfx, buffer, width, height)
    if tex_id < 0 then
        return nil
    end
    return tex_id
end

procgen.Graph = Graph

function procgen.create_graph(capacity)
    return Graph.new(capacity)
end

return procgen
