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

#include "pipe/p_state.h"
#include "pipe/p_defines.h"
#include "util/u_inlines.h"

#include "brw_resource.h"
#include "brw_context.h"
#include "brw_batchbuffer.h"
#include "brw_winsys.h"

static boolean
brw_buffer_get_handle(struct pipe_screen *screen,
		      struct pipe_resource *resource,
		      struct winsys_handle *handle)
{
   return FALSE;
}


static void
brw_buffer_destroy(struct pipe_screen *screen,
		    struct pipe_resource *resource)
{
   struct brw_buffer *buf = brw_buffer( resource );

   bo_reference(&buf->bo, NULL);
   FREE(buf);
}


static void *
brw_buffer_transfer_map( struct pipe_context *pipe,
			 struct pipe_transfer *transfer)
{
   struct brw_screen *bscreen = brw_screen(pipe->screen); 
   struct brw_winsys_screen *sws = bscreen->sws;
   struct brw_buffer *buf = brw_buffer(transfer->resource);
   unsigned offset = transfer->box.x;
   unsigned length = transfer->box.width;
   unsigned usage = transfer->usage;
   uint8_t *map;

   if (buf->user_buffer)
      map = buf->user_buffer;
   else
      map = sws->bo_map( buf->bo, 
			 BRW_DATA_OTHER,
			 offset,
			 length,
			 (usage & PIPE_TRANSFER_WRITE) ? TRUE : FALSE,
			 (usage & PIPE_TRANSFER_DISCARD) ? TRUE : FALSE,
			 (usage & PIPE_TRANSFER_FLUSH_EXPLICIT) ? TRUE : FALSE);

   return map + offset;
}


static void
brw_buffer_transfer_flush_region( struct pipe_context *pipe,
				  struct pipe_transfer *transfer,
				  const struct pipe_box *box)
{
   struct brw_screen *bscreen = brw_screen(pipe->screen); 
   struct brw_winsys_screen *sws = bscreen->sws;
   struct brw_buffer *buf = brw_buffer(transfer->resource);
   unsigned offset = box->x;
   unsigned length = box->width;

   if (buf->user_buffer)
      return;

   sws->bo_flush_range( buf->bo, 
                        offset,
                        length );
}


static void
brw_buffer_transfer_unmap( struct pipe_context *pipe,
			   struct pipe_transfer *transfer)
{
   struct brw_screen *bscreen = brw_screen(pipe->screen); 
   struct brw_winsys_screen *sws = bscreen->sws;
   struct brw_buffer *buf = brw_buffer( transfer->resource );
   
   if (buf->bo)
      sws->bo_unmap(buf->bo);
}


static unsigned brw_buffer_is_referenced( struct pipe_context *pipe,
					 struct pipe_resource *resource,
					 unsigned face,
					 unsigned level)
{
   struct brw_context *brw = brw_context(pipe);
   struct brw_winsys_buffer *batch_bo = brw->batch->buf;
   struct brw_buffer *buf = brw_buffer(resource);

   if (buf->bo == NULL)
      return PIPE_UNREFERENCED;

   if (!brw_screen(pipe->screen)->sws->bo_references( batch_bo, buf->bo ))
      return PIPE_UNREFERENCED;

   return PIPE_REFERENCED_FOR_READ | PIPE_REFERENCED_FOR_WRITE;
}


struct u_resource_vtbl brw_buffer_vtbl = 
{
   brw_buffer_get_handle,	     /* get_handle */
   brw_buffer_destroy,		     /* resource_destroy */
   brw_buffer_is_referenced,	     /* is_resource_referenced */
   u_default_get_transfer,	     /* get_transfer */
   u_default_transfer_destroy,	     /* transfer_destroy */
   brw_buffer_transfer_map,	     /* transfer_map */
   brw_buffer_transfer_flush_region,  /* transfer_flush_region */
   brw_buffer_transfer_unmap,	     /* transfer_unmap */
   u_default_transfer_inline_write   /* transfer_inline_write */
};


struct pipe_resource *
brw_buffer_create(struct pipe_screen *screen,
		  const struct pipe_resource *template)
{
   struct brw_screen *bscreen = brw_screen(screen);
   struct brw_winsys_screen *sws = bscreen->sws;
   struct brw_buffer *buf;
   unsigned buffer_type;
   enum pipe_error ret;
   
   buf = CALLOC_STRUCT(brw_buffer);
   if (!buf)
      return NULL;
      
   buf->b.b = *template;
   buf->b.vtbl = &brw_buffer_vtbl;
   pipe_reference_init(&buf->b.b.reference, 1);
   buf->b.b.screen = screen;

   switch (template->bind & (PIPE_BIND_VERTEX_BUFFER |
			      PIPE_BIND_INDEX_BUFFER |
			      PIPE_BIND_CONSTANT_BUFFER))
   {
   case PIPE_BIND_VERTEX_BUFFER:
   case PIPE_BIND_INDEX_BUFFER:
   case (PIPE_BIND_VERTEX_BUFFER|PIPE_BIND_INDEX_BUFFER):
      buffer_type = BRW_BUFFER_TYPE_VERTEX;
      break;
      
   case PIPE_BIND_CONSTANT_BUFFER:
      buffer_type = BRW_BUFFER_TYPE_SHADER_CONSTANTS;
      break;

   default:
      buffer_type = BRW_BUFFER_TYPE_GENERIC;
      break;
   }
   
   ret = sws->bo_alloc( sws, buffer_type,
                        template->width0,
			64,	/* alignment */
                        &buf->bo );
   if (ret != PIPE_OK)
      return NULL;
      
   return &buf->b.b; 
}


struct pipe_resource *
brw_user_buffer_create(struct pipe_screen *screen,
                       void *ptr,
                       unsigned bytes,
		       unsigned bind)
{
   struct brw_buffer *buf;
   
   buf = CALLOC_STRUCT(brw_buffer);
   if (!buf)
      return NULL;
   
   pipe_reference_init(&buf->b.b.reference, 1);
   buf->b.vtbl = &brw_buffer_vtbl;
   buf->b.b.screen = screen;
   buf->b.b.format = PIPE_FORMAT_R8_UNORM; /* ?? */
   buf->b.b.usage = PIPE_USAGE_IMMUTABLE;
   buf->b.b.bind = bind;
   buf->b.b.width0 = bytes;
   buf->b.b.height0 = 1;
   buf->b.b.depth0 = 1;

   buf->user_buffer = ptr;
   
   return &buf->b.b; 
}