//Implementacija sudara - Andelko Spevec - kolegij Računalna grafika
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/glut.h>
  
float bijelo[] = {1.0f, 1.0f, 1.0f, 1.0f};
float crno[] = {0.0f, 0.0f, 0.0f, 1.0f};
float zeleno[] = {0.13f, 0.75f, 0.13f, 1.0f};
float crveno[] = {1, 0.1, 0.2, 1.0};

//okretanje kamere
double lr = 0.0;
double ud = 0.0;

//granice 'stola'
double sizeX = 20.0;
double sizeY = 15.0;
//visina ograde"1368x768_60.00"   85.25  1368 1440 1576 1784  768 771 781 798 -hsync +vsync
double sizeZ = 2.0; 

//radius kugla
double r = 1.0;

//koordinate dvije kugle
double P1[3] = {0.0, 2.0, 0.0};
double P2[3] = {4.0, 0.0, 0.0};

//brzina kretanja kugle
double V1[3] = {0.3, 0.3, 0.0};
double V2[3] = {-0.5, 0.5, 0.0};
double V1temp[3] = {0.0};

//mase dviju kugli
double m1 = 1.0;
double m2 = 1.5;

//ako je true(1) simuliraj udarac 'štapom'
int stick = 0;
double kut = 0.0;

void kretanje(){  
  //povećanje za kuglu 1
  P1[0] += V1[0];
  P1[1] += V1[1];
  //povećanje za kuglu 2
  P2[0] += V2[0];
  P2[1] += V2[1];
  
  //usporavanje - simulate friction - http://cecilsunkure.blogspot.com/2012/02/basic-2d-vector-physics.html
  /*
   *Ako se brzina neke kugle po određenoj komponenti nađe unutar -0.08 i 0.8
   *brzinu odmah smanjujemo na nulu, kako bi 'zaustavili' kuglu. Jer u ovoj
   *implementaciji imam friction gdje smanjujem brzinu u svakom koraku na 99%
   *prethodne - tako se približava nuli ali ne dolazi do nule. Zato sam uzeo
   *ovaj raspon - on može biti i manji kako bi bilo realističnije, ali za ovu 
   *implementaciju smatram da je dovoljno. NAPOMENA: zbog 'naglog' postavljanja
   *brzine na nulu, primjerice za os Y, možemo primjetiti da se kugla nastavlja
   *kretati po osi X dok po osi Y odjednom prestane.
   */
  //x komponenta prve kugle
  if(V1[0] < 0.08 && V1[0] > -0.08){
    V1[0] = 0.0;
  }else{  
    V1[0] *= 0.99;
  }
  //y komponenta prve kugle
  if(V1[1] < 0.08 && V1[1] > -0.08){
    V1[1] = 0.0;
  }else{  
    V1[1] *= 0.99;
  }
  //x komponenta druge kugle
  if(V2[0] < 0.08 && V2[0] > -0.08){
    V2[0] = 0.0;
  }else{  
    V2[0] *= 0.99;
  }
  //y komponenta druge kugle
  if(V2[1] < 0.08 && V2[1] > -0.08){
    V2[1] = 0.0;
  }else{  
    V2[1] *= 0.99;
  }
  //provjera da li obje kugle miruju
  if( (V1[0] == 0.0) && (V1[1] == 0.0) && (V2[0] == 0.0) && (V2[1] == 0.0) ){
    stick = 1;
  }
}//kretanje

void provjeraSudara(){
  //provjera za zidove-------------------------------------
  if( (P1[0] + r) >= (sizeX / 2)){
    //printf("KUGLA 1 - Sudar u zid na pozitivnoj X\n");
    V1[0] *= -1;
  }
  if( (P1[0] - r) <= (-sizeX / 2)){
    //printf("KUGLA 1 - Sudar u zid na negativnoj X\n");
    V1[0] *= -1;
  }
  if( (P1[1] + r) >= (sizeY / 2)){
    //printf("KUGLA 1 - Sudar u zid na pozitivnoj Y\n");
    V1[1] *= -1;
  }
  if( (P1[1] - r) <= (-sizeY / 2)){
    //printf("KUGLA 1 - Sudar u zid na negativnoj Y\n");
    V1[1] *= -1;
  } 
  //druga kugla
  if( (P2[0] + r) >= (sizeX / 2)){
    //printf("KUGLA 2 - Sudar u zid na pozitivnoj X\n");
    V2[0] *= -1;
  }
  if( (P2[0] - r) <= (-sizeX / 2)){
    //printf("KUGLA 2 - Sudar u zid na negativnoj X\n");
    V2[0] *= -1;
  }
  if( (P2[1] + r) >= (sizeY / 2)){
    //printf("KUGLA 2 - Sudar u zid na pozitivnoj Y\n");
    V2[1] *= -1;
  }
  if( (P2[1] - r) <= (-sizeY / 2)){
    //printf("KUGLA 2 - Sudar u zid na negativnoj Y\n");
    V2[1] *= -1;
  } 
  
  //provjera za kuglu-------------------------------------
  /* 
  if( (sqrt(pow( (P2[0] - P1[0]), 2) + pow( (P2[1] - P1[1]), 2))) < (r + r)){
    printf("Sudar između dvije kugle po osi\n");
  }
  *ovo je SPORIJA funkcija jer koristi SQRT
  *Ako imamo dva broja X i Y, te X > Y tada mora biti i X na kvadrat > Y na kvadrat
  *U ovom slučaju udaljenost kugli je kao X na kvadrat, a zbroj radijusa je Y, te sada
  *umjesto da vadimo korijen iz X na kvadrat, brže je samo kvadrirati zbroj radijusa Y.
  *Na kraju uspoređujemo X na kvadrat < Y na kvadrat umjesto X < Y.
  */
  
  if((pow( (P2[0] - P1[0]), 2) + pow( (P2[1] - P1[1]), 2)) < pow(2*r, 2)){
    printf("\n\nSudar između dvije kugle\n");	
    //računanje novih vektora brzine - velocity
    //V1 = (((m1 - m2) * V1) / (m1 + m2)) + ((2 * m2 * V2)/ (m1 + m2))
    
    printf("---------------------------------------------------------\n");
    printf("Stara brzina KUGLE 1 = (%f, %f, %f)\n", V1[0], V1[1], V1[2]);
    printf("Stara brzina KUGLE 2 = (%f, %f, %f)\n", V2[0], V2[1], V2[2]);
    
    //računanje novih brzina kugli
    V1temp[0] = (((m1 - m2) * V1[0]) / (m1 + m2)) + ((2 * m2 * V2[0]) / (m1 + m2));
    V1temp[1] = (((m1 - m2) * V1[1]) / (m1 + m2)) + ((2 * m2 * V2[1]) / (m1 + m2));
    V1temp[2] = (((m1 - m2) * V1[2]) / (m1 + m2)) + ((2 * m2 * V2[2]) / (m1 + m2));
    
    V2[0] = (((m2 - m1) * V2[0]) / (m1 + m2)) + ((2 * m1 * V1[0]) / (m1 + m2));
    V2[1] = (((m2 - m1) * V2[1]) / (m1 + m2)) + ((2 * m1 * V1[1]) / (m1 + m2));
    V2[2] = (((m2 - m1) * V2[2]) / (m1 + m2)) + ((2 * m1 * V1[2]) / (m1 + m2));
    
    //originalni V1 treba koristit za V2, pa moramo koristiti pomocnu varijablu
    V1[0] = V1temp[0];
    V1[1] = V1temp[1];
    V1[2] = V1temp[2];
    
    printf("-----------------------------\n");
    
    printf("Nova brzina KUGLE 1 = (%f, %f, %f)\n", V1[0], V1[1], V1[2]);
    printf("Nova brzina KUGLE 2 = (%f, %f, %f)\n", V2[0], V2[1], V2[2]);
    printf("---------------------------------------------------------\n");
  }
}//provjeraSudara

void kocka(double a) {
  glBegin(GL_QUADS);
    glNormal3d(0.0, 0.0, -1.0);
    glVertex3d(0.0, 0.0, 0.0); glVertex3d(0.0, a, 0.0);
    glVertex3d(a, a, 0.0); glVertex3d(a, 0.0, 0.0);

    glNormal3d(0.0, 0.0, 1.0);
    glVertex3d(0.0, 0.0, a); glVertex3d(a, 0.0, a);
    glVertex3d(a, a, a); glVertex3d(0.0, a, a);
  glEnd();

  glBegin(GL_QUAD_STRIP);
    glNormal3d(0.0, -1.0, 0.0);
    glVertex3d(0.0, 0.0, a); glVertex3d(0.0, 0.0, 0.0);
    glVertex3d(a, 0.0, a); glVertex3d(a, 0.0, 0.0);

    glNormal3d(1.0, 0.0, 0.0);    
    glVertex3d(a, a, a); glVertex3d(a, a, 0.0);

    glNormal3d(0.0, 1.0, 0.0);
    glVertex3d(0.0, a, a); glVertex3d(0.0, a, 0.0);

    glNormal3d(-1.0, 0.0, 0.0);
    glVertex3d(0.0, 0.0, a); glVertex3d(0.0, 0.0, 0.0);
  glEnd();
} // kocka

void kugla(double R, int nphi, int ntheta) {
  double t, r, z;
  double theta, step_theta = M_PI / ntheta;
  double r1, r2, z1, z2;
  int i;

  // bliža kapica
  theta = M_PI / 2.0 - step_theta;
  r = R * cos(theta);
  z = R * sin(theta);
  glBegin(GL_TRIANGLE_FAN);
    glNormal3d(0.0, 0.0, 1.0);
    glVertex3d(0.0, 0.0, R);
    t = 0.0;
    for(i = 0; i <= nphi; i++) {
      glNormal3d(r * cos(t) / R, r * sin(t) / R, z / R);      
      glVertex3d(r * cos(t), r * sin(t), z);
      t += 2.0 * M_PI / nphi;
    }
  glEnd();

  // plašt
  for(theta = M_PI / 2.0 - 2.0 * step_theta; theta > -M_PI / 2.0;
      theta -= step_theta) {
    r1 = R * cos(theta); r2 = R * cos(theta + step_theta);
    z1 = R * sin(theta); z2 = R * sin(theta + step_theta);  
 
    glBegin(GL_QUAD_STRIP);
      t = 0.0; 
      for(i = 0; i <= nphi; i++) {
        glNormal3d(r2 * cos(t) / R, r2 * sin(t) / R, z2 / R);
        glVertex3d(r2 * cos(t), r2 * sin(t), z2);
        glNormal3d(r1 * cos(t) / R, r1 * sin(t) / R, z1 / R);
        glVertex3d(r1 * cos(t), r1 * sin(t), z1);
        t += 2.0 * M_PI / nphi;
      }
    glEnd();
  }

  // dalja kapica
  theta = -M_PI / 2.0 + step_theta;
  r = R * cos(theta);
  z = R * sin(theta);
  glBegin(GL_TRIANGLE_FAN);
    glNormal3d(0.0, 0.0, -1.0);
    glVertex3d(0.0, 0.0, -R);
    t = 0.0;
    for(i = 0; i <= nphi; i++) {
      glNormal3d(r * cos(t) / R, r * sin(t) / R, z / R);
      glVertex3d(r * cos(t), r * sin(t), z);
      t -= 2.0 * M_PI / nphi;
    }
  glEnd();
} // kugla

void podloga(double x, double y, double z){
  glBegin(GL_QUADS);
    glNormal3d(0.0, 0.0, 1.0);
    glVertex3d(-x/2, -y/2, 0); //treći kvadrant
    glVertex3d(x/2, -y/2, 0); //četvrti kvadrant
    glVertex3d(x/2, y/2, 0); //prvi kvadrant
    glVertex3d(-x/2, y/2, 0); //drugi kvadrant
  glEnd();
  
  glBegin(GL_QUADS);
    glNormal3d(0.0, 1.0, 0.0);
    glVertex3d(-x/2, -y/2, 0);
    glVertex3d(x/2, -y/2, 0);
    glVertex3d(x/2, -y/2, z);
    glVertex3d(-x/2, -y/2, z);
  glEnd();
  
  glBegin(GL_QUADS);
    glNormal3d(-1.0, 0.0, 0.0);
    glVertex3d(x/2, -y/2, 0);
    glVertex3d(x/2, y/2, 0);
    glVertex3d(x/2, y/2, z);
    glVertex3d(x/2, -y/2, z);
  glEnd();
  
  glBegin(GL_QUADS);
    glNormal3d(0.0, -1.0, 0.0);
    glVertex3d(-x/2, y/2, 0);
    glVertex3d(x/2, y/2, 0);
    glVertex3d(x/2, y/2, z);
    glVertex3d(-x/2, y/2, z);
  glEnd();
  
  glBegin(GL_QUADS);
    glNormal3d(1.0, 0.0, 0.0);
    glVertex3d(-x/2, -y/2, 0);
    glVertex3d(-x/2, y/2, 0);
    glVertex3d(-x/2, y/2, z);
    glVertex3d(-x/2, -y/2, z);
  glEnd();
}

void svjetlo0() {
  float pozicija[] = {0.0f, 0.0f, 0.0f, 1.0f};
  // četvrti parametar postavljen na 1.0f definira "positional light"
  // na poziciji koju određuju prva tri parametra

  glLightfv(GL_LIGHT0, GL_DIFFUSE, bijelo);
  glLightfv(GL_LIGHT0, GL_SPECULAR, bijelo);
  glLightfv(GL_LIGHT0, GL_POSITION, pozicija);
} // svjetlo0

void iscrtaj(void) {

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  //micanje gore - dolje
  double udaljenost = 25.0;
  double okret = (0.0 + lr) * M_PI / 180;
  double visina = 25.0 + ud;
  
  gluLookAt(udaljenost*cos(okret), udaljenost*sin(okret), visina, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
  
    //svjetlo
  glPushMatrix(); // pamtimo da smo u ishodištu
    // postavljamo svjetlo 0 na odgovarajuću poziciju
    glTranslated(0.0, 0.0, 10.0);
    svjetlo0();
    glLightfv(GL_LIGHT0, GL_AMBIENT, bijelo);
    // položaj svjetla 0 označit ćemo bijelom kockicom
    glMaterialfv(GL_FRONT, GL_EMISSION, bijelo);
    kocka(0.5);
    glMaterialfv(GL_FRONT, GL_EMISSION, crno);
  glPopMatrix(); // vraćamo se u ishodište
  
  //ako kugle stoje pokazi smjer u kojem 'pucamo' bijelu kuglu
  if(stick == 1){
    //printf("lokacija KUGLE 1 = (%f, %f, %f)\n", P1[0], P1[1], P1[2]);
    glPushMatrix();
      glTranslated(P1[0], P1[1], P1[2]);
      glRotated(kut, 0.0, 0.0, 1.0);
      glBegin(GL_LINES);
	glVertex3d(5.0, 0.0, r);
	glVertex3d(0.0, 0.0, r);
      glEnd();
    glPopMatrix();
  }
  
  glPushMatrix();
    glMaterialfv(GL_FRONT, GL_DIFFUSE, zeleno);
    glTranslated(0.0, 0.0, -r);
    podloga(sizeX, sizeY, sizeZ);
  glPopMatrix();
  
  glPushMatrix();
    glMaterialfv(GL_FRONT, GL_DIFFUSE, bijelo);
    glMaterialf(GL_FRONT, GL_SHININESS, 10.0);
    glTranslated(P1[0], P1[1], P1[2]);
    kugla(1.0, 14.0, 14.0);
  glPopMatrix();
  
  glPushMatrix();
    glMaterialfv(GL_FRONT, GL_DIFFUSE, crveno);
    glMaterialf(GL_FRONT, GL_SHININESS, 10.0);
    glTranslated(P2[0], P2[1], P2[2]);
    kugla(1.0, 14.0, 14.0);
  glPopMatrix();
  
  glutSwapBuffers();
} // iscrtaj

void skaliraj(int w, int h) {
  double xmin = -10.0, xmax = 10.0;
  double xrange = xmax - xmin;
  double yrange = h * xrange / w;

  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glFrustum(xmin, xmax, -yrange / 2.0, yrange / 2.0, 20.0, 50.0);
} // skaliraj

void tipka(unsigned char c, int x, int y) {
  if(c == 'q') exit(0);
  if(c == 's') glShadeModel(GL_SMOOTH);
  if(c == 'f') glShadeModel(GL_FLAT);
  if(c == 'D') glEnable(GL_DEPTH_TEST);
  if(c == 'd') glDisable(GL_DEPTH_TEST);
  if(c == 'C') glEnable(GL_CULL_FACE);
  if(c == 'c') glDisable(GL_CULL_FACE);
  if(c == 'B') glCullFace(GL_BACK);
  if(c == 'b') glCullFace(GL_FRONT);
  if(c == 'y') kut -= 1;
  if(c == 'x') kut += 1;
  if(c == 'a'){
    if(stick == 1){
      //pukni loptu u smjeru
      V1[0] = 1 * cos(kut * 3.14 /180);
      V1[1] = 1 * sin(kut * 3.14 /180);
      //printf("X = %f, Y = %f\n", V1[0], V1[1]);
      stick = 0;
    }
  }
} // tipka

void rotirajT(int id){
  kretanje();
  provjeraSudara();
  //todo
  glutPostRedisplay();
  // čekaj 50 milisekundi i ponovo pozovi rotirajT
  glutTimerFunc(50, rotirajT, 0);
} // rotirajT

void strelice(int c, int x, int y) {
  // pomak kamere
  if (c == GLUT_KEY_UP) ud += 0.3; // gore
  if (c == GLUT_KEY_DOWN) ud -= 0.3; // dolje
  if (c == GLUT_KEY_LEFT) lr -= 0.3; // lijevo
  if (c == GLUT_KEY_RIGHT) lr += 0.3; //desno
}

int main(int argc, char** argv) {
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB);

  glutInitWindowSize(640, 480);
  glutCreateWindow("Implementacija sudara");

  glShadeModel(GL_SMOOTH);
  glEnable(GL_DEPTH_TEST);
  glClearColor(0.0, 0.0, 0.0, 0.0);
  
  glEnable(GL_LIGHTING); // omogućava osvjetljavanje
  glEnable(GL_LIGHT0); // uključuje svjetlo 0

  glutDisplayFunc(iscrtaj);
  glutReshapeFunc(skaliraj);
  glutKeyboardFunc(tipka);
  //glutIdleFunc(rotiraj);
  glutSpecialFunc(strelice);
  glutTimerFunc(50, rotirajT, 0); // prvi poziv rutine rotirajT

  glutMainLoop();
  return 0;
} // main

