/* Direct3D ExecuteBuffer
 * Copyright (c) 1998 Lionel ULMER
 *
 * This file contains the implementation of Direct3DExecuteBuffer.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"

#include <string.h>

#include "windef.h"
#include "winerror.h"
#include "wine/obj_base.h"
#include "ddraw.h"
#include "d3d.h"
#include "wine/debug.h"

#include "mesa_private.h"

#define D3DDPRIVATE(x) mesa_d3dd_private *odev=((mesa_d3dd_private*)(x)->private)

WINE_DEFAULT_DEBUG_CHANNEL(ddraw);

/* Structure to store the 'semi transformed' vertices */
typedef struct {
  D3DVALUE x;
  D3DVALUE y;
  D3DVALUE z;
  D3DVALUE w;

  D3DVALUE nx;
  D3DVALUE ny;
  D3DVALUE nz;

  D3DVALUE u;
  D3DVALUE v;
} OGL_Vertex;

typedef struct {
  D3DVALUE x;
  D3DVALUE y;
  D3DVALUE z;
  D3DVALUE w;

  D3DCOLOR c;
  D3DCOLOR sc;

  D3DVALUE u;
  D3DVALUE v;
} OGL_LVertex;

static ICOM_VTABLE(IDirect3DExecuteBuffer) executebuffer_vtable;

/*******************************************************************************
 *				ExecuteBuffer static functions
 */
void _dump_d3dstatus(LPD3DSTATUS lpStatus) {

}

void _dump_executedata(LPD3DEXECUTEDATA lpData) {
  DPRINTF("dwSize : %ld\n", lpData->dwSize);
  DPRINTF("Vertex      Offset : %ld  Count  : %ld\n", lpData->dwVertexOffset, lpData->dwVertexCount);
  DPRINTF("Instruction Offset : %ld  Length : %ld\n", lpData->dwInstructionOffset, lpData->dwInstructionLength);
  DPRINTF("HVertex     Offset : %ld\n", lpData->dwHVertexOffset);
  _dump_d3dstatus(&(lpData->dsStatus));
}

#define DO_VERTEX(index) 			\
{										\
  glTexCoord2f(vx[index].u,							\
	       vx[index].v);							\
  glNormal3f(vx[index].nx,							\
	     vx[index].ny,							\
	     vx[index].nz);							\
  glVertex4f(vx[index].x,						     	\
	     vx[index].y,							\
	     vx[index].z,							\
	     vx[index].w);							\
										\
  TRACE("   V: %f %f %f %f (%f %f %f) (%f %f)\n",			\
	vx[index].x, vx[index].y, vx[index].z, vx[index].w,			\
	vx[index].nx, vx[index].ny, vx[index].nz,				\
	vx[index].u, vx[index].v);						\
}

#define DO_LVERTEX(index)							\
{										\
  DWORD col = l_vx[index].c;							\
  										\
  glColor3f(((col >> 16) & 0xFF) / 255.0,					\
	    ((col >>  8) & 0xFF) / 255.0,					\
	    ((col >>  0) & 0xFF) / 255.0);					\
  glTexCoord2f(l_vx[index].u,							\
	       l_vx[index].v);							\
  glVertex4f(l_vx[index].x,							\
	     l_vx[index].y,							\
	     l_vx[index].z,							\
	     l_vx[index].w);							\
  										\
  TRACE("  LV: %f %f %f %f (%02lx %02lx %02lx) (%f %f)\n",		\
	l_vx[index].x, l_vx[index].y, l_vx[index].z, l_vx[index].w,		\
	((col >> 16) & 0xFF), ((col >>  8) & 0xFF), ((col >>  0) & 0xFF),	\
	l_vx[index].u, l_vx[index].v);						\
}

#define DO_TLVERTEX(index)							\
{										\
  D3DTLVERTEX *vx = &(tl_vx[index]);						\
  DWORD col = vx->u5.color;							\
										\
  glColor3f(((col >> 16) & 0xFF) / 255.0,					\
            ((col >>  8) & 0xFF) / 255.0,					\
            ((col >>  0) & 0xFF) / 255.0);					\
  glTexCoord2f(vx->u7.tu, vx->u8.tv);						\
  if (vx->u4.rhw < 0.01)							\
    glVertex3f(vx->u1.sx,							\
               vx->u2.sy,							\
               vx->u3.sz);							\
  else										\
    glVertex4f(vx->u1.sx / vx->u4.rhw,						\
               vx->u2.sy / vx->u4.rhw,						\
               vx->u3.sz / vx->u4.rhw,						\
               1.0 / vx->u4.rhw);						\
  TRACE(" TLV: %f %f %f (%02lx %02lx %02lx) (%f %f) (%f)\n",		\
        vx->u1.sx, vx->u2.sy, vx->u3.sz,						\
        ((col >> 16) & 0xFF), ((col >>  8) & 0xFF), ((col >>  0) & 0xFF),	\
        vx->u7.tu, vx->u8.tv, vx->u4.rhw);						\
}

#define TRIANGLE_LOOP(macro)				\
{							\
  glBegin(GL_TRIANGLES); {				\
    for (i = 0; i < count; i++) {			\
      LPD3DTRIANGLE ci = (LPD3DTRIANGLE) instr;		\
      							\
      TRACE("  v1: %d  v2: %d  v3: %d\n",	\
	    ci->u1.v1, ci->u2.v2, ci->u3.v3);		\
      TRACE("  Flags : ");			\
      if (TRACE_ON(ddraw)) {				\
	/* Wireframe */					\
	if (ci->wFlags & D3DTRIFLAG_EDGEENABLE1)	\
	  DPRINTF("EDGEENABLE1 ");				\
	if (ci->wFlags & D3DTRIFLAG_EDGEENABLE2)	\
	  DPRINTF("EDGEENABLE2 ");				\
	if (ci->wFlags & D3DTRIFLAG_EDGEENABLE1)	\
	  DPRINTF("EDGEENABLE3 ");				\
							\
	/* Strips / Fans */				\
	if (ci->wFlags == D3DTRIFLAG_EVEN)		\
	  DPRINTF("EVEN ");				\
	if (ci->wFlags == D3DTRIFLAG_ODD)		\
	  DPRINTF("ODD ");					\
	if (ci->wFlags == D3DTRIFLAG_START)		\
	  DPRINTF("START ");				\
	if ((ci->wFlags > 0) && (ci->wFlags < 30))	\
	  DPRINTF("STARTFLAT(%d) ", ci->wFlags);		\
	DPRINTF("\n");					\
      }							\
							\
      /* Draw the triangle */				\
      macro(ci->u1.v1);					\
      macro(ci->u2.v2);					\
      macro(ci->u3.v3);					\
							\
      instr += size;					\
    }							\
  } glEnd();						\
}


static void execute(LPDIRECT3DEXECUTEBUFFER lpBuff,
		    LPDIRECT3DDEVICE dev,
		    LPDIRECT3DVIEWPORT vp) {
  IDirect3DExecuteBufferImpl* ilpBuff=(IDirect3DExecuteBufferImpl*)lpBuff;
  IDirect3DViewport2Impl* ivp=(IDirect3DViewport2Impl*)vp;
  /* DWORD bs = ilpBuff->desc.dwBufferSize; */
  DWORD vs = ilpBuff->data.dwVertexOffset;
  /* DWORD vc = ilpBuff->data.dwVertexCount; */
  DWORD is = ilpBuff->data.dwInstructionOffset;
  /* DWORD il = ilpBuff->data.dwInstructionLength; */

  void *instr = ilpBuff->desc.lpData + is;
  D3DDPRIVATE((IDirect3DDeviceImpl*)dev);

  /* Should check if the viewport was added or not to the device */

  /* Activate the viewport */
  ivp->device.active_device1 = (IDirect3DDeviceImpl*)dev;
  ivp->activate(ivp);

  TRACE("ExecuteData : \n");
  if (TRACE_ON(ddraw))
  _dump_executedata(&(ilpBuff->data));

  ENTER_GL();

  while (1) {
    LPD3DINSTRUCTION current = (LPD3DINSTRUCTION) instr;
    BYTE size;
    WORD count;

    count = current->wCount;
    size = current->bSize;
    instr += sizeof(D3DINSTRUCTION);

    switch (current->bOpcode) {
    case D3DOP_POINT: {
      TRACE("POINT-s          (%d)\n", count);

      instr += count * size;
    } break;

    case D3DOP_LINE: {
      TRACE("LINE-s           (%d)\n", count);

      instr += count * size;
    } break;

    case D3DOP_TRIANGLE: {
      int i;

      OGL_Vertex  *vx    = (OGL_Vertex  *) ilpBuff->vertex_data;
      OGL_LVertex *l_vx  = (OGL_LVertex *) ilpBuff->vertex_data;
      D3DTLVERTEX *tl_vx = (D3DTLVERTEX *) ilpBuff->vertex_data;

      TRACE("TRIANGLE         (%d)\n", count);

      switch (ilpBuff->vertex_type) {
      case D3DVT_VERTEX:
	/* This time, there is lighting */
	glEnable(GL_LIGHTING);

      /* Use given matrixes */
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity(); /* The model transformation was done during the
			   transformation phase */
      glMatrixMode(GL_PROJECTION);
	TRACE("  Projection Matrix : (%p)\n", odev->proj_mat);
	dump_mat(odev->proj_mat);
	TRACE("  View       Matrix : (%p)\n", odev->view_mat);
	dump_mat(odev->view_mat);

	/* Although z axis is inverted between OpenGL and Direct3D, the z projected coordinates
	   are always 0.0 at the front viewing volume and 1.0 at the back with Direct 3D and with
	   the default behaviour of OpenGL. So, no additional transformation is required. */
	glLoadMatrixf((float *) odev->proj_mat);
	glMultMatrixf((float *) odev->view_mat);
	break;

      case D3DVT_LVERTEX:
	/* No lighting */
	glDisable(GL_LIGHTING);

	/* Use given matrixes */
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity(); /* The model transformation was done during the
			     transformation phase */
	glMatrixMode(GL_PROJECTION);

	TRACE("  Projection Matrix : (%p)\n", odev->proj_mat);
	dump_mat(odev->proj_mat);
	TRACE("  View       Matrix : (%p)\n", odev->view_mat);
	dump_mat(odev->view_mat);

	/* Although z axis is inverted between OpenGL and Direct3D, the z projected coordinates
	   are always 0 at the front viewing volume and 1 at the back with Direct 3D and with
	   the default behaviour of OpenGL. So, no additional transformation is required. */
	glLoadMatrixf((float *) odev->proj_mat);
	glMultMatrixf((float *) odev->view_mat);
	break;

      case D3DVT_TLVERTEX: {
	GLdouble height, width, minZ, maxZ;

        /* First, disable lighting */
        glDisable(GL_LIGHTING);

        /* Then do not put any transformation matrixes */
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();

        if (ivp == NULL) {
          ERR("No current viewport !\n");
          /* Using standard values */
          height = 640.0;
          width = 480.0;
          minZ = -10.0;
          maxZ = 10.0;
        } else {
          height = (GLdouble) ivp->viewport.vp1.dwHeight;
          width  = (GLdouble) ivp->viewport.vp1.dwWidth;
          minZ   = (GLdouble) ivp->viewport.vp1.dvMinZ;
          maxZ   = (GLdouble) ivp->viewport.vp1.dvMaxZ;

	  if (minZ == maxZ) {
	    /* I do not know why, but many Dx 3.0 games have minZ = maxZ = 0.0 */
	    minZ = 0.0;
	    maxZ = 1.0;
	}
        }

        glOrtho(0.0, width, height, 0.0, -minZ, -maxZ);
      } break;

      default:
	ERR("Unhandled vertex type !\n");
	break;
      }

      switch (ilpBuff->vertex_type) {
      case D3DVT_VERTEX:
	TRIANGLE_LOOP(DO_VERTEX);
	break;

      case D3DVT_LVERTEX:
	TRIANGLE_LOOP(DO_LVERTEX);
	break;

      case D3DVT_TLVERTEX:
	TRIANGLE_LOOP(DO_TLVERTEX);
	break;

      default:
	ERR("Unhandled vertex type !\n");
      }

    } break;

    case D3DOP_MATRIXLOAD: {
      TRACE("MATRIXLOAD-s     (%d)\n", count);

      instr += count * size;
    } break;

    case D3DOP_MATRIXMULTIPLY: {
      int i;
      TRACE("MATRIXMULTIPLY   (%d)\n", count);

      for (i = 0; i < count; i++) {
	LPD3DMATRIXMULTIPLY ci = (LPD3DMATRIXMULTIPLY) instr;
	LPD3DMATRIX a = (LPD3DMATRIX) ci->hDestMatrix;
	LPD3DMATRIX b = (LPD3DMATRIX) ci->hSrcMatrix1;
	LPD3DMATRIX c = (LPD3DMATRIX) ci->hSrcMatrix2;

	TRACE("  Dest : %08lx  Src1 : %08lx  Src2 : %08lx\n",
	      ci->hDestMatrix, ci->hSrcMatrix1, ci->hSrcMatrix2);

	/* Do the multiplication..
	   As I am VERY lazy, I let OpenGL do the multiplication for me */
	glMatrixMode(GL_PROJECTION);
	/* Save the current matrix */
	glPushMatrix();
	/* Load Matrix one and do the multiplication */
	glLoadMatrixf((float *) c);
	glMultMatrixf((float *) b);
	glGetFloatv(GL_PROJECTION_MATRIX, (float *) a);
	/* Restore the current matrix */
	glPopMatrix();

	instr += size;
      }
    } break;

    case D3DOP_STATETRANSFORM: {
      int i;
      TRACE("STATETRANSFORM   (%d)\n", count);

      for (i = 0; i < count; i++) {
	LPD3DSTATE ci = (LPD3DSTATE) instr;

	/* Handle the state transform */
	switch (ci->u1.dtstTransformStateType) {
	case D3DTRANSFORMSTATE_WORLD: {
	  TRACE("  WORLD (%p)\n", (D3DMATRIX*) ci->u2.dwArg[0]);
	  odev->world_mat = (D3DMATRIX*) ci->u2.dwArg[0];
	} break;

	case D3DTRANSFORMSTATE_VIEW: {
	  TRACE("  VIEW (%p)\n", (D3DMATRIX*) ci->u2.dwArg[0]);
	  odev->view_mat = (D3DMATRIX*) ci->u2.dwArg[0];
	} break;

	case D3DTRANSFORMSTATE_PROJECTION: {
    	  TRACE("  PROJECTION (%p)\n", (D3DMATRIX*) ci->u2.dwArg[0]);
	  odev->proj_mat = (D3DMATRIX*) ci->u2.dwArg[0];
	} break;

	default:
	  ERR("  Unhandled state transformation !! (%d)\n", (int) ci->u1.dtstTransformStateType);
	  break;

	}

	instr += size;
      }
    } break;

    case D3DOP_STATELIGHT: {
      int i;
      TRACE("STATELIGHT       (%d)\n", count);

      for (i = 0; i < count; i++) {
	LPD3DSTATE ci = (LPD3DSTATE) instr;

	/* Handle the state transform */
	switch (ci->u1.dlstLightStateType) {
	case D3DLIGHTSTATE_MATERIAL: {
	  IDirect3DMaterial2Impl* mat = (IDirect3DMaterial2Impl*) ci->u2.dwArg[0];
	  TRACE("  MATERIAL\n");

	  if (mat != NULL) {
	    mat->activate(mat);
	  } else {
	    TRACE("    bad Material Handle\n");
	  }
	} break ;

	case D3DLIGHTSTATE_AMBIENT: {
	  float light[4];
	  DWORD dwLightState = ci->u2.dwArg[0];
	  TRACE("  AMBIENT\n");

	  light[0] = ((dwLightState >> 16) & 0xFF) / 255.0;
	  light[1] = ((dwLightState >>  8) & 0xFF) / 255.0;
	  light[2] = ((dwLightState >>  0) & 0xFF) / 255.0;
	  light[3] = 1.0;
	  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (float *) light);

	  TRACE("    R:%02lx G:%02lx B:%02lx A:%02lx\n",
		((dwLightState >> 16) & 0xFF),
		((dwLightState >>  8) & 0xFF),
		((dwLightState >>  0) & 0xFF),
		((dwLightState >> 24) & 0xFF));
	} break ;

	case D3DLIGHTSTATE_COLORMODEL: {
	  TRACE("  COLORMODEL\n");
	} break ;

	case D3DLIGHTSTATE_FOGMODE: {
	  TRACE("  FOGMODE\n");
	} break ;

	case D3DLIGHTSTATE_FOGSTART: {
	  TRACE("  FOGSTART\n");
	} break ;

	case D3DLIGHTSTATE_FOGEND: {
	  TRACE("  FOGEND\n");
	} break ;

	case D3DLIGHTSTATE_FOGDENSITY: {
	  TRACE("  FOGDENSITY\n");
	} break ;

	default:
	  ERR("  Unhandled light state !! (%d)\n", (int) ci->u1.dlstLightStateType);
	  break;
	}
	instr += size;
      }
    } break;

    case D3DOP_STATERENDER: {
      int i;
      TRACE("STATERENDER      (%d)\n", count);

      for (i = 0; i < count; i++) {
	LPD3DSTATE ci = (LPD3DSTATE) instr;

	/* Handle the state transform */
	set_render_state(ci->u1.drstRenderStateType, ci->u2.dwArg[0], &(odev->rs));

	instr += size;
      }
    } break;

    case D3DOP_PROCESSVERTICES: {
      int i;
      TRACE("PROCESSVERTICES  (%d)\n", count);

      for (i = 0; i < count; i++) {
	LPD3DPROCESSVERTICES ci = (LPD3DPROCESSVERTICES) instr;

	TRACE("  Start : %d Dest : %d Count : %ld\n",
	      ci->wStart, ci->wDest, ci->dwCount);
	TRACE("  Flags : ");
	if (TRACE_ON(ddraw)) {
	  if (ci->dwFlags & D3DPROCESSVERTICES_COPY)
	    DPRINTF("COPY ");
	  if (ci->dwFlags & D3DPROCESSVERTICES_NOCOLOR)
	    DPRINTF("NOCOLOR ");
	  if (ci->dwFlags == D3DPROCESSVERTICES_OPMASK)
	    DPRINTF("OPMASK ");
	  if (ci->dwFlags & D3DPROCESSVERTICES_TRANSFORM)
	    DPRINTF("TRANSFORM ");
	  if (ci->dwFlags == D3DPROCESSVERTICES_TRANSFORMLIGHT)
	    DPRINTF("TRANSFORMLIGHT ");
	  if (ci->dwFlags & D3DPROCESSVERTICES_UPDATEEXTENTS)
	    DPRINTF("UPDATEEXTENTS ");
	  DPRINTF("\n");
	}

	/* This is where doing Direct3D on top on OpenGL is quite difficult.
	   This method transforms a set of vertices using the CURRENT state
	   (lighting, projection, ...) but does not rasterize them.
	   They will only be put on screen later (with the POINT / LINE and
	   TRIANGLE op-codes). The problem is that you can have a triangle
	   with each point having been transformed using another state...

	   In this implementation, I will emulate only ONE thing : each
	   vertex can have its own "WORLD" transformation (this is used in the
	   TWIST.EXE demo of the 5.2 SDK). I suppose that all vertices of the
	   execute buffer use the same state.

	   If I find applications that change other states, I will try to do a
	   more 'fine-tuned' state emulation (but I may become quite tricky if
	   it changes a light position in the middle of a triangle).

	   In this case, a 'direct' approach (i.e. without using OpenGL, but
	   writing our own 3D rasterizer) would be easier. */

	/* The current method (with the hypothesis that only the WORLD matrix
	   will change between two points) is like this :
	    - I transform 'manually' all the vertices with the current WORLD
	      matrix and store them in the vertex buffer
	    - during the rasterization phase, the WORLD matrix will be set to
	      the Identity matrix */

	/* Enough for the moment */
	if (ci->dwFlags == D3DPROCESSVERTICES_TRANSFORMLIGHT) {
	  int nb;
	  D3DVERTEX  *src = ((LPD3DVERTEX)  (ilpBuff->desc.lpData + vs)) + ci->wStart;
	  OGL_Vertex *dst = ((OGL_Vertex *) (ilpBuff->vertex_data)) + ci->wDest;
	  D3DMATRIX *mat = odev->world_mat;

	  TRACE("  World Matrix : (%p)\n", mat);
	  dump_mat(mat);

	  ilpBuff->vertex_type = D3DVT_VERTEX;

	  for (nb = 0; nb < ci->dwCount; nb++) {
	    /* For the moment, no normal transformation... */
	    dst->nx = (src->u4.nx * mat->_11) + (src->u5.ny * mat->_21) + (src->u6.nz * mat->_31);
	    dst->ny = (src->u4.nx * mat->_12) + (src->u5.ny * mat->_22) + (src->u6.nz * mat->_32);
	    dst->nz = (src->u4.nx * mat->_13) + (src->u5.ny * mat->_23) + (src->u6.nz * mat->_33);

	    dst->u  = src->u7.tu;
	    dst->v  = src->u8.tv;

	    /* Now, the matrix multiplication */
	    dst->x = (src->u1.x * mat->_11) + (src->u2.y * mat->_21) + (src->u3.z * mat->_31) + (1.0 * mat->_41);
	    dst->y = (src->u1.x * mat->_12) + (src->u2.y * mat->_22) + (src->u3.z * mat->_32) + (1.0 * mat->_42);
	    dst->z = (src->u1.x * mat->_13) + (src->u2.y * mat->_23) + (src->u3.z * mat->_33) + (1.0 * mat->_43);
	    dst->w = (src->u1.x * mat->_14) + (src->u2.y * mat->_24) + (src->u3.z * mat->_34) + (1.0 * mat->_44);

	    src++;
	    dst++;
	  }
	} else if (ci->dwFlags == D3DPROCESSVERTICES_TRANSFORM) {
	  int nb;
	  D3DLVERTEX *src  = ((LPD3DLVERTEX) (ilpBuff->desc.lpData + vs)) + ci->wStart;
	  OGL_LVertex *dst = ((OGL_LVertex *) (ilpBuff->vertex_data)) + ci->wDest;
	  D3DMATRIX *mat = odev->world_mat;

	  TRACE("  World Matrix : (%p)\n", mat);
	  dump_mat(mat);

	  ilpBuff->vertex_type = D3DVT_LVERTEX;

	  for (nb = 0; nb < ci->dwCount; nb++) {
	    dst->c  = src->u4.color;
	    dst->sc = src->u5.specular;
	    dst->u  = src->u6.tu;
	    dst->v  = src->u7.tv;

	    /* Now, the matrix multiplication */
	    dst->x = (src->u1.x * mat->_11) + (src->u2.y * mat->_21) + (src->u3.z * mat->_31) + (1.0 * mat->_41);
	    dst->y = (src->u1.x * mat->_12) + (src->u2.y * mat->_22) + (src->u3.z * mat->_32) + (1.0 * mat->_42);
	    dst->z = (src->u1.x * mat->_13) + (src->u2.y * mat->_23) + (src->u3.z * mat->_33) + (1.0 * mat->_43);
	    dst->w = (src->u1.x * mat->_14) + (src->u2.y * mat->_24) + (src->u3.z * mat->_34) + (1.0 * mat->_44);

	    src++;
	    dst++;
	  }
	} else if (ci->dwFlags == D3DPROCESSVERTICES_COPY) {
	  D3DTLVERTEX *src = ((LPD3DTLVERTEX) (ilpBuff->desc.lpData + vs)) + ci->wStart;
	  D3DTLVERTEX *dst = ((LPD3DTLVERTEX) (ilpBuff->vertex_data)) + ci->wDest;

	  ilpBuff->vertex_type = D3DVT_TLVERTEX;

	  memcpy(dst, src, ci->dwCount * sizeof(D3DTLVERTEX));
	} else {
	  ERR("Unhandled vertex processing !\n");
	}

	instr += size;
      }
    } break;

    case D3DOP_TEXTURELOAD: {
      TRACE("TEXTURELOAD-s    (%d)\n", count);

      instr += count * size;
    } break;

    case D3DOP_EXIT: {
      TRACE("EXIT             (%d)\n", count);
      /* We did this instruction */
      instr += size;
      /* Exit this loop */
      goto end_of_buffer;
    } break;

    case D3DOP_BRANCHFORWARD: {
      int i;
      TRACE("BRANCHFORWARD    (%d)\n", count);

      for (i = 0; i < count; i++) {
	LPD3DBRANCH ci = (LPD3DBRANCH) instr;

	if ((ilpBuff->data.dsStatus.dwStatus & ci->dwMask) == ci->dwValue) {
	  if (!ci->bNegate) {
	    TRACE(" Should branch to %ld\n", ci->dwOffset);
	  }
	} else {
	  if (ci->bNegate) {
	    TRACE(" Should branch to %ld\n", ci->dwOffset);
	  }
	}

	instr += size;
      }
    } break;

    case D3DOP_SPAN: {
      TRACE("SPAN-s           (%d)\n", count);

      instr += count * size;
    } break;

    case D3DOP_SETSTATUS: {
      int i;
      TRACE("SETSTATUS        (%d)\n", count);

      for (i = 0; i < count; i++) {
	LPD3DSTATUS ci = (LPD3DSTATUS) instr;

	ilpBuff->data.dsStatus = *ci;

	instr += size;
      }
    } break;

    default:
      ERR("Unhandled OpCode !!!\n");
      /* Try to save ... */
      instr += count * size;
      break;
    }
  }

 end_of_buffer:
  LEAVE_GL();
}

/*******************************************************************************
 *				ExecuteBuffer Creation functions
 */
LPDIRECT3DEXECUTEBUFFER d3dexecutebuffer_create(IDirect3DDeviceImpl* d3ddev, LPD3DEXECUTEBUFFERDESC lpDesc)
{
  IDirect3DExecuteBufferImpl* eb;

  eb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirect3DExecuteBufferImpl));
  eb->ref = 1;
  ICOM_VTBL(eb) = &executebuffer_vtable;
  eb->d3ddev = d3ddev;

  /* Initializes memory */
  eb->desc = *lpDesc;

  /* No buffer given */
  if (!(eb->desc.dwFlags & D3DDEB_LPDATA))
    eb->desc.lpData = NULL;

  /* No buffer size given */
  if (!(lpDesc->dwFlags & D3DDEB_BUFSIZE))
    eb->desc.dwBufferSize = 0;

  /* Create buffer if asked */
  if ((eb->desc.lpData == NULL) && (eb->desc.dwBufferSize > 0)) {
    eb->need_free = TRUE;
    eb->desc.lpData = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,eb->desc.dwBufferSize);
  } else {
    eb->need_free = FALSE;
  }

  /* No vertices for the moment */
  eb->vertex_data = NULL;

  eb->desc.dwFlags |= D3DDEB_LPDATA;

  eb->execute = execute;

  return (LPDIRECT3DEXECUTEBUFFER)eb;
}

/*******************************************************************************
 *				IDirect3ExecuteBuffer methods
 */

static HRESULT WINAPI IDirect3DExecuteBufferImpl_QueryInterface(LPDIRECT3DEXECUTEBUFFER iface,
							    REFIID riid,
							    LPVOID* ppvObj)
{
  ICOM_THIS(IDirect3DExecuteBufferImpl,iface);

  FIXME("(%p)->(%s,%p): stub\n", This, debugstr_guid(riid),ppvObj);

  return S_OK;
}



static ULONG WINAPI IDirect3DExecuteBufferImpl_AddRef(LPDIRECT3DEXECUTEBUFFER iface)
{
  ICOM_THIS(IDirect3DExecuteBufferImpl,iface);
  TRACE("(%p)->()incrementing from %lu.\n", This, This->ref );

  return ++(This->ref);
}



static ULONG WINAPI IDirect3DExecuteBufferImpl_Release(LPDIRECT3DEXECUTEBUFFER iface)
{
  ICOM_THIS(IDirect3DExecuteBufferImpl,iface);
  FIXME("(%p)->() decrementing from %lu.\n", This, This->ref );

  if (!--(This->ref)) {
    if ((This->desc.lpData != NULL) && This->need_free)
      HeapFree(GetProcessHeap(),0,This->desc.lpData);

    if (This->vertex_data != NULL)
      HeapFree(GetProcessHeap(),0,This->vertex_data);

    HeapFree(GetProcessHeap(),0,This);
    return 0;
  }

  return This->ref;
}

static HRESULT WINAPI IDirect3DExecuteBufferImpl_Initialize(LPDIRECT3DEXECUTEBUFFER iface,
							LPDIRECT3DDEVICE lpDirect3DDevice,
							LPD3DEXECUTEBUFFERDESC lpDesc)
{
  ICOM_THIS(IDirect3DExecuteBufferImpl,iface);
  FIXME("(%p)->(%p,%p): stub\n", This, lpDirect3DDevice, lpDesc);

  return DD_OK;
}

static HRESULT WINAPI IDirect3DExecuteBufferImpl_Lock(LPDIRECT3DEXECUTEBUFFER iface,
						  LPD3DEXECUTEBUFFERDESC lpDesc)
{
  ICOM_THIS(IDirect3DExecuteBufferImpl,iface);
  TRACE("(%p)->(%p)\n", This, lpDesc);

  /* Copies the buffer description */
  *lpDesc = This->desc;

  return DD_OK;
}

static HRESULT WINAPI IDirect3DExecuteBufferImpl_Unlock(LPDIRECT3DEXECUTEBUFFER iface)
{
  ICOM_THIS(IDirect3DExecuteBufferImpl,iface);
  TRACE("(%p)->()\n", This);

  return DD_OK;
}

static HRESULT WINAPI IDirect3DExecuteBufferImpl_SetExecuteData(LPDIRECT3DEXECUTEBUFFER iface,
							    LPD3DEXECUTEDATA lpData)
{
  ICOM_THIS(IDirect3DExecuteBufferImpl,iface);
  DWORD nbvert;

  TRACE("(%p)->(%p)\n", This, lpData);

  This->data = *lpData;

  /* Get the number of vertices in the execute buffer */
  nbvert = This->data.dwVertexCount;

  /* Prepares the transformed vertex buffer */
  if (This->vertex_data != NULL)
    HeapFree(GetProcessHeap(), 0, This->vertex_data);
  This->vertex_data = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,nbvert * sizeof(OGL_Vertex));


  if (TRACE_ON(ddraw)) {
    _dump_executedata(lpData);
  }

  return DD_OK;
}

static HRESULT WINAPI IDirect3DExecuteBufferImpl_GetExecuteData(LPDIRECT3DEXECUTEBUFFER iface,
							    LPD3DEXECUTEDATA lpData)
{
  ICOM_THIS(IDirect3DExecuteBufferImpl,iface);
  TRACE("(%p)->(%p): stub\n", This, lpData);

  *lpData = This->data;

  return DD_OK;
}

static HRESULT WINAPI IDirect3DExecuteBufferImpl_Validate(LPDIRECT3DEXECUTEBUFFER iface,
						      LPDWORD lpdwOffset,
						      LPD3DVALIDATECALLBACK lpFunc,
						      LPVOID lpUserArg,
						      DWORD dwReserved)
{
  ICOM_THIS(IDirect3DExecuteBufferImpl,iface);
  TRACE("(%p)->(%p,%p,%p,%lu)\n", This, lpdwOffset, lpFunc, lpUserArg, dwReserved);

  return DD_OK;
}

static HRESULT WINAPI IDirect3DExecuteBufferImpl_Optimize(LPDIRECT3DEXECUTEBUFFER iface,
						      DWORD dwReserved)
{
  ICOM_THIS(IDirect3DExecuteBufferImpl,iface);
  TRACE("(%p)->(%lu)\n", This, dwReserved);

  return DD_OK;
}


/*******************************************************************************
 *				IDirect3DLight VTable
 */
static ICOM_VTABLE(IDirect3DExecuteBuffer) executebuffer_vtable =
{
  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
  /*** IUnknown methods ***/
  IDirect3DExecuteBufferImpl_QueryInterface,
  IDirect3DExecuteBufferImpl_AddRef,
  IDirect3DExecuteBufferImpl_Release,
  /*** IDirect3DExecuteBuffer methods ***/
  IDirect3DExecuteBufferImpl_Initialize,
  IDirect3DExecuteBufferImpl_Lock,
  IDirect3DExecuteBufferImpl_Unlock,
  IDirect3DExecuteBufferImpl_SetExecuteData,
  IDirect3DExecuteBufferImpl_GetExecuteData,
  IDirect3DExecuteBufferImpl_Validate,
  IDirect3DExecuteBufferImpl_Optimize
};
