#pragma once

#include <vector>
#include <string>
#include "./units.hpp"
#include "../utils/flags.hpp"

namespace world::generator { class Abstract; }
namespace world::module {

/// Index to registry element
template<typename Target>
struct id {
    id(size_t index): val(index) { }
    size_t val;
};

struct _module{ };
using module_id = id<_module>;

/// Material properties without texture
struct raw_material {
    raw_material(const std::string& name, float roughness = 0, bool invisibility = false, bool transparency = false, bool solidity = true):
        name(name), roughness(roughness), invisibility(invisibility), transparency(transparency), solidity(solidity) { }

    /// Material in-module name
    std::string name;
    /// Material roughness
    /// -1: slope, 0: normal, 1: cube
    float roughness;
    /// Material is interactive
    bool invisibility;
    /// Material is see throw
    bool transparency;
    /// Material is solid
    bool solidity;
    //uint16_t break_to
};

/// Material properties
struct material: raw_material {
    material(const std::string& name, const std::string& texture, float roughness = 0, bool invisibility = false, bool transparency = false, bool solidity = true):
        raw_material(name, roughness, invisibility, transparency, solidity), texture(texture) { }
    material(const std::string& name): material(name, name) { }

    std::string texture;
};
using material_id = id<material>;
struct _texture{ };
using texture_id = id<_texture>;

// MAYBE: templatized
struct materials {
    std::vector<std::string> names;
    std::vector<size_t> module_ids;
    std::vector<size_t> texture_ids;
    std::vector<float> roughnesses;
    std::vector<bool> invisibilities;
    std::vector<bool> transparencies;
    std::vector<bool> solidities;

    size_t size() const { return names.size(); }
    void reserve(size_t n) {
        names.reserve(n);
        module_ids.reserve(n);
        texture_ids.reserve(n);
        roughnesses.reserve(n);
        invisibilities.reserve(n);
        transparencies.reserve(n);
        solidities.reserve(n);
    }
    size_t push(const material& m, size_t mod_id, size_t tex_id) {
        const auto i = size();
        names.push_back(m.name);
        module_ids.push_back(mod_id);
        texture_ids.push_back(tex_id);
        roughnesses.push_back(m.roughness);
        invisibilities.push_back(m.invisibility);
        transparencies.push_back(m.transparency);
        solidities.push_back(m.solidity);
        return i;
    }
};

// Model properties
struct model {
    /// render::Model::Data serialized
    using data = std::vector<char>;

    model(const std::string& n, const data& m, const geometry::faabb& col, glm::vec3 sc = glm::vec3(1)):
        name(n), dt(m), collision(col), scale(sc) { }
    model(const std::string& n, data&& m, const geometry::faabb& col, glm::vec3 sc = glm::vec3(1)):
        name(n), dt(std::move(m)), collision(col), scale(sc) { }

    std::string name;
    data dt;
    /// Collision box
    geometry::faabb collision;
    /// Visual model size ratio
    glm::vec3 scale;
};
struct registered_model: model {
    registered_model(const model& m, size_t mod_id):
        model(m), module_id(mod_id) { }

    size_t module_id;
};
using model_id = id<model>;

// Generator properties
struct generator {
    using params = const void*;
    typedef owner<world::generator::Abstract*> factory_fn(params);

    generator(const std::string& n, factory_fn* f):
        name(n), factory(f) { }

    std::string name;
    factory_fn * factory;
};

struct registered_generator: generator {
    registered_generator(const generator& g, size_t mod_id):
        generator(g), module_id(mod_id) { }

    size_t module_id;
};
using generator_id = id<generator>;
}