/*

HORSKA DRAHA

return:
  0 - doslo k chybe
  1 - este nezacal
  2 - prebehol v poriadku
  3 - uz skoncil
*/

#include <iostream>
#include "api3ds.h"
#include "efekt.h"
#include "efekt_07.h"
#include "syncs.h"
#include "vlacik2c.h"
#include "vlacik2.h"

train_track track;   // neciste !!! (flash to tak chcel)
extern int sync_id;
extern BOOL usefog;

// raytracingove veci

#define grid_width 16
#define grid_height 12
#define grid_x (grid_width+1)
#define grid_y (grid_width+1)

#define sphere_x 0.0F
#define sphere_y 0.0F
#define sphere_z 0.0F
#define sphere_radius 1000.0F

Texture3DS* skytex;
Texture3DS* skyshadow;

Vertex3DS rtgrid[grid_x*grid_y];
Vertex3DS rtgrid2[grid_x*grid_y];
Vertex3DS rtgrid3[grid_x*grid_y];
GLuint rtindices[(grid_x*2)*grid_height];

void init_rtgrid () {
  Vertex3DS* v = rtgrid;
  for(GLuint i=0; i<=grid_height; i++)
    for(GLuint j=0; j<=grid_width; j++) {
      v->x = 2.0*(GLfloat)j/grid_width - 1.0;
      v->y = 2.0*(GLfloat)i/grid_height - 1.0;
      v->z = 0.0;
      v->w = 1.0;
      v->s = (GLfloat)j/grid_width;
      v->t = (GLfloat)i/grid_height;
      v->r = 0.0; v->q = 1.0;
      v->R = v->G = v->B = v->A = 1.0;
      v->i = v->j = v->k = 0.0;
      v++;
    }
  GLuint* p = rtindices;
  for(GLuint i=0; i<grid_height; i++)
    for(GLuint j=0; j<grid_x; j++) {
      *p = grid_x*i+j; p++;
      *p = grid_x*(i+1)+j; p++;
    }
}

void init_rtgrid2 () {
  Vertex3DS* v = rtgrid2;
  for(GLuint i=0; i<=grid_height; i++)
    for(GLuint j=0; j<=grid_width; j++) {
      v->x = 2.0*(GLfloat)j/grid_width - 1.0;
      v->y = 2.0*(GLfloat)i/grid_height - 1.0;
      v->z = 0.0;
      v->w = 1.0;
      v->s = (GLfloat)j/grid_width;
      v->t = (GLfloat)i/grid_height;
      v->r = 0.0; v->q = 1.0;
      v->R = v->G = v->B = v->A = 1.0;
      v->i = v->j = v->k = 0.0;
      v++;
    }
}

void init_rtgrid3 () {
  Vertex3DS* v = rtgrid3;
  for(GLuint i=0; i<=grid_height; i++)
    for(GLuint j=0; j<=grid_width; j++) {
      v->x = 2.0*(GLfloat)j/grid_width - 1.0;
      v->y = 2.0*(GLfloat)i/grid_height - 1.0;
      v->z = 0.0;
      v->w = 1.0;
      v->s = (GLfloat)j/grid_width;
      v->t = (GLfloat)i/grid_height;
      v->r = 0.0; v->q = 1.0;
      v->R = v->G = v->B = v->A = 1.0;
      v->i = v->j = v->k = 0.0;
      v++;
    }
}

void draw_rtgrid2 () {
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();

  glDisable(GL_LIGHTING);
  glDisable(GL_DEPTH_TEST);
  glDisable(GL_CULL_FACE);
  glEnable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);
  glBlendFunc(GL_ZERO, GL_SRC_COLOR);

  skyshadow->GL();

  glInterleavedArrays(GL_T4F_C4F_N3F_V4F, 0, (void*)rtgrid2);

  for(int i=0; i<grid_height; i++) {
    glDrawElements(GL_QUAD_STRIP, grid_x*2, GL_UNSIGNED_INT,
                        (void*)(rtindices+i*grid_x*2));
  }

  glEnable(GL_LIGHTING);
  glDisable(GL_TEXTURE_2D);
  glDisable(GL_BLEND);
  glBlendFunc(GL_ONE, GL_ONE);

  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
}

void draw_rtgrid3 () {
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();

  glDisable(GL_LIGHTING);
  glDisable(GL_DEPTH_TEST);
  glDisable(GL_CULL_FACE);
  glEnable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);
  glBlendFunc(GL_ZERO, GL_SRC_COLOR);

  skyshadow->GL();

  glInterleavedArrays(GL_T4F_C4F_N3F_V4F, 0, (void*)rtgrid3);

  for(int i=0; i<grid_height; i++) {
    glDrawElements(GL_QUAD_STRIP, grid_x*2, GL_UNSIGNED_INT,
                        (void*)(rtindices+i*grid_x*2));
  }

  glEnable(GL_LIGHTING);
  glDisable(GL_TEXTURE_2D);
  glDisable(GL_BLEND);
  glBlendFunc(GL_ONE, GL_ONE);

  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
}

void draw_rtgrid () {
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();

  glDisable(GL_LIGHTING);
  glDisable(GL_DEPTH_TEST);
  glDisable(GL_BLEND);
  glDisable(GL_CULL_FACE);
  glEnable(GL_TEXTURE_2D);
  skytex->GL();

  glInterleavedArrays(GL_T4F_C4F_N3F_V4F, 0, (void*)rtgrid);

  for(int i=0; i<grid_height; i++) {
    glDrawElements(GL_QUAD_STRIP, grid_x*2, GL_UNSIGNED_INT,
                        (void*)(rtindices+i*grid_x*2));
  }

  glEnable(GL_LIGHTING);
  glDisable(GL_TEXTURE_2D);

  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
}

inline GLfloat Sq (GLfloat x) {
  return x*x;
}


void drawsphere (double mytime) {
  GLfloat thfov2 = (PI/3);
  GLfloat tvfov2 = (PI/4);

//  GLfloat thfov2 = tan(PI/3);
//  GLfloat tvfov2 = tan(PI/4);

  GLfloat d = 10.0;
  GLmatrix m;
  glGetFloatv(GL_MODELVIEW_MATRIX, m());
  m.OrthoInverse();
  Vector3f O(m[12], m[13], m[14]);
  Vector3f U(m[0], m[1], m[2]);
  Vector3f V(m[4], m[5], m[6]);
  Vector3f N(m[8], m[9], m[10]);
  Vector3f K;

  Vector3f S = O - (N + V*tvfov2 + U*thfov2)*d;
  Vector3f deltaU = (U*d*thfov2*2.0F)*(1.0F/(GLfloat)grid_width);
  Vector3f deltaV = (V*d*tvfov2*2.0F)*(1.0F/(GLfloat)grid_height);

  Vertex3DS* v = rtgrid;
  Vertex3DS* v2 = rtgrid2;
  Vertex3DS* v3 = rtgrid3;
  for(int i=0; i<=grid_height; i++) {
    Vector3f A = S+deltaV*(GLfloat)i;
    for(int j=0; j<=grid_width; j++) {
      K = A - O;
      GLfloat a = Sq(K.x) + Sq(K.y) + Sq(K.z);
      GLfloat b = 0;
      GLfloat c =  - Sq(sphere_radius);
      GLfloat t = (-b + sqrt(b*b - 4.0F*a*c))/(2.0F*a);
      GLfloat x = t*K.x;
      GLfloat y = t*K.y;
      GLfloat z = t*K.z;
      v->s = fabs(atan2(x,z)/PI);
      v->t = 0.5*y/(GLfloat)sphere_radius + 0.5;

      v2->s=fabs(sin(atan2(x,z) + mytime*0.002));
      v2->t=0.5*y/(GLfloat)sphere_radius + 0.5 + mytime*0.002;

      v3->s=fabs(sin(atan2(x,z) + mytime*0.00065));
      v3->t=0.5*y/(GLfloat)sphere_radius + 0.5 - mytime*0.002;

      A += deltaU;
      v++;
      v2++;
      v3++;
    }
  }

}


int  efekt_07::init()
{
  cout << "Efekt 7 init ... ";
  start=TRUE;
  counter=ZACIATOK07*refresh;
//->init
  mytime=0.0;
  mytime2=0.0;
  vlak=1;
  animcounter=0.0;
  glClearColor (0.0, 0.0, 0.0, 1.0);
//<-
  cout << "ok!"<<endl;
  return 1;
}

int  efekt_07::load()
{
    cout << "Loading efekt07 ... ";
// -> load
    FILE *stream; 
    stream=fopen(CFGNAME07,"r");
    if (stream==NULL) return 0;
    if (stream)
      {
      fscanf(stream,"vagony            %s\n",vagony);
      fscanf(stream,"raytrace          %s\n",raytrace);

      fscanf(stream,"hfov,vfov (PI/x)  %f,%f\n",&h,&v);
      fscanf(stream,"visibility        %f\n",&far_clipplane);
      fscanf(stream,"minvisibility     %f\n",&near_clipplane);

      fscanf(stream,"%f\n",&cntr1);
      fscanf(stream,"%f %f %f %f\n",&anim1[0],&anim1[4],&anim1[ 8],&anim1[12]);
      fscanf(stream,"%f %f %f %f\n",&anim1[1],&anim1[5],&anim1[ 9],&anim1[13]);
      fscanf(stream,"%f %f %f %f\n",&anim1[2],&anim1[6],&anim1[10],&anim1[14]);
      fscanf(stream,"%f %f %f %f\n\n",&anim1[3],&anim1[7],&anim1[11],&anim1[15]);
      fscanf(stream,"%f\n",&cntr2);
      fscanf(stream,"%f %f %f %f\n",&anim2[0],&anim2[4],&anim2[ 8],&anim2[12]);
      fscanf(stream,"%f %f %f %f\n",&anim2[1],&anim2[5],&anim2[ 9],&anim2[13]);
      fscanf(stream,"%f %f %f %f\n",&anim2[2],&anim2[6],&anim2[10],&anim2[14]);
      fscanf(stream,"%f %f %f %f\n\n",&anim2[3],&anim2[7],&anim2[11],&anim2[15]);
      fscanf(stream,"%f\n",&cntr3);
      fscanf(stream,"%f %f %f %f\n",&anim3[0],&anim3[4],&anim3[ 8],&anim3[12]);
      fscanf(stream,"%f %f %f %f\n",&anim3[1],&anim3[5],&anim3[ 9],&anim3[13]);
      fscanf(stream,"%f %f %f %f\n",&anim3[2],&anim3[6],&anim3[10],&anim3[14]);
      fscanf(stream,"%f %f %f %f\n\n",&anim3[3],&anim3[7],&anim3[11],&anim3[15]);
      fscanf(stream,"%f\n",&cntr4);
      fscanf(stream,"%f %f %f %f\n",&anim4[0],&anim4[4],&anim4[ 8],&anim4[12]);
      fscanf(stream,"%f %f %f %f\n",&anim4[1],&anim4[5],&anim4[ 9],&anim4[13]);
      fscanf(stream,"%f %f %f %f\n",&anim4[2],&anim4[6],&anim4[10],&anim4[14]);
      fscanf(stream,"%f %f %f %f\n",&anim4[3],&anim4[7],&anim4[11],&anim4[15]);
      fclose(stream);
      };
    anim1.OrthoInverse();
    anim2.OrthoInverse();
    anim3.OrthoInverse();
    anim4.OrthoInverse();

  float hf = sin(PI/(2*h)) / cos(PI/(2*h)); // tan(hfov/2)
  float vf = sin(PI/(2*v)) / cos(PI/(2*v)); // tan(vfov/2)
  h=hf;
  v=vf;

  Loader3DS loader;
  wagons = loader.Load(vagony);
  if (wagons==NULL) return 0;
  locomotive = dynamic_cast<Object3DS*>(wagons->GetObject("lokomotiva"));
  wagon = dynamic_cast<Object3DS*>(wagons->GetObject("vozen"));
  if (!locomotive) return 0;
  if (!wagon) return 0;

  OmniLight3DS* l1 = new OmniLight3DS;
  l1->Diffuse(0.7F, 0.7F, 1.0F);
  l1->Position(-1500.0F, 1000.0F, 500.0F);
  scene.Add(l1);

  OmniLight3DS* l2 = new OmniLight3DS;
  l2->Diffuse(1.0F, 0.7F, 0.5F);
  l2->Position(1000.0F, 600.0F, -200.0F);
  scene.Add(l2);

  Material3DS* mrails = new Material3DS;
  mrails->Name("Rail material");
  mrails->Diffuse(0.3F, 0.3, 0.3F);
  track_base::rail_material = mrails;

  Material3DS* mtube = new Material3DS;
  mtube->Name("Tube material");
  mtube->Ambient(0.0F, 0.0F, 0.0F);
  mtube->Diffuse(0.1F, 0.1F, 0.3F);
  mtube->Transparency(50);
  track_base::tube_material = mtube;

  skytex=texture_library->GetOrCreate(raytrace);
  skyshadow=texture_library->GetOrCreate("SKYSHAD.JPG");

  train_track_desc track_description[] = {
    { STRAIGHT,          100,   0, 0 },
    { ASCEND,            100,   0, 0 },
    { DESCEND,           100,   0, 0 },
    { STRAIGHT, 	  80,   0, 0 },
    { LOOPING,           200,   0, 0 },
    { STRAIGHT,           50,   0, 0 },
    { UTURN_LEFT,         90,   0, 0 },
    { UTURN_LEFT,         80,   0, 0 },
    { ASCEND,             70,   0, 0 },
    { UTURN_LEFT,         70,   0, 0 },
    { UTURN_LEFT,         50,   0, 0 },
    { DESCEND,            70,   0, 0 },
    { HALFTURN_LEFT,      80,   0, HAS_TUBE },
    { HALFTURN_LEFT,      80,   0, HAS_TUBE },
    { DESCEND,            70,   0, HAS_TUBE },
    { ASCEND,             70,   0, HAS_TUBE },
    { STRAIGHT,           50,   0, 0 },
    { FULLWHIRL,         460,   0, 0 }, //
    { QUARTERTURN_LEFT,  115,   0, 0 },
    { QUARTERTURN_LEFT,  115,   0, HAS_TUBE },
    { HALFTURN_LEFT,     115,   0, HAS_TUBE },
    { DESCEND,           100,   0, HAS_TUBE },
    { ASCEND,            100,   0, HAS_TUBE }
  };

  scene.Add(*track.Construct(
      sizeof(track_description)/sizeof(track_description[0]),
      track_description));

  turn_matrix.LoadIdentity();
  turn_matrix[0] = -1.0F;
  turn_matrix[10] = -1.0F;

  init_rtgrid();
  init_rtgrid2();
  init_rtgrid3();
// <-
    cout << "ok!"<<endl;
    return 1;
}

int  efekt_07::free()  //vrati 3 ako ok, 0 ak doslo k chybe
{
end=true;
cout << "Efekt 7 free" << endl;
// -> free

// <-
return 3;
}

int  efekt_07::update()
{
id=Sync[GetSyncNum(float(counter)/float(refresh))].id;
// -> update
  if (id==SYNC_VLAK2) vlak=2;
  if (id>=SYNC_VLAK3) vlak=3;
  mytime+=1.3;
  mytime2+=0.7;
  mycounter+=0.03;
// <-
return 1;
}

void efekt_07::DrawTrain(double placement, int numwagons) {
  GLmatrix m;
  m = track.coordinate_system(placement);
  locomotive->CoordinateSystem(m);
  locomotive->Render();
  placement-=loco_len + wagon_spacing - 6.0;
  for(int i=0; i<numwagons; i++) {
    m = track.coordinate_system(placement);
    wagon->CoordinateSystem(m);
    wagon->Render();
    placement-=wago_len + wagon_spacing;
  }
  m = track.coordinate_system(placement)*turn_matrix;
  locomotive->CoordinateSystem(m);
  locomotive->Render();
}

int efekt_07::go(double t)
{
if (t<ZACIATOK07) return 1;
if (end) return 3;
if (counter>=KONIEC07*refresh) return free();

if (!start) if (!init()) return 0;
int cur_frm=(int)(t*refresh);
if (cur_frm>KONIEC07*refresh) cur_frm=int(KONIEC07*refresh);
if (cur_frm>counter)
  while (counter<cur_frm)
    {
    counter++;
    if (counter<KONIEC07*refresh) update();
    }
if (counter>=KONIEC07*refresh) return free();
//tu sa kresli->

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glFrustum(-near_clipplane*h, near_clipplane*h,
            -near_clipplane*v, near_clipplane*v,
             near_clipplane, far_clipplane);
  glMatrixMode(GL_MODELVIEW);

  glEnable(GL_LIGHTING);
  glDisable(GL_FOG);

  if (vlak==1)
    {
    animcounter=(t-VL1)/(VL2-VL1);
    mytime=cntr1+animcounter*(cntr2-cntr1);
    GLmatrix mm=Mslerp((float)animcounter, anim1, anim2);
    mm.OrthoInverse();
    glLoadMatrixf(mm());
    }

  if (vlak==3)
    {
    animcounter=(t-VL3)/(VL4-VL3);
    mytime=cntr3+animcounter*(cntr4-cntr3);
    GLmatrix mm=Mslerp((float)animcounter, anim3, anim4);
    mm.OrthoInverse();
    glLoadMatrixf(mm());
    }

  if (vlak==2)
    {
    GLmatrix m3 = track.coordinate_system(mytime);
  
    quaternion<float> q1 = track.orientation(mytime-100+track.length()*0.4);
    quaternion<float> q2 = track.orientation(mytime+100+track.length()*0.4);
    quaternion<float> q = slerp(0.5F, q1, q2);
    GLmatrix m = Quaternion2Matrix(q);
    m[12] = m3[12];
    m[13] = m3[13];
    m[14] = m3[14];
  
    GLmatrix P;
    P.Identity();
    P.Translate(20.0*cos(mycounter), -20.0+6.0*sin(mycounter), -130.0);
    P.Rotate(10.0, 1.0, 0.0, 0.0);
    P.Rotate(45.0, 0.0, 1.0, 0.0);
    m.OrthoInverse();
    m = P*m;
    glLoadMatrixf(m());
  
    }
    
  drawsphere(mytime2);
  draw_rtgrid();
  draw_rtgrid2();
  DrawTrain(mytime, 2);
  DrawTrain(mytime+track.length()*0.4, 1);
  DrawTrain(mytime+track.length()*0.66, 1);
  draw_rtgrid3();
  scene.Render();
//<-
return 2;
}