#include "api3ds.h"
#include "tunel.h"

TUNEL::~TUNEL()
{
delete data;
}

TUNEL::TUNEL()
{
}

TUNEL::TUNEL(int n,float sc)
{
reset(n,sc);
}

void TUNEL::reset(int n,float sc)
{
data=new OBRUCE;

for (int i=0;i<MAXEN1*TRLEN;i++)
  {
  (data->vx[i]).x=0.0;
  (data->vx[i]).y=0.0;
  (data->vx[i]).z=0.0;
  (data->vx[i]).w=1.0;

  (data->vx[i]).R=1.0;
  (data->vx[i]).G=1.0;
  (data->vx[i]).B=1.0;
  (data->vx[i]).A=1.0;

  (data->vx[i]).i=1.0;
  (data->vx[i]).j=0.0;
  (data->vx[i]).k=0.0;

  (data->vx[i]).s=0.0;
  (data->vx[i]).t=0.0;
  (data->vx[i]).r=0.0;
  (data->vx[i]).q=1.0;
  }


float si,co,s,c;
r=g=b=1.0;
int j;

//t.Rotate(0,-PI/2,0);
if (n>MAXEN) n=MAXEN;
for (int i=0;i<TRLEN;i++) 
  {
  data->en[i]=0;
  data->sc[i]=0;
  }
tex_x=0.0;
data->to_use=0;
en=n;
scale=sc;
step=sc;
inverse=0;
tr_pos=0;

data->en[0]=en;
data->sc[0]=scale;
data->polomerx[0]=1.0;
data->polomery[0]=1.0;
data->u  [0]=t.U;
data->v  [0]=t.V;
data->n  [0]=t.N;
data->pos[0]=t.POS;
j=0;

for (int i=0;i<=en;i++)
  { 
  s=sin(i*2*PI/en);
  c=cos(i*2*PI/en);
  si=scale*s;
  co=scale*c;

  (data->vx[j]).x= (t.POS).x + co*(t.N).x + si*(t.V).x;
  (data->vx[j]).y= (t.POS).y + co*(t.N).y + si*(t.V).y;
  (data->vx[j]).z= (t.POS).z + co*(t.N).z + si*(t.V).z;

  if (inverse==0)
    {
    (data->vx[j]).i= -c*(t.N).x - s*(t.V).x;
    (data->vx[j]).j= -c*(t.N).y - s*(t.V).y;
    (data->vx[j]).k= -c*(t.N).z - s*(t.V).z;
    }
   else
    {
    (data->vx[j]).i= c*(t.N).x + s*(t.V).x;
    (data->vx[j]).j= c*(t.N).y + s*(t.V).y;
    (data->vx[j]).k= c*(t.N).z + s*(t.V).z;
    }


  (data->vx[j]).R=0.0;
  (data->vx[j]).G=0.0;
  (data->vx[j]).B=0.0;
  (data->vx[j]).A=1.0;

  (data->vx[j]).s=tex_x;
  (data->vx[j]).t=i/float(en);
  j++;
  }
data->to_use=1;
tex_x+=0.1;
track[tr_pos].pos=t.POS;
track[tr_pos].u=t.U;
track[tr_pos].v=t.V;
track[tr_pos].n=t.N;
tr_pos=1;
}



void TUNEL::posun_t(float how_much)
{
t.Move(step*how_much);
}



void TUNEL::pridaj_obruc()
{
float si,co,s,c;
if (scale<0) scale=0;

int j=data->to_use*MAXEN1;
data->en[data->to_use]=en;
data->sc[data->to_use]=scale;
data->polomerx[data->to_use]=1.0;
data->polomery[data->to_use]=1.0;
data->u  [data->to_use]=t.U;
data->v  [data->to_use]=t.V;
data->n  [data->to_use]=t.N;
data->pos[data->to_use]=t.POS;
for (int i=0;i<=en;i++)
  { 
  s=sin(i*2*PI/en);
  c=cos(i*2*PI/en);

  si=scale*s;
  co=scale*c;

  (data->vx[j]).x= (t.POS).x + co*(t.N).x + si*(t.V).x;
  (data->vx[j]).y= (t.POS).y + co*(t.N).y + si*(t.V).y;
  (data->vx[j]).z= (t.POS).z + co*(t.N).z + si*(t.V).z;

  if (inverse==0)
    {
    (data->vx[j]).i= -c*(t.N).x - s*(t.V).x;
    (data->vx[j]).j= -c*(t.N).y - s*(t.V).y;
    (data->vx[j]).k= -c*(t.N).z - s*(t.V).z;
    }
   else
    {
    (data->vx[j]).i= c*(t.N).x + s*(t.V).x;
    (data->vx[j]).j= c*(t.N).y + s*(t.V).y;
    (data->vx[j]).k= c*(t.N).z + s*(t.V).z;
    }
  (data->vx[j]).R=r;
  (data->vx[j]).G=g;
  (data->vx[j]).B=b;
  (data->vx[j]).A=1.0;
        
  (data->vx[j]).s= tex_x;
  (data->vx[j]).t= i/float(en);
  j++;
  }

int a,b,q,ada,adb;

a=data->to_use;
b=a-1;
if (a==0) b=TRLEN-1;
q=a*MAXEN*4;
ada=a*MAXEN1;
adb=b*MAXEN1;

int ena=data->en[a];
int enb=data->en[b];
int enab=(ena<enb?ena:enb);

switch (inverse)
  {
  case 0:
      if (ena>enb)
      for (int i=0;i<enab;i++)
        {
        data->quads[q]=ada+i;     q++;
        data->quads[q]=adb+i;     q++;
        data->quads[q]=adb+(i+1); q++;
        data->quads[q]=ada+(i+1); q++;
        }
      if (ena<=enb)
      for (int i=0;i<enab;i++)
        {
        data->quads[q]=ada+(i+1); q++;
        data->quads[q]=ada+i;     q++;
        data->quads[q]=adb+i;     q++;
        data->quads[q]=adb+(i+1); q++;
        }

      if (ena>enb)
       {
       data->triangle[a*3  ]=ada+enab;
       data->triangle[a*3+1]=adb+enab;
       data->triangle[a*3+2]=ada+(enab+1);
       }
      if (ena<enb)
       {
       data->triangle[a*3  ]=ada+enab;
       data->triangle[a*3+1]=adb+enab;
       data->triangle[a*3+2]=adb+(enab+1);
       }

    break;
  case 1:
      if (ena>enb)
      for (int i=0;i<enab;i++)
        {
        data->quads[q]=ada+i;     q++;
        data->quads[q]=ada+(i+1); q++;
        data->quads[q]=adb+(i+1); q++;
        data->quads[q]=adb+i;     q++;
        }
      if (ena<=enb)
      for (int i=0;i<enab;i++)
        {
        data->quads[q]=adb+i;     q++;
        data->quads[q]=ada+i;     q++;
        data->quads[q]=ada+(i+1); q++;
        data->quads[q]=adb+(i+1); q++;
        }
      if (ena>enb)
       {
       data->triangle[a*3  ]=ada+enab;
       data->triangle[a*3+1]=ada+(enab+1);
       data->triangle[a*3+2]=adb+enab;
       }
      if (ena<enb)
       {
       data->triangle[a*3  ]=ada+enab;
       data->triangle[a*3+1]=adb+(enab+1);
       data->triangle[a*3+2]=adb+enab;
       }
    break;
  }

data->to_use++;
data->to_use%=TRLEN;
tex_x+=0.1;
track[tr_pos].pos=t.POS;
track[tr_pos].u=t.U;
track[tr_pos].v=t.V;
track[tr_pos].n=t.N;
tr_pos++;
tr_pos%=TRLEN;
}

void TUNEL::konvert_quads() //skonvertuje quads a triangle do q_array a t_array
{
int q=0;
int t=0;
int p,r;
int a,b,ena,enb,enab;

for (int i=0;i<TRLEN;i++) 
  {
  a=i;
  b=a-1;
  if (a==0) b=TRLEN-1;
  int ena=data->en[a];
  int enb=data->en[b];
  int enab=(ena<enb?ena:enb);
  

  if ((ena>0)&(a!=data->to_use)&( (data->sc[a]>0)|(data->sc[b]>0) ))
    {
    p=a*MAXEN*4;
    for (int j=0;j<enab;j++)
      {
      data->q_array[q]=data->quads[p]; q++;p++;
      data->q_array[q]=data->quads[p]; q++;p++;
      data->q_array[q]=data->quads[p]; q++;p++;
      data->q_array[q]=data->quads[p]; q++;p++;
      }
    if (ena!=enb) 
      {
      r=a*3;
      data->t_array[t]=data->triangle[r]; t++;r++;
      data->t_array[t]=data->triangle[r]; t++;r++;
      data->t_array[t]=data->triangle[r]; t++;r++;
      }
    }
  }
data->q_a=q;
data->t_a=t;
}

void TUNEL::rebuild()
{
float si,co;
float g_en,g_scalex,g_scaley;
INFO3D g_t;

for (int g=0;g<TRLEN;g++)
  {
  int j=g*MAXEN1;

  g_en    = data->en[g];
  g_scalex= data->sc[g]*data->polomerx[g];
  g_scaley= data->sc[g]*data->polomery[g];
  g_t.u   = data->u[g];
  g_t.v   = data->v[g];
  g_t.n   = data->n[g];
  g_t.pos = data->pos[g];

  for (int i=0;i<=g_en;i++)
    { 
    si=g_scalex*sin(i*2*PI/g_en);
    co=g_scaley*cos(i*2*PI/g_en);
  
    (data->vx[j]).x= (g_t.pos).x + co*(g_t.n).x + si*(g_t.v).x;
    (data->vx[j]).y= (g_t.pos).y + co*(g_t.n).y + si*(g_t.v).y;
    (data->vx[j]).z= (g_t.pos).z + co*(g_t.n).z + si*(g_t.v).z;         
    j++;
    }
  }
}

void TUNEL::pridaj_obruc(float how_much)
{
posun_t(how_much);
pridaj_obruc();
}


void TUNEL::render()
{
konvert_quads();
//glInterleavedArrays(GL_T2F_N3F_V3F, 0, data->vx);

//glInterleavedArrays(GL_T2F_C4F_N3F_V3F, 0, data->vx);
glInterleavedArrays(GL_T4F_C4F_N3F_V4F, 0, data->vx);



glDrawElements(GL_QUADS, data->q_a, GL_UNSIGNED_INT, data->q_array);
if (data->t_a>0) 
  glDrawElements(GL_TRIANGLES, data->t_a, GL_UNSIGNED_INT, data->t_array);
}

INFO3D TUNEL::getinfo(float p)
{
Vector3f np,nn,nv,nu,prc;
INFO3D ret;

int cc=(int)p;
float dc=p-cc;
cc%=TRLEN;
int nc=(cc+1)%TRLEN;

INFO3D *ic=track+cc;
INFO3D *in=track+nc;

np=ic->pos;
nn=ic->n;
nv=ic->v;
nu=ic->u;

float a=30.0;
Vector3f pos0=ic->pos;
Vector3f pos1=ic->pos+a*(ic->u);
Vector3f pos2=in->pos-a*(in->u);
Vector3f pos3=in->pos;

if (p!=cc)
  {
  Vector3f int1=pos0+dc*(pos1-pos0);
  Vector3f int2=pos2+dc*(pos3-pos2);
  np=int1 + dc*(int2-int1);

//  np+=dc*(in->pos-np);
  nn+=dc*(in->n-nn);
  nu+=dc*(in->u-nu);
  nv+=dc*(in->v-nv);
  }
ret.pos=np;
ret.u=nu;
ret.v=nv;
ret.n=nn;
return ret;
}

