/**************************************************************************
 * 
 * Copyright 2003 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 "glheader.h"
#include "macros.h"
#include "mtypes.h"
#include "colormac.h"

#include "intel_fbo.h"
#include "intel_screen.h"
#include "intel_span.h"
#include "intel_regions.h"
#include "intel_ioctl.h"
#include "intel_tex.h"

#include "swrast/swrast.h"

/*
  break intelWriteRGBASpan_ARGB8888
*/

#undef DBG
#define DBG 0

#define LOCAL_VARS							\
   struct intel_context *intel = intel_context(ctx);			\
   struct intel_renderbuffer *irb = intel_renderbuffer(rb);		\
   const GLint yScale = irb->RenderToTexture ? 1 : -1;			\
   const GLint yBias = irb->RenderToTexture ? 0 : irb->Base.Height - 1;	\
   GLubyte *buf = (GLubyte *) irb->pfMap				\
      + (intel->drawY * irb->pfPitch + intel->drawX) * irb->region->cpp;\
   GLuint p;								\
   assert(irb->pfMap);\
   (void) p;

/* XXX FBO: this is identical to the macro in spantmp2.h except we get
 * the cliprect info from the context, not the driDrawable.
 * Move this into spantmp2.h someday.
 */
#define HW_CLIPLOOP()							\
   do {									\
      int _nc = intel->numClipRects;					\
      while ( _nc-- ) {							\
	 int minx = intel->pClipRects[_nc].x1 - intel->drawX;		\
	 int miny = intel->pClipRects[_nc].y1 - intel->drawY;		\
	 int maxx = intel->pClipRects[_nc].x2 - intel->drawX;		\
	 int maxy = intel->pClipRects[_nc].y2 - intel->drawY;




#define Y_FLIP(_y) ((_y) * yScale + yBias)

#define HW_LOCK()

#define HW_UNLOCK()

/* 16 bit, RGB565 color spanline and pixel functions
 */
#define SPANTMP_PIXEL_FMT GL_RGB
#define SPANTMP_PIXEL_TYPE GL_UNSIGNED_SHORT_5_6_5

#define TAG(x)    intel##x##_RGB565
#define TAG2(x,y) intel##x##_RGB565##y
#define GET_PTR(X,Y) (buf + ((Y) * irb->pfPitch + (X)) * 2)
#include "spantmp2.h"

/* 32 bit, ARGB8888 color spanline and pixel functions
 */
#define SPANTMP_PIXEL_FMT GL_BGRA
#define SPANTMP_PIXEL_TYPE GL_UNSIGNED_INT_8_8_8_8_REV

#define TAG(x)    intel##x##_ARGB8888
#define TAG2(x,y) intel##x##_ARGB8888##y
#define GET_PTR(X,Y) (buf + ((Y) * irb->pfPitch + (X)) * 4)
#include "spantmp2.h"

#define LOCAL_DEPTH_VARS						\
   struct intel_context *intel = intel_context(ctx);			\
   struct intel_renderbuffer *irb = intel_renderbuffer(rb);		\
   const GLuint pitch = irb->pfPitch/***XXX region->pitch*/; /* in pixels */ \
   const GLint yScale = irb->RenderToTexture ? 1 : -1;			\
   const GLint yBias = irb->RenderToTexture ? 0 : irb->Base.Height - 1;	\
   char *buf = (char *) irb->pfMap/*XXX use region->map*/ +             \
      (intel->drawY * pitch + intel->drawX) * irb->region->cpp;


#define LOCAL_STENCIL_VARS LOCAL_DEPTH_VARS

/**
 ** 16-bit depthbuffer functions.
 **/
#define VALUE_TYPE GLushort

#define WRITE_DEPTH( _x, _y, d ) \
   ((GLushort *)buf)[(_x) + (_y) * pitch] = d;

#define READ_DEPTH( d, _x, _y )	\
   d = ((GLushort *)buf)[(_x) + (_y) * pitch];


#define TAG(x) intel##x##_z16
#include "depthtmp.h"


/**
 ** 24/8-bit interleaved depth/stencil functions
 ** Note: we're actually reading back combined depth+stencil values.
 ** The wrappers in main/depthstencil.c are used to extract the depth
 ** and stencil values.
 **/
#define VALUE_TYPE GLuint

/* Change ZZZS -> SZZZ */
#define WRITE_DEPTH( _x, _y, d ) {				\
   GLuint tmp = ((d) >> 8) | ((d) << 24);			\
   ((GLuint *)buf)[(_x) + (_y) * pitch] = tmp;			\
}

/* Change SZZZ -> ZZZS */
#define READ_DEPTH( d, _x, _y ) {				\
   GLuint tmp = ((GLuint *)buf)[(_x) + (_y) * pitch];		\
   d = (tmp << 8) | (tmp >> 24);				\
}

#define TAG(x) intel##x##_z24_s8
#include "depthtmp.h"


/**
 ** 8-bit stencil function (XXX FBO: This is obsolete)
 **/
#define WRITE_STENCIL( _x, _y, d ) {				\
   GLuint tmp = ((GLuint *)buf)[(_x) + (_y) * pitch];		\
   tmp &= 0xffffff;						\
   tmp |= ((d) << 24);						\
   ((GLuint *) buf)[(_x) + (_y) * pitch] = tmp;			\
}

#define READ_STENCIL( d, _x, _y )				\
   d = ((GLuint *)buf)[(_x) + (_y) * pitch] >> 24;

#define TAG(x) intel##x##_z24_s8
#include "stenciltmp.h"



/**
 * Map or unmap all the renderbuffers which we may need during
 * software rendering.
 * XXX in the future, we could probably convey extra information to
 * reduce the number of mappings needed.  I.e. if doing a glReadPixels
 * from the depth buffer, we really only need one mapping.
 *
 * XXX Rewrite this function someday.
 * We can probably just loop over all the renderbuffer attachments,
 * map/unmap all of them, and not worry about the _ColorDrawBuffers
 * _ColorReadBuffer, _DepthBuffer or _StencilBuffer fields.
 */
static void
intel_map_unmap_buffers(struct intel_context *intel, GLboolean map)
{
   GLcontext *ctx = &intel->ctx;
   GLuint i, j;
   struct intel_renderbuffer *irb;

   /* color draw buffers */
   for (j = 0; j < ctx->DrawBuffer->_NumColorDrawBuffers; j++) {
      struct gl_renderbuffer *rb = ctx->DrawBuffer->_ColorDrawBuffers[j];
      irb = intel_renderbuffer(rb);
      if (irb) {
         /* this is a user-created intel_renderbuffer */
         if (irb->region) {
            if (map)
               intel_region_map(intel, irb->region);
            else
               intel_region_unmap(intel, irb->region);
            irb->pfMap = irb->region->map;
            irb->pfPitch = irb->region->pitch;
         }
      }
   }

   /* check for render to textures */
   for (i = 0; i < BUFFER_COUNT; i++) {
      struct gl_renderbuffer_attachment *att =
         ctx->DrawBuffer->Attachment + i;
      struct gl_texture_object *tex = att->Texture;
      if (tex) {
         /* render to texture */
         ASSERT(att->Renderbuffer);
         if (map) {
            struct gl_texture_image *texImg;
            texImg = tex->Image[att->CubeMapFace][att->TextureLevel];
            intel_tex_map_images(intel, intel_texture_object(tex));
         }
         else {
            intel_tex_unmap_images(intel, intel_texture_object(tex));
         }
      }
   }

   /* color read buffers */
   irb = intel_renderbuffer(ctx->ReadBuffer->_ColorReadBuffer);
   if (irb && irb->region) {
      if (map)
         intel_region_map(intel, irb->region);
      else
         intel_region_unmap(intel, irb->region);
      irb->pfMap = irb->region->map;
      irb->pfPitch = irb->region->pitch;
   }

   /* Account for front/back color page flipping.
    * The span routines use the pfMap and pfPitch fields which will
    * swap the front/back region map/pitch if we're page flipped.
    * Do this after mapping, above, so the map field is valid.
    */
#if 0
   if (map && ctx->DrawBuffer->Name == 0) {
      struct intel_renderbuffer *irbFront
         = intel_get_renderbuffer(ctx->DrawBuffer, BUFFER_FRONT_LEFT);
      struct intel_renderbuffer *irbBack
         = intel_get_renderbuffer(ctx->DrawBuffer, BUFFER_BACK_LEFT);
      if (irbBack) {
         /* double buffered */
         if (intel->sarea->pf_current_page == 0) {
            irbFront->pfMap = irbFront->region->map;
            irbFront->pfPitch = irbFront->region->pitch;
            irbBack->pfMap = irbBack->region->map;
            irbBack->pfPitch = irbBack->region->pitch;
         }
         else {
            irbFront->pfMap = irbBack->region->map;
            irbFront->pfPitch = irbBack->region->pitch;
            irbBack->pfMap = irbFront->region->map;
            irbBack->pfPitch = irbFront->region->pitch;
         }
      }
   }
#endif

   /* depth buffer (Note wrapper!) */
   if (ctx->DrawBuffer->_DepthBuffer) {
      irb = intel_renderbuffer(ctx->DrawBuffer->_DepthBuffer->Wrapped);
      if (irb && irb->region) {
         if (map) {
            intel_region_map(intel, irb->region);
            irb->pfMap = irb->region->map;
            irb->pfPitch = irb->region->pitch;
         }
         else {
            intel_region_unmap(intel, irb->region);
            irb->pfMap = irb->region->map;
            irb->pfPitch = irb->region->pitch;
         }
      }
   }

   /* stencil buffer (Note wrapper!) */
   if (ctx->DrawBuffer->_StencilBuffer) {
      irb = intel_renderbuffer(ctx->DrawBuffer->_StencilBuffer->Wrapped);
      if (irb && irb->region) {
         if (map) {
            intel_region_map(intel, irb->region);
            irb->pfMap = irb->region->map;
            irb->pfPitch = irb->region->pitch;
         }
         else {
            intel_region_unmap(intel, irb->region);
            irb->pfMap = irb->region->map;
            irb->pfPitch = irb->region->pitch;
         }
      }
   }
}



/**
 * Prepare for softare rendering.  Map current read/draw framebuffers'
 * renderbuffes and all currently bound texture objects.
 *
 * Old note: Moved locking out to get reasonable span performance.
 */
void
intelSpanRenderStart(GLcontext * ctx)
{
   struct intel_context *intel = intel_context(ctx);
   GLuint i;

   intelFinish(&intel->ctx);
   LOCK_HARDWARE(intel);

#if 0
   /* Just map the framebuffer and all textures.  Bufmgr code will
    * take care of waiting on the necessary fences:
    */
   intel_region_map(intel, intel->front_region);
   intel_region_map(intel, intel->back_region);
   intel_region_map(intel, intel->depth_region);
#endif

   for (i = 0; i < ctx->Const.MaxTextureCoordUnits; i++) {
      if (ctx->Texture.Unit[i]._ReallyEnabled) {
         struct gl_texture_object *texObj = ctx->Texture.Unit[i]._Current;
         intel_tex_map_images(intel, intel_texture_object(texObj));
      }
   }

   intel_map_unmap_buffers(intel, GL_TRUE);
}

/**
 * Called when done softare rendering.  Unmap the buffers we mapped in
 * the above function.
 */
void
intelSpanRenderFinish(GLcontext * ctx)
{
   struct intel_context *intel = intel_context(ctx);
   GLuint i;

   _swrast_flush(ctx);

   /* Now unmap the framebuffer:
    */
#if 0
   intel_region_unmap(intel, intel->front_region);
   intel_region_unmap(intel, intel->back_region);
   intel_region_unmap(intel, intel->depth_region);
#endif

   for (i = 0; i < ctx->Const.MaxTextureCoordUnits; i++) {
      if (ctx->Texture.Unit[i]._ReallyEnabled) {
         struct gl_texture_object *texObj = ctx->Texture.Unit[i]._Current;
         intel_tex_unmap_images(intel, intel_texture_object(texObj));
      }
   }

   intel_map_unmap_buffers(intel, GL_FALSE);

   UNLOCK_HARDWARE(intel);
}


void
intelInitSpanFuncs(GLcontext * ctx)
{
   struct swrast_device_driver *swdd = _swrast_GetDeviceDriverReference(ctx);
   swdd->SpanRenderStart = intelSpanRenderStart;
   swdd->SpanRenderFinish = intelSpanRenderFinish;
}


/**
 * Plug in appropriate span read/write functions for the given renderbuffer.
 * These are used for the software fallbacks.
 */
void
intel_set_span_functions(struct gl_renderbuffer *rb)
{
   if (rb->_ActualFormat == GL_RGB5) {
      /* 565 RGB */
      intelInitPointers_RGB565(rb);
   }
   else if (rb->_ActualFormat == GL_RGBA8) {
      /* 8888 RGBA */
      intelInitPointers_ARGB8888(rb);
   }
   else if (rb->_ActualFormat == GL_DEPTH_COMPONENT16) {
      intelInitDepthPointers_z16(rb);
   }
   else if (rb->_ActualFormat == GL_DEPTH_COMPONENT24 ||        /* XXX FBO remove */
            rb->_ActualFormat == GL_DEPTH24_STENCIL8_EXT) {
      intelInitDepthPointers_z24_s8(rb);
   }
   else if (rb->_ActualFormat == GL_STENCIL_INDEX8_EXT) {       /* XXX FBO remove */
      intelInitStencilPointers_z24_s8(rb);
   }
   else {
      _mesa_problem(NULL,
                    "Unexpected _ActualFormat in intelSetSpanFunctions");
   }
}
