// This code example is created for educational purpose
// by Thorsten Thormaehlen (contact: www.thormae.de).
// It is distributed without any warranty.

#include <QApplication>
#include <QOpenGLWidget>
#include <QOpenGLFunctions_2_0>
#include <QKeyEvent>
#include <QTimer>
#include <QMessageBox>

#include <iostream>
using namespace std;

#include <math.h>

#include <gl/glu.h>
class Renderer : protected QOpenGLFunctions_2_0 {

public:
  float t;
  int mode;
private:
  GLuint bufID;
  int vertSize;

public:
  // constructor
  Renderer() : t(0.0), mode(0), bufID(0), vertSize(0) {}

public:
 void init() {
    initializeOpenGLFunctions();
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_DEPTH);

    // generating VBO input data
    std::vector<float> dataIn;
    unsigned ayimutSegs = 1000;
    unsigned polarSegs = 1000;
    float ayimutStep = 2.0f * M_PI / float(ayimutSegs);
    float polarStep = M_PI / float(polarSegs);
    float r = 1.0f;
    vertSize = 0;
    for(unsigned m=0; m < ayimutSegs; m++) {
      for(unsigned n=0; n < polarSegs; n++) {
        float phi = ayimutStep*m;
        float theta = polarStep * n;

        // compute xyz from spherical coordinates
        float x = r * sin(theta) * cos(phi);
        float y = r * sin(theta) * sin(phi);
        float z = r * cos(theta);

        dataIn.push_back(x);
        dataIn.push_back(y);
        dataIn.push_back(z);
        vertSize++;
      }
    }

    // generating VBO
    glGenBuffers(1, &bufID);
    glBindBuffer(GL_ARRAY_BUFFER, bufID);
    glBufferData(GL_ARRAY_BUFFER, dataIn.size()*sizeof(float), &dataIn[0], GL_STATIC_DRAW);
  }

  void resize(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective (30.0, (float)w/(float)h, 0.1, 50.0);
  }

  void display() {
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    // set camera
    gluLookAt(3.5, -1.0, 3.5, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
    // draw scene
    glRotatef(t, 0.0f, 0.0f, 1.0f);

    // activating VBO
    glBindBuffer(GL_ARRAY_BUFFER, bufID);
    int stride = 0; // if stride is 0, the vertices are understood to be tightly packed in the array
    glVertexPointer(3, GL_FLOAT, stride, NULL);
    glEnableClientState(GL_VERTEX_ARRAY);

    if(mode == 0) {
      glColor3f(1.0f,1.0f,1.0f);
    }else{
      glColorPointer(3, GL_FLOAT, stride, NULL);
      glEnableClientState(GL_COLOR_ARRAY);
    }

    // render VBO
    glDrawArrays(GL_POINTS, 0, vertSize);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
  }

  void dispose() {
    if(bufID !=0) glDeleteBuffers( 1, &bufID);
  }

};


class MyWidget : public QOpenGLWidget {

private:
  Renderer *renderer;
  QTimer *timer;

public:
  MyWidget(QWidget *parent = NULL) : QOpenGLWidget(parent) {
    this->setWindowTitle("Press 1 to use the VBO data for vertex colors as well");
    this->resize(320, 320);
    renderer = new Renderer();
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(update()));
    timer->start(30);
  }

  ~MyWidget() {
    makeCurrent();
    renderer->dispose();
    doneCurrent();
    delete renderer;
    delete timer;
  }

protected:
  void initializeGL() { renderer->init(); }
  void resizeGL(int w, int h){ renderer->resize(w, h); }
  void paintGL() {
      float offset = 1.0f;
      renderer->t += offset;
      renderer->display();
  }
  void keyPressEvent(QKeyEvent* event){
    bool redraw = false;
    switch(event->key()) {
    case '1':
      if(renderer->mode == 1) renderer->mode = 0;
      else renderer->mode = 1;
      redraw = true;
      break;
    }
    if(redraw) {
      this->update();
    }
  }
};

int main (int argc, char* argv[]) {
    // create a QApplication object that handles initialization,
    // finalization, and the main event loop
    QApplication appl(argc, argv);
    MyWidget widget;  // create a widget
    widget.show(); //show the widget and its children
    return appl.exec(); // execute the application
}
