// MIT License (see LICENSE or src/mgl.lua)
// Copyright (c) 2020 ImagicTheCat

// Benchmark transformation matrix computation for a list of entities.
// https://glm.g-truc.net

#include <iostream>
#include <sstream>
#include <ctime>
#include <cstdlib>
#include <vector>
#include <glm/glm.hpp>
#include <glm/ext.hpp>

double get_clock(){ return clock() / (double)CLOCKS_PER_SEC; }

struct Entity{
  glm::dvec3 position, rotation, scale;
  glm::dmat4 transform;
};

float randf(){ return rand()/(float)RAND_MAX; } 

int main(int argc, char **argv)
{
  std::stringstream ss(argc > 1 ? argv[1] : "1000");
  size_t ents, ticks;
  ss >> ents;
  ss.clear();
  ss.str(argc > 2 ? argv[2] : "600");
  ss >> ticks;

  std::vector<Entity> entities(ents);
  for(size_t i = 0; i < entities.size(); i++){
    entities[i].position = glm::dvec3(randf(), randf(), randf());
    entities[i].rotation = glm::dvec3(randf(), randf(), randf());
    entities[i].scale = glm::dvec3(randf(), randf(), randf());
  }

  double start = get_clock();
  
  for(int i = 0; i < ticks; i++){
    for(int j = 0; j < entities.size(); j++){
      Entity &e = entities[j];

      e.transform = glm::translate(glm::dmat4(), e.position)
        * glm::rotate(glm::dmat4(), e.rotation.x, glm::dvec3(1,0,0))
        * glm::rotate(glm::dmat4(), e.rotation.y, glm::dvec3(0,1,0))
        * glm::rotate(glm::dmat4(), e.rotation.z, glm::dvec3(0,0,1))
        * glm::scale(glm::dmat4(), e.scale);

      // The following code is faster, but is not as good for the benchmark
      // comparison. The goal is not just about having the fastest program, it
      // is also about having a clean and simple API; I believe this code is
      // skipping a lot of computation by updating the matrix directly, which
      // MGL could implement too (but will not). The above code is not perfect
      // either, because MGL is directly creating the matrix instead of applying
      // a transformation to a new matrix.

      /*
      glm::dmat4 m;
      m = glm::scale(m, e.scale);
      m = glm::rotate(m, e.rotation.z, az);
      m = glm::rotate(m, e.rotation.y, ay);
      m = glm::rotate(m, e.rotation.x, ax);
      m = glm::translate(m, e.position);
      e.transform = m;
      */
    }
  }

  std::cout << "[transform]\t" << (get_clock() - start) << std::endl;

  return 0;
}
