/****************************************************************************
** GMAF - The Geometry Modeling Algorithms Framework
**
** Created : 07172000
**
** Copyright (C) 2000 by Le-Jeng Shiue.  All rights reserved.
**
****************************************************************************/
#include <iostream>
#include <fstream>

#include <GL/glut.h>

#include "pccmOGL.h"

#include "DataStructure/gmafDataStructureManager.h"
#include "Algorithm/CCSubdivision/gmafCCSubdivision.h"
#include "Algorithm/PCCM/gmafPCCM.h"

#include "Algorithm/gmafVisualObject.h"

using namespace std;

static gmafVisualObject* pVisualObject;

static Rotation    rot;
static Translation move;
static Scale       scale;
static float cx;
static float cy;
static float cz;

enum ACTION { NONE, ROTATE, MOVE, ZOOM };
static int mouseX;
static int mouseY;
static ACTION action = NONE;

pccmOGL::pccmOGL(int argc, char** argv) {
  glutInit(&argc, argv);

  initPCCM(argc, argv);

  cout << " PCCM is running, please wait..." << endl;

  execPCCM();

  initOGL();
}

pccmOGL::~pccmOGL() {
  delete mpPCCM;
  delete mpDSManager;
}

void pccmOGL::initPCCM(int argc, char** argv) {
  if (argc != 2) {
    cout << "USAGE: pccm mesh-file" << endl;
    exit(1);
  }
  ifstream mesh(argv[1]);
  if (!mesh) {
    cout << "ERROR: cannot open \"" << argv[1] << "\"" << endl;
    exit(1);
  }

  // read in the mesh
  mpDSManager = new gmafDataStructureManager();
  mpDSManager->importMesh(mesh);
  
  // init pccm
  mpPCCM = new gmafPCCM(mpDSManager);
}

void pccmOGL::initOGL() {
  double bboxW = (mpDSManager->maxX() - mpDSManager->minX())*1.5/2;
  double bboxH = (mpDSManager->maxY() - mpDSManager->minY())*1.5/2;
  double bboxD = (mpDSManager->maxZ() - mpDSManager->minZ())*10;

  glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH);
  glutInitWindowSize((int) (400*bboxW/bboxH), 400);
  glutCreateWindow("OpenGL Example");

  glutDisplayFunc(display);
  glutMouseFunc(mouse);
  glutMotionFunc(mouseMove);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-bboxW, bboxW, -bboxH, bboxH, -bboxD, bboxD);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity(); 

  glClearColor (0.4, 0.4, 0.4, 1);

  GLfloat mat_diffuse[] = {0.85, 0.15, 0.1};
  GLfloat mat_specular[] = {0.5, 1, 1};
  GLfloat mat_shininess[] = {80};
  
  GLfloat light0_position[] = {0.0, 0, 1, 0};
  GLfloat light0_diffuse[] = {0.85, 0.15, 0.1, 1};
  GLfloat light0_ambient[] = {0, 0, 0, 1};
  GLfloat light0_specular[] = {0.5, 0.5, 0.5, 1};

  glMaterialfv(GL_FRONT, GL_DIFFUSE  , mat_diffuse);
  glMaterialfv(GL_FRONT, GL_SPECULAR , mat_specular);
  glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

  glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
  glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
  glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular);
  
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_AUTO_NORMAL);
  glEnable(GL_NORMALIZE);
}

void pccmOGL::execPCCM() {
  mpPCCM->run(gmafCCSubdivision::STANDARD);
  //mpPCCM->run(gmafCCSubdivision::BLEND_RATIO_RULE, 2, 0);
  pVisualObject = mpPCCM->visualize();

  cx = mpDSManager->centerX();
  cy = mpDSManager->centerY();
  cz = mpDSManager->centerZ();
}

void pccmOGL::display() {
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glPushMatrix();
  glScalef(scale.x, scale.y, scale.z);
  glTranslatef(move.x, move.y, 0);
  glRotatef(rot.x, 1, 0, 0);
  glRotatef(rot.y, 0, 1, 0);

  glTranslatef(-cx, -cy, -cz);
  
  pVisualObject->display();

  glutSwapBuffers();
  glPopMatrix();
}

void pccmOGL::mainloop() {
    glutMainLoop();
}

void pccmOGL::mouse(int button, int state, int x, int y) {
  if (state == GLUT_DOWN) {
    mouseX = x;
    mouseY = y;
    int m = glutGetModifiers();
    /*
      GLUT_ACTIVE_SHIFT 
      GLUT_ACTIVE_CTRL 
      GLUT_ACTIVE_ALT
    */ 
    if ( m == GLUT_ACTIVE_SHIFT) {
      action = MOVE;
    } else if (m == GLUT_ACTIVE_CTRL) {
      action = ZOOM;
    } else {
      action = ROTATE;
    }
  } else if (state == GLUT_UP) {
    mouseX = 0;
    mouseY = 0;
    action = NONE;
  }
}

void pccmOGL::mouseMove(int x, int y) {
  if (action != NONE) {
    float dx = (float(x - mouseX)) / 10.0;
    float dy = (float(y - mouseY)) / 10.0;
    mouseX = x;
    mouseY = y;
    if (action == MOVE) {
      move.x += (dx/10);
      move.y += (dy/10);
    } else if (action == ROTATE) {
      rot.x += (2*dy);
      rot.y += (2*dx);
    } else if (action == ZOOM) {
      scale.x += -(dy/10);
      scale.y += -(dy/10);
      scale.z += -(dy/10);
    }
    glutPostRedisplay();
  }
}
