/**********************************************************************
This file is part of Crack dot Com's free source code release of Golgotha.
for information about compiling & licensing issues visit this URL
 If that doesn't help, contact Jonathan Clark at 
  golgotha_source@usa.net (Subject should have "GOLG" in it) 
***********************************************************************/

#include "r1_clip.hh"
#include "r1_vert.hh"
#include "r1_api.hh"
#include "image/context.hh"

#define FPDIV_CORRECTION 0.0001

int *r1_clip(r1_clip_vert_array *v_array,
             int *src, int t_src,             // indexes into vertex array for initial verts
             int *dst1, int *dst2,            // destination index arrays
             int &t_dst,
             float center_x, float center_y,
             i4_bool clip_code_and_project_done)
{
  w32 ORCODE  = 0;
  w32 ANDCODE = 0xffffffff;
  w32 i,j,c0,c1;
  w32 bitmask;
  i4_float ooz,dx,dy,dz,ds,dt,dr,dg,db,da,t;

  
  t_dst=0;

  r1_vert *vbuf=v_array->buf, *v;
  int *sv=src;

  if (clip_code_and_project_done)
  {
    for (i=0; ioutcode;
      ANDCODE &= v->outcode;
    }
  }
  else
  {
    for (i=0; ioutcode = r1_calc_outcode(v);

      ORCODE  |= v->outcode;
      ANDCODE &= v->outcode;
      
      if (!v->outcode)
      {
        ooz = r1_ooz(v->v.z);
        v->w  = ooz;
        v->px = ((v->v.x * ooz) * center_x) + center_x;
        v->py = ((v->v.y * ooz) * center_y) + center_y;        
      }
    }
  }

  //all verts are outside one of the view planes return a poly with 0 vertices
  if (ANDCODE)
    return 0;
  
  if (!ORCODE)      // all verts are inside, return that no clipping occurred
  {
    t_dst=t_src;
    return src;
  }

  
  int *dst=dst1;  

  for (bitmask=16; bitmask; bitmask=bitmask>>1)
  {
    if (bitmask & ORCODE)
    {
      t_dst=0;

      for (i=0; ioutcode;
        c1 = bitmask & vj->outcode;
      
        //if c0 is not outside of this plane, add it
        if (c0==0) 
          dst[t_dst++]=src[i];
      
        //if they are on the same
        //side, move to the next vert
        if (c0==c1) continue;
      
        //otherwise, generate a clipped point


        dst[t_dst++]=v_array->total;
        cp=v_array->add();

        dx = vj->v.x - vi->v.x;
        dy = vj->v.y - vi->v.y;
        dz = vj->v.z - vi->v.z;
        ds = vj->s - vi->s;
        dt = vj->t - vi->t;
        dr = vj->r - vi->r;
        dg = vj->g - vi->g;
        db = vj->b - vi->b;
        da = vj->a - vi->a;
      
      

        switch (bitmask | (ORCODE&128)) {
          case 1:  
            t = (-vi->v.x + vi->v.z) / ( dx - dz);                  
            cp->v.y = vi->v.y + (t * dy);
            cp->v.z = vi->v.z + (t * dz);                 
            cp->v.x = cp->v.z;
            break;

          case 2: 
            t = ( vi->v.x + vi->v.z) / (-dx - dz);                  
            cp->v.y = vi->v.y + (t * dy);
            cp->v.z = vi->v.z + (t * dz);
                 
            cp->v.x = -cp->v.z;
            break;
        
          case 4:  
            t = (-vi->v.y + vi->v.z) / ( dy - dz);
            cp->v.x = vi->v.x + (t * dx);                 
            cp->v.z = vi->v.z + (t * dz);
                 
            cp->v.y = cp->v.z;
            break;
        
          case 8: 
            t = ( vi->v.y + vi->v.z) / (-dy - dz);
            cp->v.x = vi->v.x + (t * dx);                 
            cp->v.z = vi->v.z + (t * dz);
                 
            cp->v.y = -cp->v.z; 
            break;

          case 16: 
            t = (r1_near_clip_z - vi->v.z) / (dz);		                
            cp->v.x = vi->v.x + (t * dx);
            cp->v.y = vi->v.y + (t * dy);
                 
            cp->v.z = r1_near_clip_z;
            break;

          case 1|128:
            t = (1.0 - vi->v.x) / dx;
            cp->v.y = vi->v.y + (t * dy);
            cp->v.z = vi->v.z + (t * dz);
            cp->v.x = 1.0;
            break;
            
          case 2|128:
            t = (vi->v.x + 1.0) / -dx;
            cp->v.y = vi->v.y + (t * dy);
            cp->v.z = vi->v.z + (t * dz);
                   
            cp->v.x = -1.0;
            break;
        
          case 4|128:
            t = (1.0 - vi->v.y) / dy;
            cp->v.x = vi->v.x + (t * dx);                 
            cp->v.z = vi->v.z + (t * dz);
                   
            cp->v.y = 1.0;
            break;
        
          case 8|128:
            t = (vi->v.y + 1.0) / -dy;
            cp->v.x = vi->v.x + (t * dx);                 
            cp->v.z = vi->v.z + (t * dz);
                  
            cp->v.y = -1.0; 
            break;
        }
      
        cp->s = vi->s + (t * ds);
        cp->t = vi->t + (t * dt);
        cp->r = vi->r + (t * dr);
        cp->g = vi->g + (t * dg);
        cp->b = vi->b + (t * db);
        cp->a = vi->a + (t * da);
      
        // no far clip
        cp->outcode = r1_calc_outcode(cp);

        if (!cp->outcode)
        {
          ooz = r1_ooz(cp->v.z);
          cp->px = ((cp->v.x * ooz) * center_x) + center_x;
          cp->py = ((cp->v.y * ooz) * center_y) + center_y;          
          cp->w  = ooz;
        }  
        ORCODE |= cp->outcode;      
      }
    
      if (dst==dst1)
      {
        dst=dst2;
        src=dst1;
      }
      else
      {
        dst=dst1;
        src=dst2;
      }
      
      t_src=t_dst;
    }
  }
  return src;
}


void r1_clip_render_lines(int t_lines, r1_vert *verts, 
                          float center_x, float center_y,
                          r1_render_api_class *api)
{
  r1_vert v[2];

  w8 bitmask;

  i4_float dz,dy,dx,dr,dg,db,t;

  sw32 i;

  for (i=0; irender_lines(1,v);
      continue;
    }

    //gotta clip
    for (bitmask=32; bitmask && (v[0].outcode & v[1].outcode)==0; bitmask = bitmask >> 1)
    {      
      if (bitmask & v[0].outcode)
      {     
        r1_vert clip_point;

        dx = v[1].v.x - v[0].v.x;
        dy = v[1].v.y - v[0].v.y;
        dz = v[1].v.z - v[0].v.z;
        dr = v[1].r - v[0].r;
        dg = v[1].g - v[0].g;
        db = v[1].b - v[0].b;
  
        switch (bitmask | (v[0].outcode&128)) 
        {
          case 1:
            t = (-v[0].v.x + v[0].v.z) / ( dx - dz);                  
            clip_point.v.y = v[0].v.y + (t * dy);
            clip_point.v.z = v[0].v.z + (t * dz);
                   
            clip_point.v.x = clip_point.v.z;
            break;

          case 2:
            t = ( v[0].v.x + v[0].v.z) / (-dx - dz);                  
            clip_point.v.y = v[0].v.y + (t * dy);
            clip_point.v.z = v[0].v.z + (t * dz);
                   
            clip_point.v.x = -clip_point.v.z;
            break;
        
          case 4:
            t = (-v[0].v.y + v[0].v.z) / ( dy - dz);
            clip_point.v.x = v[0].v.x + (t * dx);                 
            clip_point.v.z = v[0].v.z + (t * dz);
                   
            clip_point.v.y = clip_point.v.z;
            break;
        
          case 8:
            t = ( v[0].v.y + v[0].v.z) / (-dy - dz);
            clip_point.v.x = v[0].v.x + (t * dx);                 
            clip_point.v.z = v[0].v.z + (t * dz);
                  
            clip_point.v.y = -clip_point.v.z; 
            break;

          case 16: 
            t = (r1_near_clip_z - v[0].v.z) / (dz);		                
            clip_point.v.x = v[0].v.x + (t * dx);
            clip_point.v.y = v[0].v.y + (t * dy);
                 
            clip_point.v.z = r1_near_clip_z;
            break;


          case 32: 
            t = -(r1_far_clip_z - v[0].v.z) / (dz);		                
            clip_point.v.x = v[0].v.x + (t * dx);
            clip_point.v.y = v[0].v.y + (t * dy);
                 
            clip_point.v.z = r1_far_clip_z;
            break;

          case 1|128:
            t = (1.0 - v[0].v.x) / dx;
            clip_point.v.y = v[0].v.y + (t * dy);
            clip_point.v.z = v[0].v.z + (t * dz);
            clip_point.v.x = 1.0;
            break;
            
          case 2|128:
            t = (1.0 + v[0].v.x) / -dx;
            clip_point.v.y = v[0].v.y + (t * dy);
            clip_point.v.z = v[0].v.z + (t * dz);
            clip_point.v.x = -1.0;
            break;
        
          case 4|128:
            t = (1.0 - v[0].v.y) / dy;
            clip_point.v.x = v[0].v.x + (t * dx);                 
            clip_point.v.z = v[0].v.z + (t * dz);
                   
            clip_point.v.y = 1.0;
            break;
        
          case 8|128:
            t = ( 1.0 + v[0].v.y) / -dy;
            clip_point.v.x = v[0].v.x + (t * dx);                 
            clip_point.v.z = v[0].v.z + (t * dz);
            clip_point.v.y = -1.0; 
            break;
        }
        clip_point.r = v[0].r + (t * dr);
        clip_point.g = v[0].g + (t * dg);
        clip_point.b = v[0].b + (t * db);        
                
        clip_point.outcode = r1_calc_outcode(&clip_point);

        if (!clip_point.outcode)
        {
          i4_float ooz  = r1_ooz(clip_point.v.z);

          clip_point.px = ((clip_point.v.x * ooz) * center_x) + center_x;
          clip_point.py = ((clip_point.v.y * ooz) * center_y) + center_y;
          clip_point.w  = ooz;
        }
        
        v[0] = clip_point;
      }
      
      if (bitmask & v[1].outcode)
      {     
        r1_vert clip_point;

        clip_point = v[0];
        v[0]       = v[1];
        v[1]       = clip_point;

        dx = v[1].v.x - v[0].v.x;
        dy = v[1].v.y - v[0].v.y;
        dz = v[1].v.z - v[0].v.z;
        dr = v[1].r - v[0].r;
        dg = v[1].g - v[0].g;
        db = v[1].b - v[0].b;
  
        switch (bitmask | (v[1].outcode&128)) 
        {
          case 1: 
            t = (-v[0].v.x + v[0].v.z) / ( dx - dz);                  
            clip_point.v.y = v[0].v.y + (t * dy);
            clip_point.v.z = v[0].v.z + (t * dz);
            
            clip_point.v.x = clip_point.v.z;
            break;

          case 2: 
            t = ( v[0].v.x + v[0].v.z) / (-dx - dz);                  
            clip_point.v.y = v[0].v.y + (t * dy);
            clip_point.v.z = v[0].v.z + (t * dz);
            
            clip_point.v.x = -clip_point.v.z;
            break;
            
          case 4: 
            t = (-v[0].v.y + v[0].v.z) / ( dy - dz);
            clip_point.v.x = v[0].v.x + (t * dx);                 
            clip_point.v.z = v[0].v.z + (t * dz);
            
            clip_point.v.y = clip_point.v.z;
            break;
            
          case 8:
            t = ( v[0].v.y + v[0].v.z) / (-dy - dz);
            clip_point.v.x = v[0].v.x + (t * dx);                 
            clip_point.v.z = v[0].v.z + (t * dz);
            
            clip_point.v.y = -clip_point.v.z; 
            break;
            
          case 16:
            t = (r1_near_clip_z - v[0].v.z) / (dz);		                
            clip_point.v.x = v[0].v.x + (t * dx);
            clip_point.v.y = v[0].v.y + (t * dy);
            
            clip_point.v.z = r1_near_clip_z;
            break;

          case 32:
            t = -(r1_far_clip_z - v[0].v.z) / (dz);		                
            clip_point.v.x = v[0].v.x + (t * dx);
            clip_point.v.y = v[0].v.y + (t * dy);
            
            clip_point.v.z = r1_far_clip_z;
            break;

          case 1|128:
            t = (1.0 - v[0].v.x) / dx;
            clip_point.v.y = v[0].v.y + (t * dy);
            clip_point.v.z = v[0].v.z + (t * dz);
            clip_point.v.x = 1.0;
            break;
            
          case 2|128:
            t = (1.0 + v[0].v.x) / -dx;
            clip_point.v.y = v[0].v.y + (t * dy);
            clip_point.v.z = v[0].v.z + (t * dz);
            clip_point.v.x = -1.0;
            break;
        
          case 4|128:
            t = (1.0 - v[0].v.y) / dy;
            clip_point.v.x = v[0].v.x + (t * dx);                 
            clip_point.v.z = v[0].v.z + (t * dz);
                   
            clip_point.v.y = 1.0;
            break;
        
          case 8|128:
            t = ( 1.0 + v[0].v.y) / -dy;
            clip_point.v.x = v[0].v.x + (t * dx);                 
            clip_point.v.z = v[0].v.z + (t * dz);
            clip_point.v.y = -1.0; 
            break;
        }
        clip_point.r = v[0].r + (t * dr);
        clip_point.g = v[0].g + (t * dg);
        clip_point.b = v[0].b + (t * db);        
                
        clip_point.outcode = r1_calc_outcode(&clip_point);

        if (!clip_point.outcode)
        {
          i4_float ooz = r1_ooz(clip_point.v.z);
          clip_point.px = ((clip_point.v.x * ooz) * center_x) + center_x;
          clip_point.py = ((clip_point.v.y * ooz) * center_y) + center_y;          
          clip_point.w  = ooz;
        }
        
        v[0] = v[1];
        v[1] = clip_point;
      }      
    }

    if (!v[0].outcode && !v[1].outcode)
      api->render_lines(1,v);
  }
}


void r1_clip_clear_area(int x1, int y1, int x2, int y2, w32 color, float z, 
                        i4_draw_context_class &context,
                        r1_render_api_class *api)
{
  api->set_alpha_mode(R1_ALPHA_DISABLED);
  api->set_constant_color(color);
  api->disable_texture();

  for (i4_rect_list_class::area_iter c=context.clip.list.begin();c!=context.clip.list.end();++c)
  {
    int lx1,ly1,lx2,ly2;

    if (x1x1) lx1=c->x1; else lx1=x1;
    if (y1y1) ly1=c->y1; else ly1=y1;
    if (x2>c->x2) lx2=c->x2; else lx2=x2;
    if (y2>c->y2) ly2=c->y2; else ly2=y2;
    if (!(lx1>lx2 || ly1>ly2))
      api->clear_area(lx1, ly1, lx2, ly2, color, z);
  } 
}


void r1_clip_render_textured_rect(float x1, float y1, float x2, float y2, float z,  float a,
                                  int win_width, int win_height,
                                  r1_texture_handle handle,
                                  int frame,
                                  r1_render_api_class *api,
                                  float s1, float t1,
                                  float s2, float t2)
{
  float ooz=r1_ooz(z);

  api->use_texture(handle, i4_f_to_i( x2-x1+1), frame);

  r1_vert v[4];
  
  // should be counter-clockwise
  v[0].px  = x2;
  v[0].py  = y1;
  v[0].v.z = z;
  v[0].w   = ooz;
  v[0].s   = s2;
  v[0].t   = t1;
      
  v[1].px  = x1;
  v[1].py  = y1;
  v[1].v.z = z;
  v[1].w   = ooz;      
  v[1].s   = s1;
  v[1].t   = t1;

  v[2].px  = x1;
  v[2].py  = y2;
  v[2].v.z = z;
  v[2].w   = ooz;      
  v[2].s   = s1;
  v[2].t   = t2;

  v[3].px  = x2;
  v[3].py  = y2;
  v[3].v.z = z;
  v[3].w   = ooz;      
  v[3].s   = s2;
  v[3].t   = t2;

  v[0].r=v[1].r=v[2].r=v[3].r = 1;
  v[0].g=v[1].g=v[2].g=v[3].g = 1;
  v[0].b=v[1].b=v[2].b=v[3].b = 1;

  v[0].a=v[1].a=v[2].a=v[3].a = a;
                                  

  int clip_x1=0, clip_y1=0, clip_x2=win_width, clip_y2=win_height;

  //left x check
  if (v[1].px < clip_x1)
  {
    i4_float diff = (clip_x1 - v[1].px) / (i4_float)(x2-x1+1);
    v[1].s = s1+(s2-s1)*diff;
    v[2].s = s1+(s2-s1)*diff;
    v[1].px = clip_x1;
    v[2].px = clip_x1;
  }
  else if (v[1].px > clip_x2)
    return;

  //right x check
  if (v[0].px > clip_x2)
  {
    i4_float diff = (v[0].px - clip_x2) / (i4_float)(x2-x1+1);
    v[0].s = s2 - diff*(s2-s1);        
    v[3].s = s2 - diff*(s2-s1);
    v[0].px = clip_x2;
    v[3].px = clip_x2;
  }
  else if (v[0].px < clip_x1)
    return;


  //top y check
  if (v[0].py < clip_y1)
  {
    i4_float diff = (clip_y1 - v[0].py) / (i4_float)(y2-y1+1);
    v[0].t  = t1+(t2-t1)*diff;
    v[1].t  = t1+(t2-t1)*diff;
    v[0].py = clip_y1;
    v[1].py = clip_y1;
  }
  else if (v[0].py > clip_y2)
    return;

  //bottom y check
  if (v[2].py > clip_y2)
  {
    i4_float diff = (v[2].py - clip_y2) / (i4_float)(y2-y1+1);
    v[2].t  = t2-diff*(t2-t1);
    v[3].t  = t2-diff*(t2-t1);        
    v[2].py = clip_y2;
    v[3].py = clip_y2;
  }
  else if (v[2].py < clip_y1)
    return;

  api->render_sprite(v);
}