#include "BeachBall.h"

#include <igl/quat_to_mat.h>
#include <igl/report_gl_error.h>
#include <igl/trackball.h>
#include <igl/canonical_quaternions.h>
#include <igl/PI.h>

#include <GLUT/GLUT.h>

#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>

double back[3] = {85.0/255.0,85.0/255.0,85.0/255.0};
double width,height;
double scene_rot[4] = {0,0,0,1};
double * BeachBall::scene_rot = NULL;
double down_scene_rot[4] = {0,0,0,1};
int down_mouse_x=-1,down_mouse_y=-1;
bool trackball_on = false;
const float light_pos[4] = {-0.2, -0.1, 1,0};

std::vector<BeachBall> balls;
float plane[4] = {0,0,1,1.5};

int display_count = 0;

void push_scene()
{
  using namespace igl;
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  //gluOrtho2D(0,width,0,height);
  gluPerspective(15,(double)width/(double)height,1e-2,300);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glPushMatrix();
  gluLookAt(0,0,20,0,0,0,0,1,0);
  double mat[4*4];
  quat_to_mat(scene_rot,mat);
  glMultMatrixd(mat);
}

void lights()
{
  using namespace std;
  using namespace igl;

  glEnable(GL_LIGHTING);
  float ones[4] = {1.0,1.0,1.0,1.0};
  glLightfv(GL_LIGHT0,GL_AMBIENT,ones);
  glLightfv(GL_LIGHT0,GL_DIFFUSE,ones);
  glLightfv(GL_LIGHT0,GL_SPECULAR,ones);
  glLightfv(GL_LIGHT0,GL_POSITION,light_pos);
  glEnable(GL_LIGHT0);
}

void pop_scene()
{
  glPopMatrix();
}

void display()
{
  using namespace std;
  using namespace igl;

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  if(true || display_count==0 || display_count==200)
  {
    glClearColor(back[0],back[1],back[2],0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    
    lights();
    push_scene();

    //// Normal list
    //for(int i = 0;i<(int)balls.size();i++)
    //{
    //  balls[i].draw();
    //}

    // single root
    balls[0].draw();
    balls[0].push();
    for(int i = 1;i<(int)balls.size();i++)
    {
      balls[i].radius = 0.5;
      balls[i].draw();
    }
    balls[0].pop();

  // Draw floor
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_POLYGON_OFFSET_FILL);
    glPolygonOffset(10.0,1);
    float size = 10;
    glBegin(GL_QUADS);
    glNormal3f(0,0,1);
    glColor4f(0,0.2,0.8,0.5);
    glVertex4f(-size,-size,-plane[3],1);
    glColor4f(0,0,1,0.5);
    glVertex4f( size,-size,-plane[3],1);
    glColor4f(0.0,0.1,0.5,0.5);
    glVertex4f( size, size,-plane[3],1);
    //glColor4f(back[0],back[1],back[2],1);
    glColor4f(0.0,0.1,0.5,0.5);
    glVertex4f(-size, size,-plane[3],1);
    glEnd();
    glDisable(GL_POLYGON_OFFSET_FILL);
    pop_scene();
  }


  //// Animation
  //for(int i = 0;i<(int)balls.size();i++)
  //{
  //  balls[i].r[0] += cos(double(i*display_count)/1000.0) + sin(double(display_count)/1000.0);
  //  balls[i].r[1] += sin(double(2*i*display_count)/1000.0) + cos(double(2*display_count)/1000.0);
  //  balls[i].r[2] += cos(double(3*i*display_count)/1000.0) + sin(double(3*display_count)/1000.0);
  //  balls[i].r[3] += sin(double(4*i*display_count)/1000.0) + cos(double(4*display_count)/1000.0);
  //  double len = 
  //    sqrt(balls[i].r[0]*balls[i].r[0]+
  //    balls[i].r[1]*balls[i].r[1]+
  //    balls[i].r[2]*balls[i].r[2]+
  //    balls[i].r[3]*balls[i].r[3]);
  //  for(int j = 0;j<4;j++)
  //  {
  //    balls[i].r[j] /= len;
  //  }
  //}

  report_gl_error();
  glutSwapBuffers();
  glutPostRedisplay();

  display_count++;
}

void reshape(int width, int height)
{
  using namespace std;
  ::width = width;
  ::height = height;
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glViewport(0,0,width,height);
}

void key(unsigned char key, int /*mouse_x*/, int /*mouse_y*/)
{
  using namespace std;

  switch(key)
  {
    case char(27):
      exit(0);
    default:
      cout<<"Unknown key command: "<<key<<" "<<int(key)<<endl;
  }
}

void mouse(int glutButton, int glutState, int mouse_x, int mouse_y)
{
  mouse_y = height-mouse_y;
  using namespace std;
  push_scene();
  if(glutState==1)
  {
    for(int i = 0;i<(int)balls.size();i++)
    {
      balls[i].up(mouse_x,mouse_y);
      balls[i].hover(mouse_x,mouse_y);
    }
    trackball_on = false;
  }else if(glutState==0)
  {
    // down
    bool any = false;
    for(int i = 0;!any && i<(int)balls.size();i++)
    {
      if(glutButton==0)
      {
        any |= balls[i].down(mouse_x,mouse_y);
      }else if(glutButton==2)
      {
        any |= balls[i].right_down(mouse_x,mouse_y);
      }
    }
    trackball_on = !any;
    copy(scene_rot,scene_rot+4,down_scene_rot);
    down_mouse_x = mouse_x;
    down_mouse_y = mouse_y;
  }
  pop_scene();
}

void mouse_drag(int mouse_x, int mouse_y)
{
  mouse_y = height-mouse_y;
  using namespace std;
  using namespace igl;
  if(trackball_on)
  {
    trackball<double>(
      width,
      height,
      2,
      down_scene_rot,
      down_mouse_x,
      height-down_mouse_y,
      mouse_x,
      height-mouse_y,
      scene_rot);
  }
  push_scene();
  for(int i = 0;i<(int)balls.size();i++)
  {
    balls[i].drag(mouse_x,mouse_y);
  }
  pop_scene();

}

void mouse_move(int mouse_x, int mouse_y)
{
  mouse_y = height-mouse_y;
  push_scene();
  bool any = false;
  for(int i = 0;!any && i<(int)balls.size();i++)
  {
    any |= balls[i].hover(mouse_x,mouse_y);
  }
  pop_scene();
}


int main(int argc,char * argv[])
{
  using namespace igl;
  using namespace std;
  glutInit(&argc,argv);
  glutInitDisplayString( "rgba depth double samples>=8 ");
  glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH),glutGet(GLUT_SCREEN_HEIGHT)/2);
  glutCreateWindow(__FILE__);
  glutDisplayFunc(display);
  glutReshapeFunc(reshape);
  glutKeyboardFunc(key);
  glutMouseFunc(mouse);
  glutMotionFunc(mouse_drag);
  glutPassiveMotionFunc(mouse_move);

  //copy(XZ_PLANE_QUAT_D,XZ_PLANE_QUAT_D+4,scene_rot);

  balls.push_back(BeachBall());
  const int children = 10;
  for(int i = 1;i<=children;i++)
  {
    balls.push_back(BeachBall());
    balls[i].t[0] = 2.5*sin(double(i)*2.0*PI/double(children));
    balls[i].t[1] = 2.5*cos(double(i)*2.0*PI/double(children));
  }
  BeachBall::scene_rot = scene_rot;

  glutMainLoop();
  return 0;
}
