#include "nebulus.h"

#define COUNT 50
#define DEPTH 60

GLfloat tunnel_time, tunnel_speed = 1.8f, old_tunnel_speed = 1.8f;

typedef struct
{
  GLfloat radius[COUNT];
} ring_type;

ring_type ring[DEPTH];
GLfloat itime = 0.0f, total_time = 0.0f;
GLfloat tex_rad = 1.0f, tex_len = 1.0f, len_fact = 0.50f, kot;
int start_ring = 0;
int tunnel_first = TRUE;


void
precalculate_tunnel(void)
{
  int i, j, k, uring;
  GLfloat ckot0;
  GLfloat x, y, r;
  GLfloat rx, ry;
  glcoord normal = { 0.0f, 0.0f, 0.0f };

  tunnel_time = 0.6f;
  for (k = 0; k < DEPTH; k++) {
    itime += tunnel_time * tunnel_speed;
    total_time += tunnel_time * tunnel_speed * 0.2f;
    while (itime > 1.0f) {
      itime -= 1.0f;
      for (i = 0; i < COUNT; i++) {
        kot = (GLfloat)i / COUNT * PI2;
        ring[start_ring].radius[i] = 100 + 20 * sinf(kot*3 + total_time)
                                         + 20 * sinf(kot*2 + total_time*2 + 50)
                                         + 15 * sinf(kot*4 - total_time*3 + 60);
        ring[start_ring].radius[i] *= 0.01f;
      }
      start_ring = (start_ring + 1) % DEPTH;
    }
    uring = start_ring;
    for (i = 0; i < DEPTH; i++) {
      for (j = 0; j < COUNT + 1; j++) {
        kot = (GLfloat)j / COUNT * PI2;
        ckot0 = ((GLfloat)i - itime) * 0.1f;
        rx = 0.3f * cosf(ckot0 + total_time * 3);
        ry = 0.3f * sinf(ckot0 + total_time * 4);
        r = ring[uring].radius[j % COUNT];
        x = rx + r * cosf(kot);
        y = ry + r * sinf(kot);
        normal.x = -x;
        normal.y = -y;
        normal.z = 1.0f - r;
        normal.x = normal.x * sqrt(normal.x * normal.x + normal.y*normal.y + 
                                   normal.z * normal.z);
        normal.y = normal.y * sqrt(normal.x * normal.x + normal.y*normal.y + 
                                   normal.z * normal.z);
        normal.z = normal.z * sqrt(normal.x * normal.x + normal.y*normal.y + 
                                   normal.z * normal.z);
        ckot0 += 0.1f;
        rx = 0.3f * cosf(ckot0 + total_time * 3);
        ry = 0.3f * sinf(ckot0 + total_time * 4);
        r = ring[(uring + 1) % DEPTH].radius[j % COUNT];
        x = rx + r * cosf(kot);
        y = ry + r * sinf(kot);
        normal.x = -x;
        normal.y = -y;
        normal.z = 1.0f - r;
        normal.x = normal.x * sqrt(normal.x * normal.x + normal.y * normal.y + 
                                   normal.z * normal.z);
        normal.y = normal.y * sqrt(normal.x * normal.x + normal.y * normal.y + 
                                   normal.z * normal.z);
        normal.z = normal.z * sqrt(normal.x * normal.x + normal.y * normal.y + 
                                   normal.z * normal.z);
      }
      uring = (uring + 1) % DEPTH;
    }
  }
  tunnel_first = FALSE;
}


void
updatetunnel(void)
{
  int i;

  tunnel_time = 0.06f;
  itime += tunnel_time * tunnel_speed;
  total_time += tunnel_time * tunnel_speed * 0.2f;
  while (itime > 1.0f) {
    itime -= 1.0f;
    for (i = 0; i < COUNT; i++) {
      kot = (GLfloat)i / COUNT * PI2;
      ring[start_ring].radius[i] = 100 + 20 * sinf(kot*3 + total_time)
                                       + 20 * sinf(kot*2 + total_time*2 + 50)
                                       + 15 * sinf(kot*4 - total_time*3 + 60);
      ring[start_ring].radius[i] *= 0.01f;
    }
    start_ring = (start_ring + 1) % DEPTH;
  }
}


void
drawtunnel(void)
{
  int i, j, uring;
  GLfloat ckot0;
  GLfloat x, y, z, r;
  GLfloat rx, ry;
  GLfloat u, v;
  GLfloat fog_color[3] = { 0.0f, 0.0f, 0.0f };
  glcoord normal = { 0.0f, 0.0f, 0.0f };

  glPushMatrix();
  glTranslatef(0.0f, 0.0f, 0.0f);
  glColor3f(1.0f, 1.0f, 1.0f);
  glFogfv(GL_FOG_COLOR, fog_color);
  glFogf(GL_FOG_DENSITY, 0.04f);
  glFogi(GL_FOG_MODE, GL_EXP2);
  glEnable(GL_FOG);
  uring = start_ring;

  for (i = 0; i < DEPTH; i++) {
    glBegin(GL_QUAD_STRIP);
    for (j = 0; j < COUNT + 1; j++) {
      v = tex_rad * (GLfloat)j / COUNT;
      u = tex_len * (GLfloat)(i + total_time * 5.0f - itime) / DEPTH;
      kot = (GLfloat)j / COUNT * PI2;
      ckot0 = ((GLfloat)i - itime) * 0.1f;
      rx = 0.3f * cosf(ckot0 + total_time * 3);
      ry = 0.3f * sinf(ckot0 + total_time * 4);
      r = ring[uring].radius[j % COUNT];
      x = rx + r * cosf(kot);
      y = ry + r * sinf(kot);
      z = -((GLfloat)i - itime) * len_fact;
      normal.x = -x;
      normal.y = -y;
      normal.z = 1.0f - r;
      normal.x = normal.x * sqrt(normal.x * normal.x + normal.y * normal.y + 
                                 normal.z * normal.z);
      normal.y = normal.y * sqrt(normal.x * normal.x + normal.y * normal.y + 
                                 normal.z * normal.z);
      normal.z = normal.z * sqrt(normal.x * normal.x + normal.y * normal.y + 
                                 normal.z * normal.z);
      glTexCoord2f(u, v);
      glVertex3f(x, y, z);
      ckot0 += 0.1f;
      rx = 0.3f * cosf(ckot0 + total_time * 3);
      ry = 0.3f * sinf(ckot0 + total_time * 4);
      u = tex_len * (GLfloat)(i + 1 + total_time * 5.0f - itime) / DEPTH;
      r = ring[(uring + 1) % DEPTH].radius[j % COUNT];
      x = rx + r * cosf(kot);
      y = ry + r * sinf(kot);
      z = -((GLfloat)i - itime + 1.0f) * len_fact;
      normal.x = -x;
      normal.y = -y;
      normal.z = 1.0f - r;
      normal.x = normal.x * sqrt(normal.x * normal.x + normal.y * normal.y + 
                                 normal.z * normal.z);
      normal.y = normal.y * sqrt(normal.x * normal.x + normal.y * normal.y + 
                                 normal.z * normal.z);
      normal.z = normal.z * sqrt(normal.x * normal.x + normal.y * normal.y + 
                                 normal.z * normal.z);
      glTexCoord2f(u, v);
      glVertex3f(x, y, z);
    }
    glEnd();
    uring = (uring + 1) % DEPTH;
  }
  glDisable(GL_FOG);
  glPopMatrix();
}


void
render_tunnel(void)
{
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glViewport(0, 0, point_general->WIDTH, point_general->HEIGHT);
  glDisable(GL_BLEND);
  glDisable(GL_NORMALIZE);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_TEXTURE_2D);
}


void
draw_tunnel(void)
{
  tunnel_speed = old_tunnel_speed;
  tunnel_speed = reduce_vsync(tunnel_speed);
  render_tunnel();
  updatetunnel();
  drawtunnel();
}
