//********************************************************************************
//*
//*  C++ finite element method for heat equation
//*  James sandham
//*  15 April 2015
//*
//********************************************************************************

//********************************************************************************
//
// HeatFE is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
//
//********************************************************************************

#include <QtGui>
#include <QtOpenGL>

#include <math.h>

#include "GLWidget.h"
#include<GL/glut.h>

GLWidget::GLWidget(QWidget *parent) : QGLWidget(parent)
{
  object = 0;
  xRot = 0;
  yRot = 0;
  zRot = 0;
  xTrans = 0;
  yTrans = 0;
  scale = 1.0;

  trolltechGreen = QColor::fromCmykF(0.40, 0.0, 1.0, 0.0);
  trolltechPurple = QColor::fromCmykF(0.39, 0.39, 0.0, 0.0);
}
  

GLWidget::~GLWidget()
{
  makeCurrent();
  glDeleteLists(object, 1);
}


QSize GLWidget::minimumSizeHint() const
{
  return QSize(50, 50);
}


QSize GLWidget::sizeHint() const
{
  return QSize(600, 600);
}


void GLWidget::setXRotation(int angle)
{
  normalizeAngle(&angle);
  if (angle != xRot) {
    xRot = angle;
    emit xRotationChanged(angle);
    updateGL();
  }
}


void GLWidget::setYRotation(int angle)
{
  normalizeAngle(&angle);
  if (angle != yRot) {
    yRot = angle;
    emit yRotationChanged(angle);
    updateGL();
  }
}


void GLWidget::setZRotation(int angle)
{
  normalizeAngle(&angle);
  if (angle != zRot) {
    zRot = angle;
    emit zRotationChanged(angle);
    updateGL();
  }
}


void GLWidget::setXTranslation(int increment){
  xTrans += increment/10.0;
  emit xTranslationChanged(increment);
  updateGL();  
}


void GLWidget::setYTranslation(int increment){
  yTrans += increment/10.0;
  emit yTranslationChanged(increment);
  updateGL();
}


void GLWidget::setScale(double factor){
  scale *= factor;
  emit scaleChanged(factor);
  updateGL();
}


void GLWidget::initializeGL()
{
  qglClearColor(trolltechPurple.dark());
  glShadeModel(GL_FLAT);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_CULL_FACE);
}


void GLWidget::paintGL()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();
  glTranslated(xTrans, yTrans, -10.0);
  glRotated(xRot / 16.0, 1.0, 0.0, 0.0);
  glRotated(yRot / 16.0, 0.0, 1.0, 0.0);
  glRotated(zRot / 16.0, 0.0, 0.0, 1.0);
  glScalef(scale,scale,scale);
  glCallList(object);
}


void GLWidget::resizeGL(int width, int height)
{
  int side = qMin(width, height);
  glViewport((width - side) / 2, (height - side) / 2, side, side);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-0.5, +0.5, +0.5, -0.5, 4.0, 15.0);
  glMatrixMode(GL_MODELVIEW);
}


void GLWidget::mousePressEvent(QMouseEvent *event)
{
  lastPos = event->pos();
}


void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
  int dx = event->x() - lastPos.x();
  int dy = event->y() - lastPos.y();

  if (event->buttons() & Qt::LeftButton) {
    setXRotation(xRot + 8 * dy);
    setYRotation(yRot + 8 * dx);
  } 
  else if (event->buttons() & Qt::RightButton) {
    setXRotation(xRot + 8 * dy);
    setZRotation(zRot + 8 * dx);
  }
  lastPos = event->pos();
}


void GLWidget::wheelEvent(QWheelEvent *event)
{
  if(event->orientation() == Qt::Vertical)
  {
    scale += (double)(event->delta()) / 10000;
    event->Quit;
  } 
}


void GLWidget::normalizeAngle(int *angle)
{
  while (*angle < 0)
    *angle += 360 * 16;
   while (*angle > 360 * 16)
     *angle -= 360 * 16;
}


GLuint GLWidget::setObjectToZero()
{
  object = 0;
  updateGL();
  return object;
}


GLuint GLWidget::setObjectToMesh(int **connect, double *x_pts, double *y_pts, double *z_pts, int ne, int typ)
{
  int npe = 0;
  switch(typ)
  {
    case(1):
      npe = 2;
      break;
    case(2):
      npe = 3;
      break;
    case(3):
      npe = 4;
      break;
    case(4):
      npe = 4;
      break;
    case(8):
      npe = 2;
      break;
    case(9):
      npe = 3;
      break;
    case(10):
      npe = 4;
      break;
    case(11):
      npe = 4;
      break;
  }

  GLuint list = glGenLists(1);
  glNewList(list, GL_COMPILE);
    for(int i=0;i<ne;i++){
      drawWireElement(connect[i],x_pts,y_pts,z_pts,npe);
    }
  glEndList();

  object = list;
  updateGL();

  return list;
}


GLuint GLWidget::setObjectToSoln(int **connect, double *x, double *x_pts, double *y_pts, double *z_pts, int ne, int typ)
{
  int npe = 0;
  switch(typ)
  {
    case(1):
      npe = 2;
      break;
    case(2):
      npe = 3;
      break;
    case(3):
      npe = 4;
      break;
    case(4):
      npe = 4;
      break;
    case(8):
      npe = 2;
      break;
    case(9):
      npe = 3;
      break;
    case(10):
      npe = 4;
      break;
    case(11):
      npe = 4;
      break;
  }

  GLuint list = glGenLists(1);
  glNewList(list, GL_COMPILE);
    for(int i=0;i<ne;i++){
      drawSolidElement(connect[i],x,x_pts,y_pts,z_pts,npe);
    }
  glEndList();

  object = list;
  updateGL();

  return list;
}


//----------------------------------------------------------------------------
// draw wire functions for different elements
//----------------------------------------------------------------------------
void GLWidget::drawWireElement(int *nodes, double *x_pts, double *y_pts, double *z_pts, int npe)
{
  glBegin(GL_LINE_LOOP);
    for(int j=0;j<npe;j++){
      glVertex3f(x_pts[nodes[j]-1],
                 y_pts[nodes[j]-1],
                 z_pts[nodes[j]-1]);
    }
  glEnd();
}


//----------------------------------------------------------------------------
// draw solid functions for different elements
//----------------------------------------------------------------------------
void GLWidget::drawSolidElement(int *nodes, double *x, double *x_pts, double *y_pts, double *z_pts, int npe)
{
  //jet colormap
  double s = x[nodes[0]-1];
  double red = jetColorRed(s);
  double green = jetColorGreen(s);
  double blue = jetColorBlue(s);

  glColor3f(red,green,blue);

  glBegin(GL_LINE_LOOP);
    for(int j=0;j<npe;j++){
      glVertex3f(x_pts[nodes[j]-1],
                 y_pts[nodes[j]-1],
                 z_pts[nodes[j]-1]);
    }
  glEnd();
}


//----------------------------------------------------------------------------
// colormaps
//----------------------------------------------------------------------------
double GLWidget::jetColorRed(double s)
{
  //set red
  double xmin = 0, xmax = 0, red = 0;
  if((s-xmin)/(xmax-xmin)<0.4){red = 0.0;}
  else if((s-xmin)/(xmax-xmin)<0.6){red = 5.0*(s-xmin)/(xmax-xmin) - 2.0;}
  else if((s-xmin)/(xmax-xmin)<0.8){red = 1.0;}
  else{red = -5/2*(s-xmin)/(xmax-xmin)+7/2;}

  return red;
}


double GLWidget::jetColorGreen(double s)
{
  //green
  double xmin = 0, xmax = 0, green = 0;
  if((s-xmin)/(xmax-xmin)<0.1){green = 0.0;}
  else if((s-xmin)/(xmax-xmin)<0.4){green = 10/3*(s-xmin)/(xmax-xmin)-1/3;}
  else if((s-xmin)/(xmax-xmin)<0.6){green = 1.0;}
  else if((s-xmin)/(xmax-xmin)<0.8){green = -5.0*(s-xmin)/(xmax-xmin)+4.0;}
  else{green = 0.0;}

  return green;
}


double GLWidget::jetColorBlue(double s)
{
  //blue
  double xmin = 0, xmax = 0, blue = 0;
  if((s-xmin)/(xmax-xmin)<0.1){blue = 5*(s-xmin)/(xmax-xmin)+0.5;}
  else if((s-xmin)/(xmax-xmin)<0.4){blue = 1.0;}
  else if((s-xmin)/(xmax-xmin)<0.8){blue = -5*(s-xmin)/(xmax-xmin)+3.0;}
  else{blue = 0.0;}
  return blue;
}
