/* This file is part of Clementine.
   Copyright 2004, Adam Pigg <adam@piggz.co.uk>
   Copyright 2009, David Sansome <davidsansome@gmail.com>
   Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
   Copyright 2014, John Maguire <john.maguire@gmail.com>

   Clementine is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   Clementine is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with Clementine.  If not, see <http://www.gnu.org/licenses/>.
*/

/* Original Author:  Adam Pigg  <adam@piggz.co.uk>  2004
 */

#include <config.h>

#ifdef HAVE_QGLWIDGET

#include <kdebug.h>

#include <cmath>

#include "glanalyzer.h"

GLAnalyzer::GLAnalyzer(QWidget* parent)
    : Analyzer::Base3D(parent, 15), m_oldy(32, -10.0f), m_peaks(32) {}

GLAnalyzer::~GLAnalyzer() {}

void GLAnalyzer::analyze(const Scope& s) {
  // kdDebug() << "Scope Size: " << s.size() << endl;
  /* Scope t(32);
  if (s.size() != 32)
  {
          Analyzer::interpolate(s, t);
  }
  else
  {
          t = s;
  }*/
  uint offset = 0;
  static float peak;
  float mfactor = 0.0;
  static int drawcount;

  if (s.size() == 64) {
    offset = 8;
  }

  glRotatef(0.25f, 0.0f, 1.0f, 0.5f);  // Rotate the scene
  drawFloor();
  drawcount++;
  if (drawcount > 25) {
    drawcount = 0;
    peak = 0.0;
  }

  for (uint i = 0; i < 32; i++) {
    if (s[i] > peak) {
      peak = s[i];
    }
  }

  mfactor = 20 / peak;
  for (uint i = 0; i < 32; i++) {
    // Calculate new horizontal position (x) depending on number of samples
    x = -16.0f + i;

    // Calculating new vertical position (y) depending on the data passed by
    // amarok
    y = static_cast<float>(s[i + offset] *
                           mfactor);  // This make it kinda dynamically
                                      // resize depending on the data

    // Some basic bounds checking
    if (y > 30)
      y = 30;
    else if (y < 0)
      y = 0;

    if ((y - m_oldy[i]) < -0.6f) {
      y = m_oldy[i] - 0.7f;
    }

    if (y < 0.0f) {
      y = 0.0f;
    }

    m_oldy[i] = y;  // Save value as last value

    // Peak Code
    if (m_oldy[i] > m_peaks[i].level) {
      m_peaks[i].level = m_oldy[i];
      m_peaks[i].delay = 30;
    }

    if (m_peaks[i].delay > 0) {
      m_peaks[i].delay--;
    }

    if (m_peaks[i].level > 1.0f) {
      if (m_peaks[i].delay <= 0) {
        m_peaks[i].level -= 0.4f;
      }
    }
    // Draw the bar
    drawBar(x, y);
    drawPeak(x, m_peaks[i].level);
  }

  updateGL();
}

void GLAnalyzer::initializeGL() {
  // Clear frame (next fading will be preferred to clearing)
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);  // Set clear color to black
  glClear(GL_COLOR_BUFFER_BIT);

  // Set the shading model
  glShadeModel(GL_SMOOTH);

  // Set the polygon mode to fill
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

  // Enable depth testing for hidden line removal
  glEnable(GL_DEPTH_TEST);

  // Set blend parameters for 'composting alpha'
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

void GLAnalyzer::resizeGL(int w, int h) {
  glViewport(0, 0, (GLint)w, (GLint)h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-16.0f, 16.0f, -10.0f, 10.0f, -50.0f, 100.0f);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void GLAnalyzer::paintGL() {
  glMatrixMode(GL_MODELVIEW);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_BLEND);
  glPushMatrix();
  glLoadIdentity();
  glBegin(GL_TRIANGLE_STRIP);
  glColor4f(0.0f, 0.0f, 0.1f, 0.08f);
  glVertex2f(20.0f, 10.0f);
  glVertex2f(-20.0f, 10.0f);
  glVertex2f(20.0f, -10.0f);
  glVertex2f(-20.0f, -10.0f);
  glEnd();
  glPopMatrix();
  glDisable(GL_BLEND);
  glEnable(GL_DEPTH_TEST);
  glClear(GL_DEPTH_BUFFER_BIT);
#endif
  // swapBuffers();

  glFlush();
}

void GLAnalyzer::drawBar(float xPos, float height) {
  glPushMatrix();

  // Sets color to blue
  // Set the colour depending on the height of the bar
  glColor3f((height / 40) + 0.5f, (height / 40) + 0.625f, 1.0f);
  glTranslatef(xPos, -10.0f, 0.0f);

  glScalef(1.0f, height, 3.0f);
  drawCube();

  // Set colour to full blue
  // glColor3f(0.0f, 0.0f, 1.0f);
  // drawFrame();
  glPopMatrix();
}

void GLAnalyzer::drawFloor() {
  glPushMatrix();

  // Sets color to amarok blue
  glColor3f(0.5f, 0.625f, 1.0f);
  glTranslatef(-16.0f, -11.0f, -4.0f);

  glScalef(32.0f, 1.0f, 10.0f);
  drawCube();

  // Set colour to full blue
  glColor3f(0.0f, 0.0f, 1.0f);
  drawFrame();
  glPopMatrix();
}

void GLAnalyzer::drawPeak(float xPos, float ypos) {
  glPushMatrix();

  // Set the colour to red
  glColor3f(1.0f, 0.0f, 0.0f);
  glTranslatef(xPos, ypos - 10.0f, 0.0f);

  glScalef(1.0f, 1.0f, 3.0f);
  drawCube();

  glPopMatrix();
}

void GLAnalyzer::drawCube() {
  glPushMatrix();
  glBegin(GL_POLYGON);

  // This is the top face
  glVertex3f(0.0f, 1.0f, 0.0f);
  glVertex3f(1.0f, 1.0f, 0.0f);
  glVertex3f(1.0f, 1.0f, 1.0f);
  glVertex3f(0.0f, 1.0f, 1.0f);
  glVertex3f(0.0f, 1.0f, 0.0f);

  // This is the front face
  glVertex3f(0.0f, 0.0f, 0.0f);
  glVertex3f(1.0f, 0.0f, 0.0f);
  glVertex3f(1.0f, 1.0f, 0.0f);
  glVertex3f(0.0f, 1.0f, 0.0f);
  glVertex3f(0.0f, 0.0f, 0.0f);

  // This is the right face
  glVertex3f(1.0f, 0.0f, 0.0f);
  glVertex3f(1.0f, 0.0f, 1.0f);
  glVertex3f(1.0f, 1.0f, 1.0f);
  glVertex3f(1.0f, 1.0f, 0.0f);
  glVertex3f(1.0f, 0.0f, 0.0f);

  // This is the left face
  glVertex3f(0.0f, 0.0f, 0.0f);
  glVertex3f(0.0f, 0.0f, 1.0f);
  glVertex3f(0.0f, 1.0f, 1.0f);
  glVertex3f(0.0f, 1.0f, 0.0f);
  glVertex3f(0.0f, 0.0f, 0.0f);

  // This is the bottom face
  glVertex3f(0.0f, 0.0f, 0.0f);
  glVertex3f(1.0f, 0.0f, 0.0f);
  glVertex3f(1.0f, 0.0f, 1.0f);
  glVertex3f(0.0f, 0.0f, 1.0f);
  glVertex3f(0.0f, 0.0f, 0.0f);

  // This is the back face
  glVertex3f(0.0f, 0.0f, 1.0f);
  glVertex3f(1.0f, 0.0f, 1.0f);
  glVertex3f(1.0f, 1.0f, 1.0f);
  glVertex3f(0.0f, 1.0f, 1.0f);
  glVertex3f(0.0f, 0.0f, 1.0f);

  glEnd();
  glPopMatrix();
}
void GLAnalyzer::drawFrame() {
  glPushMatrix();
  glBegin(GL_LINES);

  // This is the top face
  glVertex3f(0.0f, 1.0f, 0.0f);
  glVertex3f(1.0f, 1.0f, 0.0f);
  glVertex3f(1.0f, 1.0f, 1.0f);
  glVertex3f(0.0f, 1.0f, 1.0f);
  glVertex3f(0.0f, 1.0f, 0.0f);

  // This is the front face
  glVertex3f(0.0f, 0.0f, 0.0f);
  glVertex3f(1.0f, 0.0f, 0.0f);
  glVertex3f(1.0f, 1.0f, 0.0f);
  glVertex3f(0.0f, 1.0f, 0.0f);
  glVertex3f(0.0f, 0.0f, 0.0f);

  // This is the right face
  glVertex3f(1.0f, 0.0f, 0.0f);
  glVertex3f(1.0f, 0.0f, 1.0f);
  glVertex3f(1.0f, 1.0f, 1.0f);
  glVertex3f(1.0f, 1.0f, 0.0f);
  glVertex3f(1.0f, 0.0f, 0.0f);

  // This is the left face
  glVertex3f(0.0f, 0.0f, 0.0f);
  glVertex3f(0.0f, 0.0f, 1.0f);
  glVertex3f(0.0f, 1.0f, 1.0f);
  glVertex3f(0.0f, 1.0f, 0.0f);
  glVertex3f(0.0f, 0.0f, 0.0f);

  // This is the bottom face
  glVertex3f(0.0f, 0.0f, 0.0f);
  glVertex3f(1.0f, 0.0f, 0.0f);
  glVertex3f(1.0f, 0.0f, 1.0f);
  glVertex3f(0.0f, 0.0f, 1.0f);
  glVertex3f(0.0f, 0.0f, 0.0f);

  // This is the back face
  glVertex3f(0.0f, 0.0f, 1.0f);
  glVertex3f(1.0f, 0.0f, 1.0f);
  glVertex3f(1.0f, 1.0f, 1.0f);
  glVertex3f(0.0f, 1.0f, 1.0f);
  glVertex3f(0.0f, 0.0f, 1.0f);

  glEnd();
  glPopMatrix();
}

#endif
