/**************************************************************************
 *
 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 **************************************************************************/

#include "util/u_math.h"
#include "util/u_memory.h"

#include "draw/draw_context.h"
#include "draw/draw_private.h"
#include "draw/draw_pt.h"

#define FETCH_MAX 256
#define DRAW_MAX (FETCH_MAX+8)

struct varray_frontend {
   struct draw_pt_front_end base;
   struct draw_context *draw;

   ushort draw_elts[DRAW_MAX];
   unsigned fetch_elts[FETCH_MAX];

   unsigned driver_fetch_max;
   unsigned fetch_max;

   struct draw_pt_middle_end *middle;

   unsigned input_prim;
   unsigned output_prim;
};


static void varray_flush_linear(struct varray_frontend *varray,
                                unsigned start, unsigned count)
{
   if (count) {
      assert(varray->middle->run_linear);
      varray->middle->run_linear(varray->middle, start, count);
   }
}

static void varray_line_loop_segment(struct varray_frontend *varray,
                                     unsigned start,
                                     unsigned segment_start,
                                     unsigned segment_count,
                                     boolean end )
{
   assert(segment_count+1 < varray->fetch_max);
   if (segment_count >= 1) {
      unsigned nr = 0, i;

      for (i = 0; i < segment_count; i++) 
         varray->fetch_elts[nr++] = start + segment_start + i;

      if (end) 
         varray->fetch_elts[nr++] = start;

      assert(nr < FETCH_MAX);

      varray->middle->run(varray->middle, 
                          varray->fetch_elts,
                          nr,
                          varray->draw_elts, /* ie. linear */
                          nr);
   }
}



static void varray_fan_segment(struct varray_frontend *varray,
                               unsigned start, 
                               unsigned segment_start,
                               unsigned segment_count )
{
   assert(segment_count+1 < varray->fetch_max);
   if (segment_count >= 2) {
      unsigned nr = 0, i;

      if (segment_start != 0)
         varray->fetch_elts[nr++] = start;

      for (i = 0 ; i < segment_count; i++) 
         varray->fetch_elts[nr++] = start + segment_start + i;

      assert(nr < FETCH_MAX);

      varray->middle->run(varray->middle, 
                          varray->fetch_elts,
                          nr,
                          varray->draw_elts, /* ie. linear */
                          nr);
   }
}




#define FUNC varray_run
#include "draw_pt_varray_tmp_linear.h"

static unsigned decompose_prim[PIPE_PRIM_POLYGON + 1] = {
   PIPE_PRIM_POINTS,
   PIPE_PRIM_LINES,
   PIPE_PRIM_LINE_STRIP,        /* decomposed LINELOOP */
   PIPE_PRIM_LINE_STRIP,
   PIPE_PRIM_TRIANGLES,
   PIPE_PRIM_TRIANGLE_STRIP,
   PIPE_PRIM_TRIANGLE_FAN, 
   PIPE_PRIM_QUADS,
   PIPE_PRIM_QUAD_STRIP,
   PIPE_PRIM_POLYGON
};



static void varray_prepare(struct draw_pt_front_end *frontend,
                           unsigned prim,
                           struct draw_pt_middle_end *middle,
                           unsigned opt)
{
   struct varray_frontend *varray = (struct varray_frontend *)frontend;

   varray->base.run = varray_run;

   varray->input_prim = prim;
   varray->output_prim = decompose_prim[prim];

   varray->middle = middle;
   middle->prepare(middle, varray->output_prim, opt, &varray->driver_fetch_max );

   /* check that the max is even */
   assert((varray->driver_fetch_max & 1) == 0);

   varray->fetch_max = MIN2(FETCH_MAX, varray->driver_fetch_max);
}




static void varray_finish(struct draw_pt_front_end *frontend)
{
   struct varray_frontend *varray = (struct varray_frontend *)frontend;
   varray->middle->finish(varray->middle);
   varray->middle = NULL;
}

static void varray_destroy(struct draw_pt_front_end *frontend)
{
   FREE(frontend);
}


struct draw_pt_front_end *draw_pt_varray(struct draw_context *draw)
{
   ushort i;
   struct varray_frontend *varray = CALLOC_STRUCT(varray_frontend);
   if (varray == NULL)
      return NULL;

   varray->base.prepare = varray_prepare;
   varray->base.run     = NULL;
   varray->base.finish  = varray_finish;
   varray->base.destroy = varray_destroy;
   varray->draw = draw;

   for (i = 0; i < DRAW_MAX; i++) {
      varray->draw_elts[i] = i;
   }

   return &varray->base;
}