#include <iostream>
#include <blt/std/allocator.h>
#include <blt/gfx/window.h>
#include <blt/gfx/state.h>
#include <blt/gfx/stb/stb_image.h>
#include <blt/std/hashmap.h>
#include "blt/gfx/renderer/resource_manager.h"
#include "blt/gfx/renderer/batch_2d_renderer.h"
#include <variant>
#include <random>

blt::gfx::matrix_state_manager global_matrices;
blt::gfx::resource_manager resources;
blt::gfx::batch_renderer_2d renderer_2d(resources);

struct node;

blt::area_allocator<node, 32000> allocator;

std::variant<float, blt::vec3f> input_t;

enum class function_t
{
    // FUNC     // inputs
    ADD,        // 2
    SUB,        // 2
    MUL,        // 2
    DIV,        // 2
    
    LOG,        // 1
    EXP,        // 1
    SQRT,       // 1
    POW,        // 1
    QUAD,       // 1
    
    RANDOM,     // 0
    NOISE,      // 0
    COLOR,      // 0
    SCALAR      // 0
};

static constexpr int OPERATOR_COUNT = 13;

class tree;

struct node
{
    friend tree;
    private:
        node* left;
        node* right;
        function_t type;
        std::array<float, 7> data;
    public:
        
        static node* construct_random()
        {
            node* n = allocator.allocate(1);
            allocator.construct(n);
            
            std::random_device dev;
            std::mt19937_64 engine{dev()};
            std::uniform_real_distribution<float> dist(0.0f, 1.0f);
            for (float& f : n->data)
                f = dist(engine);
            
            std::uniform_int_distribution op(0, OPERATOR_COUNT - 1);
            n->type = static_cast<function_t>(op(engine));
            
            return n;
        }
        
        ~node()
        {
            allocator.destroy(left);
            allocator.deallocate(left, 1);
            allocator.destroy(right);
            allocator.deallocate(right, 1);
        }
};

class tree
{
    public:
        node* root;
    
    
};

void init()
{
    global_matrices.create_internals();
    resources.load_resources();
    renderer_2d.create();
}

void update(std::int32_t, std::int32_t)
{
    global_matrices.update();
    renderer_2d.render();
}

int main()
{
    blt::gfx::init(blt::gfx::window_data{"Window of GP test", init, update}.setSyncInterval(1));
    global_matrices.cleanup();
    resources.cleanup();
    renderer_2d.cleanup();
    blt::gfx::cleanup();
    return 0;
}
