//
// Created by fotyev on 2019-02-22.
//

#ifndef ZENGINE_STLLOADER_HPP
#define ZENGINE_STLLOADER_HPP

#include "shader.hpp"
#include <cstdio>

template <typename Callable>
unsigned stl_load(const char * filename, Callable f)
{
    FILE * in = fopen(filename, "rb");
    if(!in)
        throw std::runtime_error { std::string{"stl: no file: "} + filename};
    char desc[80];
    fread(desc,1,80, in);

    uint32_t trigs;
    fread(&trigs, 4, 1, in);

    for(unsigned i = 0; i < trigs; i++)
    {
        if(feof(in))
        {
            fclose(in);
            throw std::runtime_error{std::string{"stl: file ended: "} + filename};
        }

        float norm[3] = {0};
        fread(norm, 4, 3, in);
        float vert[3][3] = {{0}};
        fread(vert, 4, 3 * 3, in);
        uint16_t attr;
        fread(&attr, 2, 1, in);
        fseek(in, attr, SEEK_CUR);

        for(int j = 0; j < 3; j++)
        {
            f(qvm::vec3{vert[j][0], vert[j][1], vert[j][2]}, qvm::vec3{norm[0],norm[1],norm[2]});
        }
    }
    fclose(in);
    return trigs;
}

class stl_renderer
{
public:
    /*< define_attributes('attributes',{
a_position=>['qvm::vec3','float'],
a_vnormal=>['qvm::vec<int8_t, 3>','int8_t','true'],
});%*/
#pragma pack(push,1)
struct attributes
{
qvm::vec3 	a_position;
qvm::vec<int8_t, 3> 	a_vnormal;
static void setup(program& p) {
    GET_ATTRIBUTE(p,a_position).setup(sizeof(qvm::vec3)/sizeof(float), type_to_gl<float>::value, false, sizeof(attributes), offsetof(attributes, a_position));
    GET_ATTRIBUTE(p,a_vnormal).setup(sizeof(qvm::vec<int8_t, 3>)/sizeof(int8_t), type_to_gl<int8_t>::value, true, sizeof(attributes), offsetof(attributes, a_vnormal));
}
static void disable(program& p) {
    GET_ATTRIBUTE(p,a_position).disable();
    GET_ATTRIBUTE(p,a_vnormal).disable();
}
};
#pragma pack(pop)
/*>*/

    program shader;
private:
    vector_vertex_buffer<attributes> vertices;
    std::vector<unsigned int> models;
public:
    stl_renderer(const char * prog_file) : shader{prog_file} {}


    unsigned load(const char * filename)
    {
        unsigned idx = models.size();
        models.push_back(vertices.size());
        stl_load(filename, [this](qvm::vec3 vertice, qvm::vec3 normal){
            vertices.push_back(attributes{std::move(vertice), std::move(normal) * 127});
        });
        vertices.upload();
        return idx;
    }

    void pop()
    {
        vertices.resize(models.back());
        models.pop_back();
    }

    // models loaded
    unsigned size() const { return models.size(); }

    void draw(unsigned model = 0)
    {
        assume(models.size() > model);
        int count = (models.size() > (model+1)) ? (int)(models[model+1]-models[model]) : -1;
        vertices.draw(shader, GL_TRIANGLES, models[model], count);
    }

};

#endif //ZENGINE_STLLOADER_HPP
