// 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 <QString>
#include <fstream>
#include <iostream>
#include <math.h>
using namespace std;

#include <GL/glu.h>

class Renderer : protected QOpenGLFunctions_2_0 {

public:
  float t;
  GLuint texID0, texID1;

public:
  Renderer() : t(0.0), texID0(0), texID1(0) {}

public:
  void display() {
    glClearColor(0.8f, 0.8f, 0.8f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    // set camera
    gluLookAt(0.0, 0.0, 8.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0);
    // draw scene
    glPushMatrix();
    glTranslatef(0.0f, -t*0.015f, 0.0f);
    drawTexturedPlaneBackground();
    glPopMatrix();

    glPushMatrix();
    glRotatef(-t, 0.0f, 0.0f, 1.0f);
    drawTexturedPlaneForeground();
    glPopMatrix();
  }

  void init() {
    initializeOpenGLFunctions();
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_DEPTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    texID0 = loadTexture(findFile("foreground.png", "qt", 5));
    texID1 = loadTexture(findFile("background.png", "qt", 5));
  }

  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 dispose() {
     if(texID0 !=0) glDeleteTextures(1, &texID0);
     if(texID1 !=0) glDeleteTextures(1, &texID1);
   }

private:

  void drawTexturedPlaneForeground() {
    glEnable(GL_TEXTURE_2D);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //GL_MODULATE
    glBindTexture(GL_TEXTURE_2D, texID0);

    glColor4f(1.0f,0.0f,0.0f,1.0f);
    glBegin(GL_POLYGON);
    glTexCoord2f(0.05f,0.95f); glVertex3f(-1.0f, 1.0f, 0.1f);
    glTexCoord2f(0.95f,0.95f); glVertex3f(-1.0f,-1.0f, 0.1f);
    glTexCoord2f(0.95f,0.05f); glVertex3f( 1.0f,-1.0f, 0.1f);
    glTexCoord2f(0.05f,0.05f); glVertex3f( 1.0f, 1.0f, 0.1f);
    glEnd();
    glDisable(GL_TEXTURE_2D);
  }

  void drawTexturedPlaneBackground() {
    glEnable(GL_TEXTURE_2D);

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //GL_MODULATE
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    glBindTexture(GL_TEXTURE_2D, texID1);

    glColor4f(0.0f,1.0f,0.0f,1.0f);
    glBegin(GL_POLYGON);
    glTexCoord2f(0.00f,1.00f); glVertex3f(-2.0f, 16.0f, 0.0f);
    glTexCoord2f(8.00f,1.00f); glVertex3f(-2.0f,-16.0f, 0.0f);
    glTexCoord2f(8.00f,0.00f); glVertex3f( 2.0f,-16.0f, 0.0f);
    glTexCoord2f(0.00f,0.00f); glVertex3f( 2.0f, 16.0f, 0.0f);
    glEnd();
    glDisable(GL_TEXTURE_2D);
  }

  GLuint loadTexture(const std::string &filename) {

    unsigned width;
    unsigned height;
    int level = 0;
    int border = 0;
    std::vector <unsigned char> imgData;

    QString fileName0(filename.c_str());
    QImage img(fileName0);
    if(img.isNull())  {
       return -1;
    }
    width = img.width();
    height = img.height();
    if(img.format() != QImage::Format_RGBA8888) {
      img = img.convertToFormat(QImage::Format_RGBA8888);
    }

    img = img.mirrored();

    // data is aligned in byte order
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    //request textureID
    GLuint textureID;
    glGenTextures( 1, &textureID);

    // bind texture
    glBindTexture( GL_TEXTURE_2D, textureID);

    //define how to filter the texture (important but ignore for now)
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    //texture colors should modulate the original color values
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    // specify the 2D texture map
    glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width, height, border, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());

    // return unique texture identifier
    return textureID;
  }

  bool fileExists(const std::string& filename)
  {
    ifstream myfile(filename.c_str());
    if (!myfile.is_open()) {
      return false;
    }
    myfile.close();
    return true;
  }

  std::string findFile(const std::string& filename, const std::string& subdir, int depth)
  {
    int counter = 0;
    std::string path("");

    while (counter < depth) {
      if (fileExists(path + filename)) return path + filename;
      if (fileExists(path + "/" + subdir + "/" + filename)) return path + "/" + subdir + "/" + filename;
      path += "../";
      counter++;
    }
    return filename;
  }
};

class MyWidget : public QOpenGLWidget {

private:
  Renderer *renderer;
  QTimer *timer;

public:
  MyWidget(QWidget *parent = NULL) : QOpenGLWidget(parent) {
    this->setWindowTitle("Blending of textures");
    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();
  }
};

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
}
