#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#include <math.h>
#include <stdlib.h>
#include <sys/timeb.h>
#include "control.h"
#include "cube.h"
#include "matrix.h"

float rot[16];
float roti[16];

bool reference = false;
int mousex, mousey;
int twisting = 0;

void invert_rot() {
  for (int i = 0, ii = 0; i < 4; i++, ii += 4) {
    for (int j = 0, jj = 0; j < 4; j++, jj += 4) {
      roti[ii + j] = rot[i + jj];
    }
  }
}

void mouseinout(int state) {
  reference = false;
}

void mousebutton(int button, int state, int x, int y) {
  if (state != GLUT_DOWN) return;
  bool ccw = button == GLUT_LEFT_BUTTON;

  if (twisting) twisting = ccw - 2;
  else twistfunc(ccw - 2);
}

void mousemove(int x, int y) {
  int dx = x - mousex;
  int dy = mousey - y;
  mousex = x;
  mousey = y;

  if (!reference) {
    reference = true;
    return;
  }

  if (dx == 0 && dy == 0) return;

  float dh = sqrt(dx * dx + dy * dy);

  invert_rot();
  float axis[4] = {-dy, dx, 0, 1};
  matrixmult(roti, axis);

  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadMatrixf(rot);
  glRotatef(dh, axis[0], axis[1], axis[2]);
  glGetFloatv(GL_MODELVIEW_MATRIX, rot);
  glPopMatrix();

  glutPostRedisplay();
}

void keypress(unsigned char k, int x, int y) {
  if (k == 27 || k == 'q') exit(0);
  if (k == 's' || k == 'S') cube.scramble(), glutPostRedisplay();
  if (k == 'r' || k == 'R') cube.reset(), glutPostRedisplay();
}

void twistfunc(int val) { // -1 ccw, -2 cw, 0 continuation
  static timeb starttime;
  static bool ccw;
  static int face;

  timeb t;
  ftime(&t);

  if (val < 0) {
    twisting = 1;
    cube.twistface = face = cube.front_face();
    ccw = val == -1; // -1 ccw, -2 cw
    starttime = t;
  }

  int diff = (t.time - starttime.time) * 1000;
  diff += t.millitm - starttime.millitm;

  float mtor = 90.0 / 500; // full twist in half second
  float r = mtor * diff;

  if (r >= 90) {
    cube.twist(ccw, face);
    cube.twist_deg = 0;
    if (twisting < 0) twistfunc(twisting);
    else twisting = 0;
  } else {
    cube.twist_deg = r * (ccw * 2 - 1);
    glutTimerFunc(1000.0 / 60, &twistfunc, 0);
  }
  glutPostRedisplay();
}
