#ifndef POLYHEDRON_DEMO_CREATE_SPHERE_H
#define POLYHEDRON_DEMO_CREATE_SPHERE_H

#include <CGAL/number_type_config.h>

#include <cmath>
#include <vector>

template <class FLOAT>
void create_flat_sphere(FLOAT R,
                        std::vector<FLOAT>& positions_spheres,
                        std::vector<FLOAT>& normals_spheres,
                        int prec = 36)
{
  // The smaller they are, the more precise the sphere will be

  // Must be divisors of 180 and 360
  const float rings = float(prec) / 2;
  const float sectors = float(prec);
  const float to_rad = static_cast<float>(CGAL_PI / 180.);

  float T, P;
  float x[4],y[4],z[4];

  //Top of the sphere
  for(float t=0; t<360.f; t+=sectors)
  {
    positions_spheres.push_back(0);
    positions_spheres.push_back(0);
    positions_spheres.push_back(R);

    normals_spheres.push_back(0);
    normals_spheres.push_back(0);
    normals_spheres.push_back(1);

    P = rings*to_rad;
    T = t*to_rad;
    x[1] = sin(P) * cos(T);
    y[1] = sin(P) * sin(T);
    z[1] = cos(P);
    positions_spheres.push_back(R * x[1]);
    positions_spheres.push_back(R * y[1]);
    positions_spheres.push_back(R * z[1]);

    normals_spheres.push_back(x[1]);
    normals_spheres.push_back(y[1]);
    normals_spheres.push_back(z[1]);

    //
    P = rings*to_rad;
    T = (t+sectors)*to_rad;
    x[2] = sin(P) * cos(T);
    y[2] = sin(P) * sin(T);
    z[2] = cos(P);
    positions_spheres.push_back(R * x[2]);
    positions_spheres.push_back(R * y[2]);
    positions_spheres.push_back(R * z[2]);

    normals_spheres.push_back(x[2]);
    normals_spheres.push_back(y[2]);
    normals_spheres.push_back(z[2]);
  }

  //Body of the sphere
  for(float p=rings; p<180.f-rings; p+=rings)
  {
    for(float t=0; t<360.f; t+=sectors)
    {
      //A
      P = p*to_rad;
      T = t*to_rad;
      x[0] = sin(P) * cos(T);
      y[0] = sin(P) * sin(T);
      z[0] = cos(P);

      positions_spheres.push_back(R * x[0]);
      positions_spheres.push_back(R * y[0]);
      positions_spheres.push_back(R * z[0]);

      normals_spheres.push_back(x[0]);
      normals_spheres.push_back(y[0]);
      normals_spheres.push_back(z[0]);

      //B
      P = (p+rings)*to_rad;
      T = t*to_rad;
      x[1] = sin(P) * cos(T);
      y[1] = sin(P) * sin(T);
      z[1] = cos(P);
      positions_spheres.push_back(R * x[1]);
      positions_spheres.push_back(R * y[1]);
      positions_spheres.push_back(R * z[1]);

      normals_spheres.push_back(x[1]);
      normals_spheres.push_back(y[1]);
      normals_spheres.push_back(z[1]);

      //C
      P = p*to_rad;
      T = (t+sectors)*to_rad;
      x[2] = sin(P) * cos(T);
      y[2] = sin(P) * sin(T);
      z[2] = cos(P);
      positions_spheres.push_back(R * x[2]);
      positions_spheres.push_back(R * y[2]);
      positions_spheres.push_back(R * z[2]);

      normals_spheres.push_back(x[2]);
      normals_spheres.push_back(y[2]);
      normals_spheres.push_back(z[2]);

      //D
      P = (p+rings)*to_rad;
      T = (t+sectors)*to_rad;
      x[3] = sin(P) * cos(T);
      y[3] = sin(P) * sin(T);
      z[3] = cos(P);
      positions_spheres.push_back(R * x[3]);
      positions_spheres.push_back(R * y[3]);
      positions_spheres.push_back(R * z[3]);

      normals_spheres.push_back(x[3]);
      normals_spheres.push_back(y[3]);
      normals_spheres.push_back(z[3]);

      positions_spheres.push_back(R * x[1]);
      positions_spheres.push_back(R * y[1]);
      positions_spheres.push_back(R * z[1]);

      normals_spheres.push_back(x[1]);
      normals_spheres.push_back(y[1]);
      normals_spheres.push_back(z[1]);

      positions_spheres.push_back(R * x[2]);
      positions_spheres.push_back(R * y[2]);
      positions_spheres.push_back(R * z[2]);

      normals_spheres.push_back(x[2]);
      normals_spheres.push_back(y[2]);
      normals_spheres.push_back(z[2]);
    }
  }

  //Bottom of the sphere
  for(float t=0; t<360.f; t+=sectors)
  {
    positions_spheres.push_back(0);
    positions_spheres.push_back(0);
    positions_spheres.push_back(-R);

    normals_spheres.push_back(0);
    normals_spheres.push_back(0);
    normals_spheres.push_back(-1);

    P = (180-rings)*to_rad;
    T = t*to_rad;
    x[1] = sin(P) * cos(T);
    y[1] = sin(P) * sin(T);
    z[1] = cos(P);
    positions_spheres.push_back(R * x[1]);
    positions_spheres.push_back(R * y[1]);
    positions_spheres.push_back(R * z[1]);

    normals_spheres.push_back(x[1]);
    normals_spheres.push_back(y[1]);
    normals_spheres.push_back(z[1]);

    P = (180-rings)*to_rad;
    T = (t+sectors)*to_rad;
    x[2] = sin(P) * cos(T);
    y[2] = sin(P) * sin(T);
    z[2] = cos(P);
    positions_spheres.push_back(R * x[2]);
    positions_spheres.push_back(R * y[2]);
    positions_spheres.push_back(R * z[2]);

    normals_spheres.push_back(x[2]);
    normals_spheres.push_back(y[2]);
    normals_spheres.push_back(z[2]);
  }
}

template <class FLOAT>
void create_flat_and_wire_sphere(FLOAT R,
                                 std::vector<FLOAT>& positions_spheres,
                                 std::vector<FLOAT>& normals_spheres,
                                 std::vector<FLOAT>& positions_wire_spheres,
                                 int prec = 36)
{
  // The smaller they are, the more precise the sphere will be

  // Must be a divisor of 360 and 180.
  const float rings = float(prec)/2;
  const float sectors = float(prec);
  const float to_rad = static_cast<float>(CGAL_PI / 180.0);

  create_flat_sphere(R, positions_spheres, normals_spheres);

  float T, P;
  float x[4],y[4],z[4];

  //Top of the sphere
  for(float t=0; t<360; t+=sectors)
  {
    positions_wire_spheres.push_back(0);
    positions_wire_spheres.push_back(0);
    positions_wire_spheres.push_back(R);

    P = rings*to_rad;
    T = t*to_rad;
    x[1] = sin(P) * cos(T);
    y[1] = sin(P) * sin(T);
    z[1] = cos(P);

    positions_wire_spheres.push_back(R * x[1]);
    positions_wire_spheres.push_back(R * y[1]);
    positions_wire_spheres.push_back(R * z[1]);

    positions_wire_spheres.push_back(R * x[1]);
    positions_wire_spheres.push_back(R * y[1]);
    positions_wire_spheres.push_back(R * z[1]);

    //
    P = rings*to_rad;
    T = (t+sectors)*to_rad;
    x[2] = sin(P) * cos(T);
    y[2] = sin(P) * sin(T);
    z[2] = cos(P);
    positions_wire_spheres.push_back(R * x[2]);
    positions_wire_spheres.push_back(R * y[2]);
    positions_wire_spheres.push_back(R * z[2]);

    positions_wire_spheres.push_back(R * x[2]);
    positions_wire_spheres.push_back(R * y[2]);
    positions_wire_spheres.push_back(R * z[2]);

    positions_wire_spheres.push_back(0);
    positions_wire_spheres.push_back(0);
    positions_wire_spheres.push_back(R);
  }

  //Body of the sphere
  for(float p=rings; p<180.f-rings; p+=rings)
  {
    for(float t=0; t<360.f; t+=sectors)
    {
      //A
      P = p*to_rad;
      T = t*to_rad;
      x[0] = sin(P) * cos(T);
      y[0] = sin(P) * sin(T);
      z[0] = cos(P);

      //B
      P = (p+rings)*to_rad;
      T = t*to_rad;
      x[1] = sin(P) * cos(T);
      y[1] = sin(P) * sin(T);
      z[1] = cos(P);

      //C
      P = p*to_rad;
      T = (t+sectors)*to_rad;
      x[2] = sin(P) * cos(T);
      y[2] = sin(P) * sin(T);
      z[2] = cos(P);

      //D
      P = (p+rings)*to_rad;
      T = (t+sectors)*to_rad;
      x[3] = sin(P) * cos(T);
      y[3] = sin(P) * sin(T);
      z[3] = cos(P);

      positions_wire_spheres.push_back(R * x[0]);
      positions_wire_spheres.push_back(R * y[0]);
      positions_wire_spheres.push_back(R * z[0]);

      positions_wire_spheres.push_back(R * x[1]);
      positions_wire_spheres.push_back(R * y[1]);
      positions_wire_spheres.push_back(R * z[1]);


      positions_wire_spheres.push_back(R * x[1]);
      positions_wire_spheres.push_back(R * y[1]);
      positions_wire_spheres.push_back(R * z[1]);

      positions_wire_spheres.push_back(R * x[3]);
      positions_wire_spheres.push_back(R * y[3]);
      positions_wire_spheres.push_back(R * z[3]);


      positions_wire_spheres.push_back(R * x[3]);
      positions_wire_spheres.push_back(R * y[3]);
      positions_wire_spheres.push_back(R * z[3]);

      positions_wire_spheres.push_back(R * x[2]);
      positions_wire_spheres.push_back(R * y[2]);
      positions_wire_spheres.push_back(R * z[2]);


      positions_wire_spheres.push_back(R * x[2]);
      positions_wire_spheres.push_back(R * y[2]);
      positions_wire_spheres.push_back(R * z[2]);

      positions_wire_spheres.push_back(R * x[0]);
      positions_wire_spheres.push_back(R * y[0]);
      positions_wire_spheres.push_back(R * z[0]);
    }
  }

  //Bottom of the sphere
  for(float t=0; t<360.f; t+=sectors)
  {
    P = (180-rings)*to_rad;
    T = t*to_rad;
    x[1] = sin(P) * cos(T);
    y[1] = sin(P) * sin(T);
    z[1] = cos(P);

    P = (180-rings)*to_rad;
    T = (t+sectors)*to_rad;
    x[2] = sin(P) * cos(T);
    y[2] = sin(P) * sin(T);
    z[2] = cos(P);

    positions_wire_spheres.push_back(0);
    positions_wire_spheres.push_back(0);
    positions_wire_spheres.push_back(-R);

    positions_wire_spheres.push_back(R * x[1]);
    positions_wire_spheres.push_back(R * y[1]);
    positions_wire_spheres.push_back(R * z[1]);


    positions_wire_spheres.push_back(R * x[1]);
    positions_wire_spheres.push_back(R * y[1]);
    positions_wire_spheres.push_back(R * z[1]);

    positions_wire_spheres.push_back(R * x[2]);
    positions_wire_spheres.push_back(R * y[2]);
    positions_wire_spheres.push_back(R * z[2]);


    positions_wire_spheres.push_back(R * x[2]);
    positions_wire_spheres.push_back(R * y[2]);
    positions_wire_spheres.push_back(R * z[2]);

    positions_wire_spheres.push_back(0);
    positions_wire_spheres.push_back(0);
    positions_wire_spheres.push_back(-R);
  }
}

#endif //POLYHEDRON_DEMO_DRAW_SPHERE_H
