// 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 <iostream>
using namespace std;

class Renderer : protected QOpenGLFunctions_2_0 {

public:
  int mode;
  double alpha;

public:
  Renderer() : mode(1), alpha(-45.0) {}

public:

  void init() {
    initializeOpenGLFunctions();
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_DEPTH);
  }

  void display() {
    glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    float m[16]; // identity
    glGetFloatv(GL_MODELVIEW_MATRIX, m);
    float angle = (M_PI / 180.0f) * float(alpha);
    if(mode == 1) { // cavalier
      m[2*4+0] = -cos(angle);
      m[2*4+1] = sin(angle);
    }
    if(mode == 2) { // cabinet
      m[2*4+0] = -cos(angle)/2.0f;
      m[2*4+1] = sin(angle)/2.0f;
    }
    glMultMatrixf(m);

    glColor3f(0.0f, 0.0f, 1.0f);
    drawCoordinateAxisZ();
    glColor3f(0.0f, 1.0f, 0.0f);
    drawCoordinateAxisY();
    glColor3f(1.0f, 0.0f, 0.0f);
    drawCoordinateAxisX();

    drawUnitCube();
  }

  void resize(int w, int h) {
    double aspect = double(w)/double(h);
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-2.0*aspect, 2.0*aspect, -2.0, 2.0, -5.0, 5.0);
  }

  void dispose() {
  }

private:
  void drawCoordinateAxisZ() {
    glLineWidth(2);
    glBegin(GL_LINES);
    glVertex3f(0.0f, 0.0f, 0.0f); // z-axis
    glVertex3f(0.0f, 0.0f, 2.0f);
    glEnd();
    glLineWidth(1);

    // z-axis tip
    glBegin(GL_TRIANGLES);
    glVertex3f( 0.0f, 0.0f, 2.0f);
    glVertex3f(-0.05f, 0.05f, 1.9f);
    glVertex3f( 0.05f, 0.05f, 1.9f);
    glVertex3f( 0.0f,  0.0f, 2.0f);
    glVertex3f( 0.05f, -0.05f, 1.9f);
    glVertex3f(-0.05f, -0.05f, 1.9f);
    glVertex3f( 0.0f,  0.0f, 2.0f);
    glVertex3f( 0.05f,  0.05f, 1.9f);
    glVertex3f( 0.05f, -0.05f, 1.9f);
    glVertex3f( 0.0f,  0.0f, 2.0f);
    glVertex3f(-0.05f, -0.05f, 1.9f);
    glVertex3f(-0.05f,  0.05f, 1.9f);
    glEnd();
    glBegin(GL_POLYGON);
    glVertex3f( 0.05f, -0.05f, 1.9f);
    glVertex3f( 0.05f,  0.05f, 1.9f);
    glVertex3f(-0.05f,  0.05f, 1.9f);
    glVertex3f(-0.05f, -0.05f, 1.9f);
    glEnd();
  }

  void drawCoordinateAxisX() {
      glPushMatrix();
      glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
      drawCoordinateAxisZ();
      glPopMatrix();
  }

  void drawCoordinateAxisY() {
      glPushMatrix();
      glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
      drawCoordinateAxisZ();
      glPopMatrix();
  }

  void drawUnitCube() {
    glColor3f(0.0f,0.0f,0.0f);
    glBegin(GL_LINE_LOOP);
    glVertex3f(0.0f, 1.0f, 1.0f);
    glVertex3f(0.0f,0.0f, 1.0f);
    glVertex3f( 1.0f,0.0f, 1.0f);
    glVertex3f( 1.0f, 1.0f, 1.0f);
    glEnd();
    glBegin(GL_LINE_LOOP);
    glVertex3f( 1.0f, 1.0f,0.0f);
    glVertex3f( 1.0f,0.0f,0.0f);
    glVertex3f(0.0f,0.0f,0.0f);
    glVertex3f(0.0f, 1.0f,0.0f);
    glEnd();
    glBegin(GL_LINE_LOOP);
    glVertex3f( 1.0f, 1.0f, 1.0f);
    glVertex3f( 1.0f,0.0f, 1.0f);
    glVertex3f( 1.0f,0.0f,0.0f);
    glVertex3f( 1.0f, 1.0f,0.0f);
    glEnd();
    glBegin(GL_LINE_LOOP);
    glVertex3f(0.0f, 1.0f,0.0f);
    glVertex3f(0.0f,0.0f,0.0f);
    glVertex3f(0.0f,0.0f, 1.0f);
    glVertex3f(0.0f, 1.0f, 1.0f);
    glEnd();
    glBegin(GL_LINE_LOOP);
    glVertex3f( 1.0f, 1.0f,0.0f);
    glVertex3f(0.0f, 1.0f,0.0f);
    glVertex3f(0.0f, 1.0f, 1.0f);
    glVertex3f( 1.0f, 1.0f, 1.0f);
    glEnd();
    glBegin(GL_LINE_LOOP);
    glVertex3f( 1.0f, 0.0f, 1.0f);
    glVertex3f(0.0f, 0.0f, 1.0f);
    glVertex3f(0.0f, 0.0f,0.0f);
    glVertex3f( 1.0f, 0.0f,0.0f);
    glEnd();
  }
};

class MyWidget : public QOpenGLWidget {

private:
  Renderer *renderer;

public:
  MyWidget(QWidget *parent = NULL) : QOpenGLWidget(parent) {
    this->setWindowTitle("Use 1 for cavalier, 2 for cabinet, 3 to change angle");
    this->resize(320, 320);
    renderer = new Renderer();
  }

  ~MyWidget() {
    makeCurrent();
    renderer->dispose();
    doneCurrent();
    delete renderer;
  }

protected:
  void initializeGL() { renderer->init(); }
  void resizeGL(int w, int h){ renderer->resize(w, h); }
  void paintGL() { renderer->display(); }
  void keyPressEvent(QKeyEvent* event){
    bool redraw = false;
    QString modeStr;
    switch(event->key()) {
    case '1':
      renderer->mode = 1;
      redraw = true;
      modeStr = QString("cavalier");
      break;
    case '2':
      renderer->mode = 2;
      redraw = true;
      modeStr = QString("cabinet");
      break;
    case '3':
      renderer->alpha += 2.0f;
      if(renderer->alpha > 360.0f) renderer->alpha = 0.0f;
      redraw = true;
      modeStr = QString("alpha: %1").arg(renderer->alpha);
      break;
    case '0':
      renderer->alpha = -45.0f;
      redraw = true;
      modeStr = QString("alpha: %1").arg(renderer->alpha);
      break;
    }
    if(redraw) {
      this->update();
      this->setWindowTitle(modeStr);
    }
  }

};

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
}
