#include "SparseMetaball.h"
#include "MetaballTables.h"

#include "../sleek/math/function.h"
#include "../sleek/library/glm/glm.hpp"
#include "../sleek/library/opengl/gl.h"

namespace sample
{
    SparseMetaball::SparseMetaball(int gridSize)
        : threshold(1.0), gridsize(gridSize)
    {
        cubes.reserve(gridsize*gridsize);
    }

    SparseMetaball::~SparseMetaball()
    {
    }

    void SparseMetaball::setThreshold(sleek::f32 i)
    {
        threshold = i;
    }

    sleek::f32 SparseMetaball::getThreshold() const noexcept
    {
        return threshold;
    }

    void SparseMetaball::move()
    {
        cubes.clear();
        sleek::u32 j;

        for(int i=0; i<balls.size(); i++)
        {
            sleek::f32 squaredRadius = balls[i].squaredRadius;
            for(int x=0; x<gridsize+1; x++)
            {
                for(int y=0; y<gridsize+1; y++)
                {
                    for(int z=0; z<gridsize+1; z++)
                    {
                        sleek::math::vec3f ballToPoint = sleek::math::vec3f{
                            (x*20.0f)/(gridsize)-10.0f,
                            (y*20.0f)/(gridsize)-10.0f,
                            (z*20.0f)/(gridsize)-10.0f
                        } - balls[i].position;

                        float squaredDistance = glm::length(ballToPoint);
                              squaredDistance = squaredDistance*squaredDistance;

                        if(squaredDistance == 0.0f)
                            squaredDistance = 0.0001f;

//                        cubes[j].value += squaredRadius/squaredDistance;
//                        cubes[j].normal += ballToPoint * squaredRadius/(squaredDistance*squaredDistance);
                    }

//                    for(int k = 0; k<8; ++k)
//                        cubeIndex |= (cubes[i].vertices[k]->value < threshold) << k;
//
//                    int usedEdges = edgeTable[cubeIndex];
//
//                    if(usedEdges==0 || usedEdges==255)
//                        continue;
                }
            }
        }
    }

    void SparseMetaball::update()
    {
        vertices.clear();
        indices.clear();

        unsigned int index = 0;

        for(int i=0; i<cubes.size(); i++)
        {
            unsigned char cubeIndex = 0;

//            for(int k = 0; k<8; ++k)
//                cubeIndex |= (cubes[i].vertices[k]->value < threshold) << k;

            int usedEdges = edgeTable[cubeIndex];

            if(usedEdges==0 || usedEdges==255)
                continue;

            for(int currentEdge=0; currentEdge<12; currentEdge++)
            {
                sleek::math::vertex *tmp = &vertices[index*12 + currentEdge];

                if(usedEdges & 1<<currentEdge)
                {
                    sleek::u32 v1i = verticesAtEndsOfEdges[currentEdge*2  ];
                    sleek::u32 v2i = verticesAtEndsOfEdges[currentEdge*2+1];

                    auto &v1_pos = cubes[i].position[v1i];
                    auto &v2_pos = cubes[i].position[v2i];
                    auto &v1_val = cubes[i].value[v1i];
                    auto &v2_val = cubes[i].value[v2i];
                    auto &v1_nrm = cubes[i].normal[v1i];
                    auto &v2_nrm = cubes[i].normal[v2i];

                    float delta = (threshold - v1_val) / (v2_val - v1_val);

                    tmp->Pos = (v1_pos + delta*(v2_pos - v1_pos)) * sleek::math::vec3f(2.0);
                    tmp->Normal = v1_nrm + delta*(v2_nrm - v1_nrm);
                    tmp->Color = sleek::math::pixel(0xFFFFFFFF);
                }

                vertices.push_back(*tmp);
            }

            for(unsigned int k=0; triTable[cubeIndex][k] != -1; k+=3)
            {
                indices.push_back(sleek::math::index<3>(
                    sleek::u32(index*12 + triTable[cubeIndex][k+0]),
                    sleek::u32(index*12 + triTable[cubeIndex][k+1]),
                    sleek::u32(index*12 + triTable[cubeIndex][k+2])
                ));
            }

            index++;
        }
    }
}