/*
 * $Id: amigaMesa.c 3.00 1998/09/15 19:16:56 NielsF Exp NielsF $ 
 */

/*
 * Mesa 3-D graphics library
 * Copyright (C) 1995  Brian Paul  (brianp@ssec.wisc.edu)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * $Log: amigaMesa.c $
 * Revision 3.00  1998/09/15  19:16:56  NielsF
 * - Mesa 3.0 API
 * - fixed most of the bugs
 *
 * $Log: amigaMesa.c $
 * Revision 1.16  1997/06/25  19:16:56  StefanZ
 * New drawing rutines:
 * - Now in separate files
 * - cyberGfx added.
 *
 * Revision 1.15  1996/10/13  20:54:59  StefanZ
 * A few nasty bugfixes.
 *
 * Revision 1.14  1996/10/07  00:11:07  StefanZ
 * Mesa 2.0 Fixed
 *
 * Revision 1.13  1996/10/06  20:31:50  StefanZ
 * Source Bump before Mesa 2.0
 *
 * Revision 1.12  1996/08/14  22:17:32  StefanZ
 * New API to amigacalls (uses taglist)
 * Made it more flexible to add gfx-card support.
 * Fast doublebuff rutines
 * minor bugfixes
 *
 * Revision 1.10    1996/06/12  13:06:00  StefanZ
 * BugFix and encatments by Jorge Acereda (JAM)
 *
 * Revision 1.9  1996/06/11  15:53:02   StefanZ
 * Little speedup, VerryFast colorallocation by Stefan Burstöm
 *
 * Revision 1.8  1996/06/01  23:57:47   StefanZ
 * Started to use RCS to keep track of code.
 *
 * Revision 1.7  1996/05/21  23:08:42   StefanZ
 * A few bug and enforcer fixes
 *
 * Revision 1.6  1996/04/29  22:14:31   StefanZ
 * BugFixes reported by by Daniel Jönsson
 *
 * Revision 1.5  1996/03/14  23:54:33   StefanZ
 * Doublebuffer & Tmprastport seams to work (big speed improvment)
 * a fastpolydraw is also implemented
 *
 * Revision 1.4  1996/03/07  16:55:04   StefanZ
 * Much of the code works now (RGB mode is simulated) Doublebuffers... (didn't work)
 *
 * Revision 1.3  1996/02/29  02:12:45   StefanZ
 * First sight of colors (even the right ones) maglight.c works
 *
 * Revision 1.2  1996/02/25  13:11:16   StefanZ
 * First working version. Draws everything with the same color
 * (Colormaping is now urgent needed)
 *
 * Revision 1.1  1996/02/23  22:01:15   StefanZ
 * Made changes to match latest version of ddsample 1.5
 *
 * Revision 1.0  1996/02/21  11:01:15   StefanZ
 * File created from ddsample.c ver 1.3 and amesa.c ver 1.5
 * in a brave atempt to rebuild the amiga version
 *
 */

/*
 * TODO:
 * Dynamic allocate the vectorbuffer for polydrawing. (memory improvment)
 * implement shared list.
 * fix resizing bug.
 * some native asm rutine
 * fast asm line drawin in db mode
 * fast asm clear       in db mode
 * read buffer rutines  in db-mode
 * 
 * IDEAS:
 * Make the gl a sharedlibrary. (Have ben started look in /amiga)
 */

/*
 * Note that you'll usually have to flip Y coordinates since Mesa's
 * window coordinates start at the bottom and increase upward.  Most
 * window system's Y-axis increases downward
 *
 * See dd.h for more device driver info.
 * See the other device driver implementations for ideas.
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <gl/gl.h>
#include "context.h"
#include "dd.h"
#include "xform.h"
#include "macros.h"
#include "vb.h"

#include <AOS/amigamesa.h>

/**********************************************************************/
/*****                Internal Data                               *****/
/**********************************************************************/

GLenum LastError;						/*  The last error generated */
struct Library *CyberGfxBase = NULL;				/*  optional base address for cybergfx  */
struct Library *GfxBase = NULL;
struct Library *IntuitionBase = NULL;
struct Library *UtilityBase = NULL;
APTR amesaPool = NULL;
#if !defined(NDEBUG) && !defined(NODEBUG)
LONG debugOutput = 0;
#endif
int trueColor = 0;
BOOL tryCached = GL_FALSE;

/**********************************************************************/
/*****                Some Usefull code                           *****/
/**********************************************************************/

/*
 * The Drawing area is defined by:
 * 
 * CC.Viewport.X = x;
 * CC.Viewport.Width = width;
 * CC.Viewport.Y = y;
 * CC.Viewport.Height = height;
 */

/**********************************************************************/
/**********************************************************************/

void glConstructor(void)
{
  char *Flag;

  if (!(CyberGfxBase = OpenLibrary("cybergraphics.library", 39))) {
    DEBUGOUT(1, "Can't open cybergraphics.library version 39\n");
  }
  if (!(IntuitionBase = OpenLibrary("intuition.library", 39))) {
    printf("Can't open intuition.library version 39\n");
    exit(10);
  }
  if (!(GfxBase = OpenLibrary("graphics.library", 39))) {
    printf("Can't open graphics.library version 39\n");
    exit(10);
  }
  if (!(UtilityBase = OpenLibrary("utility.library", 39))) {
    printf("Can't open utility.library version 39\n");
    exit(10);
  }
  if (!(amesaPool = CreatePool(MEMF_ANY, 65536, 32768))) {
    printf("Can't create mempools for amesa\n");
    exit(10);
  }

#if !defined(NDEBUG) && !defined(NODEBUG)
  if((Flag = getenv("MESA_DEBUG"))) {
    if(!strncasecmp(Flag, "ON", 2)) {
      debugOutput = 1;
      if ((Flag[2] == ' ') &&
          (Flag[3] >= '0') &&
          (Flag[3] <= '9'))
        debugOutput = Flag[3] - '0';
      DEBUGOUT(1, "enable debugging stage %ld\n", debugOutput);
    }
    else {
      debugOutput = 0;
      DEBUGOUT(1, "disable debugging\n");
    }
  }
#endif

  if((Flag = getenv("MESA_TRUECOLOR"))) {
    if(!strncasecmp(Flag, "ON", 2)) {
      trueColor = 1;
      DEBUGOUT(1, "enable 332 truecolor colormap\n");

      if(!strncasecmp(Flag + 3, "MATCH", 2)) {
        trueColor = -1;
        DEBUGOUT(1, "disable 332 truecolor match by shift\n");
      }
      else {
        trueColor = 1;
        DEBUGOUT(1, "disable 332 truecolor match by shift\n");
      }
    }
    else {
      trueColor = 0;
      DEBUGOUT(1, "disable 332 truecolor colormap\n");
    }
  }

  if((Flag = getenv("MESA_PENCACHE"))) {
    if(!strncasecmp(Flag, "ON", 2)) {
      tryCached = GL_TRUE;
      DEBUGOUT(1, "enable penCache\n");
    }
    else {
      tryCached = GL_FALSE;
      DEBUGOUT(1, "disable penCache\n");
    }
  }
}

void glDestructor(void)
{
  if (CyberGfxBase) {
    CloseLibrary(CyberGfxBase);
    CyberGfxBase = 0;
  }
  if (IntuitionBase) {
    CloseLibrary(IntuitionBase);
    IntuitionBase = 0;
  }
  if (GfxBase) {
    CloseLibrary(GfxBase);
    GfxBase = 0;
  }
  if (UtilityBase) {
    CloseLibrary(UtilityBase);
    UtilityBase = 0;
  }
  if (amesaPool) {
    DeletePool(amesaPool);
    amesaPool = 0;
  }
}

#if 0
asm("	.text; 	.stabs \"___CTOR_LIST__\",22,0,0,_constructor");
asm("	.text; 	.stabs \"___DTOR_LIST__\",22,0,0,_destructor");
#endif

/**********************************************************************/
/***** amigaMesaSet/GetOneColor                                       *****/
/**********************************************************************/

ULONG amigaMesaGetOneColor(struct amigamesa_context *amesa, int index)
{
  if (amesa->depth > 8)
    return amesa->penconv[(unsigned char)index] >> 8;
  else {
    //DEBUGOUT(4, "result: 0x%lx, penconv: %x, index: %x\n", GetIndex(amesa->penconv[(unsigned char)index]) >> 8, amesa->penconv[(unsigned char)index], index);
    return GetIndex(amesa->penconv[(unsigned char)index]) >> 8;
  }
}

void amigaMesaSetOneColor(struct amigamesa_context *amesa, int index, float r, float g, float b)
{
  if (amesa->depth > 8)
    amesa->penconv[(unsigned char)index] = TC_RGBA((int)(r * 255), (int)(g * 255), (int)(b * 255));
  else {
    amesa->penconv[(unsigned char)index] = PL_RGBA((unsigned char)(r * 255), (unsigned char)(g * 255), (unsigned char)(b * 255));
    //DEBUGOUT(4, "r: %x, g: %x, b: %x, penconv: %x, index: %x\n", (unsigned char)(r * 255), (unsigned char)(g * 255), (unsigned char)(b * 255), amesa->penconv[(unsigned char)index], index);
  }
}

/**********************************************************************/
/*****                  amiga/Mesa API Functions                  *****/
/**********************************************************************/
/*
 * Implement the client-visible amiga/Mesa interface functions defined
 * in Mesa/include/GL/amigamesa.h
 *
 **********************************************************************/

struct amigamesa_visual *amigaMesaCreateVisualTags(long Tag1,...)
{
  return amigaMesaCreateVisual((struct TagItem *)&Tag1);
}

struct amigamesa_visual *amigaMesaCreateVisual(struct TagItem *tagList)
{
  struct amigamesa_visual *v;
  struct Window *actWindow;
  struct Screen *actScreen;
  struct RastPort *actRPort;
  GLint rbits = 8, gbits = 8, bbits = 8, abits = 0;
  GLint index_bits = 0;

  if (!(v = (struct amigamesa_visual *)AllocVecPooled(amesaPool, sizeof(struct amigamesa_visual)))) {
    LastError = AMESA_OUT_OF_MEM;
    return NULL;
  }
  bzero(v, sizeof(struct amigamesa_visual));

  if (GetTagData(AMA_RGBMode, 0xDEAD, tagList) == 0xDEAD) {
    if ((actWindow = (struct Window *)GetTagData(AMA_Window, NULL, tagList))) {
      actScreen = (struct Screen *)GetTagData(AMA_Screen, (unsigned long)actWindow->WScreen, tagList);
      actRPort = (struct RastPort *)GetTagData(AMA_RastPort, (unsigned long)actWindow->RPort, tagList);
    }
    else {
      actScreen = (struct Screen *)GetTagData(AMA_Screen, NULL, tagList);
      actRPort = (struct RastPort *)GetTagData(AMA_RastPort, NULL, tagList);
    }

    if (actRPort) {
      if ((CyberGfxBase) && (GetCyberMapAttr(actRPort->BitMap, CYBRMATTR_ISCYBERGFX) == TRUE)) {
	int pixFmt;
	int pixDpt;

	pixFmt = GetCyberMapAttr(actRPort->BitMap, CYBRMATTR_PIXFMT);
	pixDpt = GetCyberMapAttr(actRPort->BitMap, CYBRMATTR_DEPTH);

	switch (pixFmt) {
	  case PIXFMT_LUT8:
	    index_bits = pixDpt;
	    v->alpha_flag = GL_FALSE;
	    break;
	  case PIXFMT_RGB15:
	  case PIXFMT_BGR15:
	  case PIXFMT_RGB15PC:
	  case PIXFMT_BGR15PC:
	    rbits = gbits = bbits = 5;
	    abits = 0;
	    v->alpha_flag = GL_FALSE;
	    break;
	  case PIXFMT_RGB16:
	  case PIXFMT_BGR16:
	  case PIXFMT_RGB16PC:
	  case PIXFMT_BGR16PC:
	    rbits = bbits = 5;
	    gbits = 6;
	    abits = 0;
	    v->alpha_flag = GL_FALSE;
	    break;
	  case PIXFMT_RGB24:
	  case PIXFMT_BGR24:
	    v->alpha_flag = GL_FALSE;
	    break;
	  case PIXFMT_ARGB32:
	  case PIXFMT_BGRA32:
	  case PIXFMT_RGBA32:
	    abits = 8;
	    v->alpha_flag = GL_TRUE;
	    break;
	}
      }
      else {
	index_bits = GetBitMapAttr(actRPort->BitMap, BMA_DEPTH);
	rbits = gbits = bbits = abits = 0;
      }
    }
  }
  else if (GetTagData(AMA_RGBMode, 0xDEAD, tagList) == GL_FALSE) {
    if ((actRPort = (struct RastPort *)GetTagData(AMA_RastPort, NULL, tagList)))
      index_bits = GetBitMapAttr(actRPort->BitMap, BMA_DEPTH);
    else
      index_bits = 8;

    rbits = gbits = bbits = abits = 0;
  }
  else {
  }

  /*
   * Doublebuffer always configureable
   */
  if (!(v->db_flag = GetTagData(AMA_DoubleBuf, GL_FALSE, tagList)))
    DEBUGOUT(1, "disable doublebuffering\n");
  else
    DEBUGOUT(1, "enable doublebuffering\n");

  /*
   * RGBMode if no index_bits
   */
  if (!(v->rgb_flag = index_bits == 0 ? GL_TRUE : GL_FALSE))
    DEBUGOUT(1, "disable rgbmode\n");
  else
    DEBUGOUT(1, "enable rgbmode\n");

  /*
   * AlphaMode configureable if RGBMode (others make no sence)
   */
  if (!(v->alpha_flag = index_bits == 0 ? v->alpha_flag : GetTagData(AMA_AlphaFlag, GL_FALSE, tagList)))
    DEBUGOUT(1, "disable alphachannel\n");
  else
    DEBUGOUT(1, "enable alphachannel\n");

  /*
   * Create core visual 
   */
  v->gl_visual = gl_create_visual(v->rgb_flag,
				  v->alpha_flag,
				  v->db_flag,
				  GL_FALSE,			/*  stereo */
				  DEPTH_BITS,			/*  depth_size  */
				  STENCIL_BITS,			/*  stencil_size  */
				  ACCUM_BITS,			/*  accum_size  */
				  index_bits,
				  rbits, gbits, bbits, abits);
  return v;
}

void amigaMesaDestroyVisual(struct amigamesa_visual *v)
{
  gl_destroy_visual(v->gl_visual);
  FreeVecPooled(amesaPool, (ULONG *)v);
}

/*****************************************************************************/

struct amigamesa_buffer *amigaMesaCreateBuffer(struct amigamesa_visual *visual, int windowid)
{
  struct amigamesa_buffer *b;

  if (!(b = (struct amigamesa_buffer *)AllocVecPooled(amesaPool, sizeof(struct amigamesa_buffer)))) {
    LastError = AMESA_OUT_OF_MEM;
  }
  else {
    bzero(b, sizeof(struct amigamesa_buffer));
    b->gl_buffer = gl_create_framebuffer(visual->gl_visual);
    /* other stuff */
  }

  return b;
}

void amigaMesaDestroyBuffer(struct amigamesa_buffer *b)
{
  gl_destroy_framebuffer(b->gl_buffer);
  FreeVecPooled(amesaPool, (ULONG *)b);
}

/*****************************************************************************/

struct amigamesa_context *amigaMesaCreateContextTags(long Tag1,...)
{
  return amigaMesaCreateContext((struct TagItem *)&Tag1);
}

struct amigamesa_context *amigaMesaCreateContext(struct TagItem *tagList)
{
  /*
   * Create a new amiga/Mesa context 
   * Be sure to initialize the following in the core Mesa context: 
   * DrawBuffer, ReadBuffer 
   */
  struct amigamesa_context *amesa;
  BOOL (*Init)(struct amigamesa_context *amesa, struct TagItem *tagList);

  /*
   * allocate amigamesa_context struct initialized to zeros 
   */
  if (!(amesa = (struct amigamesa_context *)AllocVecPooled(amesaPool, sizeof(struct amigamesa_context)))) {
    LastError = AMESA_OUT_OF_MEM;
    return (NULL);
  }
  bzero(amesa, sizeof(struct amigamesa_context));

  amesa->visual = (struct amigamesa_visual *)GetTagData(AMA_Visual, NULL, tagList);
  amesa->buffer = (struct amigamesa_buffer *)GetTagData(AMA_Buffer, NULL, tagList);
  /* amesa->memPool = amesaPool; */

  if (!amesa->visual) {
    if (!(amesa->visual = amigaMesaCreateVisual(tagList))) {
      LastError = AMESA_OUT_OF_MEM;
      return NULL;
    }
    amesa->flags |= RESOURCE_VISUAL;
  }

  if (!amesa->buffer) {
    if (!(amesa->buffer = amigaMesaCreateBuffer(amesa->visual, GetTagData(AMA_WindowID, 1, tagList)))) {
      LastError = AMESA_OUT_OF_MEM;
      return NULL;
    }
    amesa->flags |= RESOURCE_BUFFER;
  }

  if((amesa->share = (struct amigamesa_context *)GetTagData(AMA_ShareGLContext, NULL, tagList)))
    DEBUGOUT(1, "use shared contexts\n");
  /*  sharetmp = amesa->share; */
  amesa->gl_ctx = gl_create_context(amesa->visual->gl_visual, amesa->share ? amesa->share->gl_ctx : NULL, (void *)amesa, GL_FALSE);

  /*  drawMode = GetTagData(AMA_DrawMode,AMESA_AGA,tagList);  */
  if (amesa->visual->db_flag == GL_TRUE) {
    if (CyberGfxBase)
      Init = cybStandardInitDB;
    else
      Init = amigaStandardInitDB;
  }
  else {
    if (CyberGfxBase)
      Init = cybStandardInit;
    else
      Init = amigaStandardInit;
  }

  if (!(*Init)(amesa, tagList)) {
    amigaMesaDestroyContext(amesa);
    amesa = NULL;
  }

  return amesa;
}

void amigaMesaDestroyContext(struct amigamesa_context *amesa)
{
  /*
   * destroy a amiga/Mesa context 
   */

  /*  if (amesa==amesa) */
  /*    amesa=NULL; */
  (*amesa->Dispose) (amesa);

  if (amesa->flags & RESOURCE_VISUAL)
    amigaMesaDestroyVisual(amesa->visual);
  if (amesa->flags & RESOURCE_BUFFER)
    amigaMesaDestroyBuffer(amesa->buffer);

  gl_destroy_context(amesa->gl_ctx);
  FreeVecPooled(amesaPool, (ULONG *)amesa);
}

/*****************************************************************************/

void amigaMesaMakeCurrent(struct amigamesa_context *amesa, struct amigamesa_buffer *b)
{
  /*
   * Make the specified context the current one 
   * the order of operations here is very important! 
   */

  /*  Current = amesa; */
  if (amesa && b) {
    (*amesa->InitDD)(amesa->gl_ctx);				/*  Call Driver_init_rutine  */
    gl_make_current(amesa->gl_ctx, b->gl_buffer);

    if (amesa->gl_ctx->Viewport.Width == 0) {
      /* initialize viewport to window size */
      glViewport(0, 0, amesa->width, amesa->height);
      amesa->gl_ctx->Scissor.Width = amesa->width;
      amesa->gl_ctx->Scissor.Height = amesa->height;
    }
  }
}

/*****************************************************************************/

void amigaMesaSwapBuffers(struct amigamesa_context *amesa)
{
  /* copy/swap back buffer to front if applicable */
  (*amesa->SwapBuffer)(amesa);
}

/*
 * This is on the drawingboard 
 * Mostly for when future changes the library is still intact
 */

/*****************************************************************************/

BOOL amigaMesaSetDefs(struct TagItem *tagList)
{
  /*  struct TagItem *tag; */

  /*  tagValue=AMA_DrawMode; */
  /*  tag = FindTagItem(tagValue,tagList); */
  /*  if (tag) */
  /*    printf("Tag=0x%x, is 0x%x/n",tagValue,tag->ti_Data); */
  /*  else */
  /*    printf("Tag=0x%x is not specified/n",tagValue); */
  return FALSE;
}

/*****************************************************************************/

/*
 * Maybe a report error rutine ??? like:
 */
GLenum amigaMesaReportError(struct amigamesa_context *amesa)
{
  GLenum error;

  error = LastError;
  LastError = NULL;
  return (error);
}

#undef DEBUGPRINT
