#ifndef UTILS_H
#define UTILS_H

#include <vector>
#include <string>
#include <glm/glm.hpp>
#include <algorithm>
#include <functional>
#include <thread>

class ScriptNode;
class btRigidBody;
class GeometryNode;
class SceneNode;

namespace utils
{
std::vector<std::string> split(const std::string &line, char sep);
void initScriptingUtilsFunctions(ScriptNode* scriptNode);
void initStandardScene();

/**
 * @warning collisions on static meshes does not work if the transform is modified after the rigidbody construction
 */
btRigidBody* buildStaticCollider(GeometryNode* node);

SceneNode* createTerrain(const std::string &heightMap, const std::string &colorMap, float maxHeight = 100);

template <typename T>
void parallelFor(T startVal, T endVal, std::function<void(T)> fun, unsigned int nbThreads = 0)
{
    std::vector<std::thread*> threads;

    // each thread will execute this lambda once
    auto taskLoop = [=](T start, T end){
        for(T i=start; i<end; ++i)
            fun(i);
    };

    // check parameters
    if(nbThreads == 0)
        nbThreads = std::thread::hardware_concurrency();
    if(nbThreads == 0)
        nbThreads = 1;

    T totalSize = endVal - startVal;
    if(totalSize < nbThreads)
        return; // nope

    // start threads
    int interval = totalSize/nbThreads;
    for(unsigned int i=0; i<nbThreads; ++i)
    {
        T end = (i == nbThreads - 1) ? endVal : startVal + interval;
        threads.push_back(new std::thread(taskLoop, startVal, end));
        startVal = end;
    }

    // synchronize threads
    for(std::thread* t : threads)
    {
        t->join();
        delete t;
    }
}

} // namespace utils

#endif // UTILS_H
