/*
 * DOS/DJGPP Mesa Utility Toolkit
 * Version:  1.0
 *
 * Copyright (C) 2005  Daniel Borca   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, sublicense,
 * 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 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 NONINFRINGEMENT.  IN NO EVENT SHALL
 * DANIEL BORCA 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 <stdio.h>

#include "internal.h"


static GLuint swaptime, swapcount;

static DMesaVisual visual = NULL;

GLUTwindow *_glut_current, *_glut_windows[MAX_WINDOWS];


static void
clean (void)
{
   int i;

   for (i=1; i<=MAX_WINDOWS; i++) {
      glutDestroyWindow(i);
   }
   if (visual) DMesaDestroyVisual(visual);

   pc_close_stdout();
   pc_close_stderr();
}


static GLUTwindow *
_glut_window (int win)
{
   if (win > 0 && --win < MAX_WINDOWS) {
      return _glut_windows[win];
   }
   return NULL;
}


int APIENTRY
glutCreateWindow (const char *title)
{
   int i;
   int m8width = (_glut_default.width + 7) & ~7;

   if (!(_glut_default.mode & GLUT_DOUBLE)) {
      return 0;
   }
    
   /* We set the Visual once. This will be our desktop (graphic mode).
    * We should do this in the `glutInit' code, but we don't have any idea
    * about its geometry. Supposedly, when we are about to create one
    * window, we have a slight idea about resolution.
    */
   if (!visual) {
      if ((visual=DMesaCreateVisual(_glut_default.x + m8width, _glut_default.y + _glut_default.height, _glut_visual.bpp, _glut_visual.refresh,
                                    GLUT_SINGLE,
                                    !(_glut_default.mode & GLUT_INDEX),
                                    (_glut_default.mode & GLUT_ALPHA  ) ? _glut_visual.alpha   : 0,
                                    (_glut_default.mode & GLUT_DEPTH  ) ? _glut_visual.depth   : 0,
                                    (_glut_default.mode & GLUT_STENCIL) ? _glut_visual.stencil : 0,
                                    (_glut_default.mode & GLUT_ACCUM  ) ? _glut_visual.accum   : 0))==NULL) {
         return 0;
      }

      DMesaGetIntegerv(DMESA_GET_SCREEN_SIZE, _glut_visual.geometry);
      DMesaGetIntegerv(DMESA_GET_DRIVER_CAPS, &_glut_visual.flags);

      /* Also hook stdio/stderr once */
      pc_open_stdout();
      pc_open_stderr();
      pc_atexit(clean);
   }

   /* Search for an empty slot.
    * Each window has its own rendering Context and its own Buffer.
    */
   for (i=0; i<MAX_WINDOWS; i++) {
      if (_glut_windows[i] == NULL) {
         DMesaContext c;
         DMesaBuffer b;
         GLUTwindow *w;

         if ((w = (GLUTwindow *)calloc(1, sizeof(GLUTwindow))) == NULL) {
            return 0;
         }

         /* Allocate the rendering Context. */
         if ((c = DMesaCreateContext(visual, NULL)) == NULL) {
            free(w);
            return 0;
         }

         /* Allocate the Buffer (displayable area).
          * We have to specify buffer size and position (inside the desktop).
          */
         if ((b = DMesaCreateBuffer(visual, _glut_default.x, _glut_default.y, m8width, _glut_default.height)) == NULL) {
            DMesaDestroyContext(c);
            free(w);
            return 0;
         }

         /* Bind Buffer to Context and make the Context the current one. */
         if (!DMesaMakeCurrent(c, b)) {
            DMesaDestroyBuffer(b);
            DMesaDestroyContext(c);
            free(w);
            return 0;
         }

         _glut_current = _glut_windows[i] = w;

         w->num = ++i;
         w->xpos = _glut_default.x;
         w->ypos = _glut_default.y;
         w->width = m8width;
         w->height = _glut_default.height;
         w->context = c;
         w->buffer = b;

         return i;
      }
   }

   return 0;
}


int APIENTRY
glutCreateSubWindow (int win, int x, int y, int width, int height)
{
   return GL_FALSE;
}


void APIENTRY
glutDestroyWindow (int win)
{
   GLUTwindow *w = _glut_window(win);
   if (w != NULL) {
      if (w->destroy) {
         w->destroy();
      }
      DMesaMakeCurrent(NULL, NULL);
      DMesaDestroyBuffer(w->buffer);
      DMesaDestroyContext(w->context);
      free(w);
      _glut_windows[win - 1] = NULL;
   }
}


void APIENTRY
glutPostRedisplay (void)
{
   _glut_current->redisplay = GL_TRUE;
}


void APIENTRY
glutSwapBuffers (void)
{
   if (_glut_current->show_mouse) {
      /* XXX scare mouse */
      DMesaSwapBuffers(_glut_current->buffer);
      /* XXX unscare mouse */
   } else {
      DMesaSwapBuffers(_glut_current->buffer);
   }

   if (_glut_fps) {
      GLint t = glutGet(GLUT_ELAPSED_TIME);
      swapcount++;
      if (swaptime == 0)
         swaptime = t;
      else if (t - swaptime > _glut_fps) {
         double time = 0.001 * (t - swaptime);
         double fps = (double)swapcount / time;
         fprintf(stderr, "GLUT: %d frames in %.2f seconds = %.2f FPS\n", swapcount, time, fps);
         swaptime = t;
         swapcount = 0;
      }
   }
}


int APIENTRY
glutGetWindow (void)
{
   return _glut_current->num;
}


void APIENTRY
glutSetWindow (int win)
{
   GLUTwindow *w = _glut_window(win);
   if (w != NULL) {
      _glut_current = w;
      DMesaMakeCurrent(_glut_current->context, _glut_current->buffer);
   }
}


void APIENTRY
glutSetWindowTitle (const char *title)
{
}


void APIENTRY
glutSetIconTitle (const char *title)
{
}


void APIENTRY
glutPositionWindow (int x, int y)
{
   if (DMesaMoveBuffer(x, y)) {
      _glut_current->xpos = x;
      _glut_current->ypos = y;
   }
}


void APIENTRY
glutReshapeWindow (int width, int height)
{ 
   if (DMesaResizeBuffer(width, height)) {
      _glut_current->width = width;
      _glut_current->height = height;
      if (_glut_current->reshape) {
         _glut_current->reshape(width, height);
      } else {
         glViewport(0, 0, width, height);
      }
   }
}


void APIENTRY
glutFullScreen (void)
{
}


void APIENTRY
glutPopWindow (void)
{
}


void APIENTRY
glutPushWindow (void)
{
}


void APIENTRY
glutIconifyWindow (void)
{
}


void APIENTRY
glutShowWindow (void)
{
}


void APIENTRY
glutHideWindow (void)
{
}


void APIENTRY
glutCloseFunc (GLUTdestroyCB destroy)
{
   _glut_current->destroy = destroy;
}


void APIENTRY
glutPostWindowRedisplay (int win)
{
   GLUTwindow *w = _glut_window(win);
   if (w != NULL) {
      w->redisplay = GL_TRUE;
   }
}


void * APIENTRY
glutGetWindowData (void)
{
   return _glut_current->data;
}


void APIENTRY
glutSetWindowData (void *data)
{
   _glut_current->data = data;
}