/*
 * Copyright © 2010 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Soft-
 * ware"), to deal in the Software without restriction, including without
 * limitation the rights to use, copy, modify, merge, publish, distribute,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, provided that the above copyright
 * notice(s) and this permission notice appear in all copies of the Soft-
 * ware and that both the above copyright notice(s) and this permission
 * notice appear in supporting documentation.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
 * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
 * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
 * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE-
 * QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR-
 * MANCE OF THIS SOFTWARE.
 *
 * Except as contained in this notice, the name of a copyright holder shall
 * not be used in advertising or otherwise to promote the sale, use or
 * other dealings in this Software without prior written authorization of
 * the copyright holder.
 *
 * Authors:
 *   Kristian Høgsberg (krh@bitplanet.net)
 */

#include <stdbool.h>

#include "glapi.h"
#include "glxclient.h"
#include "indirect.h"
#include "util/debug.h"

#ifndef GLX_USE_APPLEGL

extern struct _glapi_table *__glXNewIndirectAPI(void);

/*
** All indirect rendering contexts will share the same indirect dispatch table.
*/
static struct _glapi_table *IndirectAPI = NULL;

static void
__glFreeAttributeState(struct glx_context * gc)
{
   __GLXattribute *sp, **spp;

   for (spp = &gc->attributes.stack[0];
        spp < &gc->attributes.stack[__GL_CLIENT_ATTRIB_STACK_DEPTH]; spp++) {
      sp = *spp;
      if (sp) {
         free((char *) sp);
      }
      else {
         break;
      }
   }
}

static void
indirect_destroy_context(struct glx_context *gc)
{
   __glXFreeVertexArrayState(gc);

   free((char *) gc->vendor);
   free((char *) gc->renderer);
   free((char *) gc->version);
   free((char *) gc->extensions);
   __glFreeAttributeState(gc);
   free((char *) gc->buf);
   free((char *) gc->client_state_private);
   free((char *) gc);
}

static Bool
SendMakeCurrentRequest(Display * dpy, GLXContextID gc_id,
                       GLXContextTag gc_tag, GLXDrawable draw,
                       GLXDrawable read, GLXContextTag *out_tag)
{
   xGLXMakeCurrentReply reply;
   Bool ret;
   int opcode = __glXSetupForCommand(dpy);

   LockDisplay(dpy);

   if (draw == read) {
      xGLXMakeCurrentReq *req;

      GetReq(GLXMakeCurrent, req);
      req->reqType = opcode;
      req->glxCode = X_GLXMakeCurrent;
      req->drawable = draw;
      req->context = gc_id;
      req->oldContextTag = gc_tag;
   }
   else {
      struct glx_display *priv = __glXInitialize(dpy);

      /* If the server can support the GLX 1.3 version, we should
       * perfer that.  Not only that, some servers support GLX 1.3 but
       * not the SGI extension.
       */

      if (priv->minorVersion >= 3) {
         xGLXMakeContextCurrentReq *req;

         GetReq(GLXMakeContextCurrent, req);
         req->reqType = opcode;
         req->glxCode = X_GLXMakeContextCurrent;
         req->drawable = draw;
         req->readdrawable = read;
         req->context = gc_id;
         req->oldContextTag = gc_tag;
      }
      else {
         xGLXVendorPrivateWithReplyReq *vpreq;
         xGLXMakeCurrentReadSGIReq *req;

         GetReqExtra(GLXVendorPrivateWithReply,
                     sz_xGLXMakeCurrentReadSGIReq -
                     sz_xGLXVendorPrivateWithReplyReq, vpreq);
         req = (xGLXMakeCurrentReadSGIReq *) vpreq;
         req->reqType = opcode;
         req->glxCode = X_GLXVendorPrivateWithReply;
         req->vendorCode = X_GLXvop_MakeCurrentReadSGI;
         req->drawable = draw;
         req->readable = read;
         req->context = gc_id;
         req->oldContextTag = gc_tag;
      }
   }

   ret = _XReply(dpy, (xReply *) &reply, 0, False);

   if (out_tag)
      *out_tag = reply.contextTag;

   UnlockDisplay(dpy);
   SyncHandle();

   return ret;
}

static int
indirect_bind_context(struct glx_context *gc, struct glx_context *old,
		      GLXDrawable draw, GLXDrawable read)
{
   GLXContextTag tag;
   Display *dpy = gc->psc->dpy;
   Bool sent;

   if (old != &dummyContext && !old->isDirect && old->psc->dpy == dpy) {
      tag = old->currentContextTag;
      old->currentContextTag = 0;
   } else {
      tag = 0;
   }

   sent = SendMakeCurrentRequest(dpy, gc->xid, tag, draw, read,
				 &gc->currentContextTag);

   if (sent) {
      if (!IndirectAPI)
         IndirectAPI = __glXNewIndirectAPI();
      _glapi_set_dispatch(IndirectAPI);

      /* The indirect vertex array state must to be initialised after we
       * have setup the context, as it needs to query server attributes.
       *
       * At the point this is called gc->currentDpy is not initialized
       * nor is the thread's current context actually set. Hence the
       * cleverness before the GetString calls.
       */
      __GLXattribute *state = gc->client_state_private;
      if (state && state->array_state == NULL) {
         gc->currentDpy = gc->psc->dpy;
         __glXSetCurrentContext(gc);
         __indirect_glGetString(GL_EXTENSIONS);
         __indirect_glGetString(GL_VERSION);
         __glXInitVertexArrayState(gc);
      }
   }

   return !sent;
}

static void
indirect_unbind_context(struct glx_context *gc, struct glx_context *new)
{
   Display *dpy = gc->psc->dpy;

   if (gc == new)
      return;
   
   /* We are either switching to no context, away from an indirect
    * context to a direct context or from one dpy to another and have
    * to send a request to the dpy to unbind the previous context.
    */
   if (!new || new->isDirect || new->psc->dpy != dpy) {
      SendMakeCurrentRequest(dpy, None, gc->currentContextTag, None, None,
                             NULL);
      gc->currentContextTag = 0;
   }
}

static void
indirect_wait_gl(struct glx_context *gc)
{
   xGLXWaitGLReq *req;
   Display *dpy = gc->currentDpy;

   /* Flush any pending commands out */
   __glXFlushRenderBuffer(gc, gc->pc);

   /* Send the glXWaitGL request */
   LockDisplay(dpy);
   GetReq(GLXWaitGL, req);
   req->reqType = gc->majorOpcode;
   req->glxCode = X_GLXWaitGL;
   req->contextTag = gc->currentContextTag;
   UnlockDisplay(dpy);
   SyncHandle();
}

static void
indirect_wait_x(struct glx_context *gc)
{
   xGLXWaitXReq *req;
   Display *dpy = gc->currentDpy;

   /* Flush any pending commands out */
   __glXFlushRenderBuffer(gc, gc->pc);

   LockDisplay(dpy);
   GetReq(GLXWaitX, req);
   req->reqType = gc->majorOpcode;
   req->glxCode = X_GLXWaitX;
   req->contextTag = gc->currentContextTag;
   UnlockDisplay(dpy);
   SyncHandle();
}

static const struct glx_context_vtable indirect_context_vtable = {
   .destroy             = indirect_destroy_context,
   .bind                = indirect_bind_context,
   .unbind              = indirect_unbind_context,
   .wait_gl             = indirect_wait_gl,
   .wait_x              = indirect_wait_x,
};

_X_HIDDEN struct glx_context *
indirect_create_context(struct glx_screen *psc,
			struct glx_config *mode,
			struct glx_context *shareList, int renderType)
{
   unsigned error = 0;
   const uint32_t attribs[] = { GLX_RENDER_TYPE, renderType };

   return indirect_create_context_attribs(psc, mode, shareList,
                                          1, attribs, &error);
}

/**
 * \todo Eliminate \c __glXInitVertexArrayState.  Replace it with a new
 * function called \c __glXAllocateClientState that allocates the memory and
 * does all the initialization (including the pixel pack / unpack).
 */
_X_HIDDEN struct glx_context *
indirect_create_context_attribs(struct glx_screen *psc,
				struct glx_config *mode,
				struct glx_context *shareList,
				unsigned num_attribs,
				const uint32_t *attribs,
				unsigned *error)
{
   struct glx_context *gc;
   int bufSize;
   CARD8 opcode;
   __GLXattribute *state;
   int i, renderType = GLX_RGBA_TYPE;
   uint32_t mask = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
   uint32_t major = 1;
   uint32_t minor = 0;

   opcode = __glXSetupForCommand(psc->dpy);
   if (!opcode) {
      return NULL;
   }

   for (i = 0; i < num_attribs; i++) {
      uint32_t attr = attribs[i*2], val = attribs[i*2 + 1];

      if (attr == GLX_RENDER_TYPE)
         renderType = val;
      if (attr == GLX_CONTEXT_PROFILE_MASK_ARB)
         mask = val;
      if (attr == GLX_CONTEXT_MAJOR_VERSION_ARB)
         major = val;
      if (attr == GLX_CONTEXT_MINOR_VERSION_ARB)
         minor = val;
   }

   /* We have no indirect support for core or ES contexts, and our compat
    * context support is limited to GL 1.4.
    */
   if (mask != GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB ||
       major != 1 ||
       minor > 4) {
      return NULL;
   }

   /* We can't share with a direct context */
   if (shareList && shareList->isDirect)
      return NULL;

   /* Allocate our context record */
   gc = calloc(1, sizeof *gc);
   if (!gc) {
      /* Out of memory */
      return NULL;
   }

   glx_context_init(gc, psc, mode);
   gc->isDirect = GL_FALSE;
   gc->vtable = &indirect_context_vtable;
   state = calloc(1, sizeof(struct __GLXattributeRec));
   gc->renderType = renderType;

   if (state == NULL) {
      /* Out of memory */
      free(gc);
      return NULL;
   }
   gc->client_state_private = state;
   state->NoDrawArraysProtocol = env_var_as_boolean("LIBGL_NO_DRAWARRAYS", false);

   /*
    ** Create a temporary buffer to hold GLX rendering commands.  The size
    ** of the buffer is selected so that the maximum number of GLX rendering
    ** commands can fit in a single X packet and still have room in the X
    ** packet for the GLXRenderReq header.
    */

   bufSize = (XMaxRequestSize(psc->dpy) * 4) - sz_xGLXRenderReq;
   gc->buf = malloc(bufSize);
   if (!gc->buf) {
      free(gc->client_state_private);
      free(gc);
      return NULL;
   }
   gc->bufSize = bufSize;

   /* Fill in the new context */
   gc->renderMode = GL_RENDER;

   state->storePack.alignment = 4;
   state->storeUnpack.alignment = 4;

   gc->attributes.stackPointer = &gc->attributes.stack[0];

   gc->pc = gc->buf;
   gc->bufEnd = gc->buf + bufSize;
   gc->isDirect = GL_FALSE;
   if (__glXDebug) {
      /*
       ** Set limit register so that there will be one command per packet
       */
      gc->limit = gc->buf;
   }
   else {
      gc->limit = gc->buf + bufSize - __GLX_BUFFER_LIMIT_SIZE;
   }
   gc->majorOpcode = opcode;

   /*
    ** Constrain the maximum drawing command size allowed to be
    ** transferred using the X_GLXRender protocol request.  First
    ** constrain by a software limit, then constrain by the protocol
    ** limit.
    */
   gc->maxSmallRenderCommandSize = MIN3(bufSize, __GLX_RENDER_CMD_SIZE_LIMIT,
                                        __GLX_MAX_RENDER_CMD_SIZE);


   return gc;
}

static const struct glx_screen_vtable indirect_screen_vtable = {
   .create_context         = indirect_create_context,
   .create_context_attribs = indirect_create_context_attribs,
   .query_renderer_integer = NULL,
   .query_renderer_string  = NULL,
};

_X_HIDDEN struct glx_screen *
indirect_create_screen(int screen, struct glx_display * priv)
{
   struct glx_screen *psc;

   psc = calloc(1, sizeof *psc);
   if (psc == NULL)
      return NULL;

   glx_screen_init(psc, screen, priv);
   psc->vtable = &indirect_screen_vtable;

   return psc;
}

#endif
