/*
 * Sample driver: Demo
 */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "eglconfig.h"
#include "eglcontext.h"
#include "egldisplay.h"
#include "egldriver.h"
#include "eglglobals.h"
#include "eglmode.h"
#include "eglscreen.h"
#include "eglsurface.h"


/**
 * Demo driver-specific driver class derived from _EGLDriver
 */
typedef struct demo_driver
{
   _EGLDriver Base;  /* base class/object */
   GLuint DemoStuff;
} DemoDriver;

#define DEMO_DRIVER(D) ((DemoDriver *) (D))


/**
 * Demo driver-specific surface class derived from _EGLSurface
 */
typedef struct demo_surface
{
   _EGLSurface Base;  /* base class/object */
   GLuint DemoStuff;
} DemoSurface;


/**
 * Demo driver-specific context class derived from _EGLContext
 */
typedef struct demo_context
{
   _EGLContext Base;  /* base class/object */
   GLuint DemoStuff;
} DemoContext;



static EGLBoolean
demoInitialize(_EGLDriver *drv, EGLDisplay dpy, EGLint *major, EGLint *minor)
{
   _EGLDisplay *disp = _eglLookupDisplay(dpy);
   _EGLScreen *scrn;
   EGLint i;

   /* Create a screen */
   scrn = calloc(1, sizeof(*scrn));
   _eglAddScreen(disp, scrn);

   /* Create the screen's modes - silly example */
   _eglAddMode(scrn, 1600, 1200, 72 * 1000, "1600x1200-72");
   _eglAddMode(scrn, 1280, 1024, 72 * 1000, "1280x1024-70");
   _eglAddMode(scrn, 1280, 1024, 70 * 1000, "1280x1024-70");
   _eglAddMode(scrn, 1024,  768, 72 * 1000, "1024x768-72");

   /* Create the display's visual configs - silly example */
   for (i = 0; i < 4; i++) {
      _EGLConfig config;
      _eglInitConfig(&config, i + 1);
      _eglSetConfigAttrib(&config, EGL_RED_SIZE, 8);
      _eglSetConfigAttrib(&config, EGL_GREEN_SIZE, 8);
      _eglSetConfigAttrib(&config, EGL_BLUE_SIZE, 8);
      _eglSetConfigAttrib(&config, EGL_ALPHA_SIZE, 8);
      _eglSetConfigAttrib(&config, EGL_BUFFER_SIZE, 32);
      if (i & 1) {
         _eglSetConfigAttrib(&config, EGL_DEPTH_SIZE, 32);
      }
      if (i & 2) {
         _eglSetConfigAttrib(&config, EGL_STENCIL_SIZE, 8);
      }
      _eglSetConfigAttrib(&config, EGL_SURFACE_TYPE,
                          (EGL_WINDOW_BIT | EGL_PIXMAP_BIT | EGL_PBUFFER_BIT));
      _eglAddConfig(disp, &config);
   }

   drv->Initialized = EGL_TRUE;

   *major = 1;
   *minor = 0;

   return EGL_TRUE;
}


static EGLBoolean
demoTerminate(_EGLDriver *drv, EGLDisplay dpy)
{
   /*DemoDriver *demo = DEMO_DRIVER(dpy);*/
   free(drv);
   return EGL_TRUE;
}


static DemoContext *
LookupDemoContext(EGLContext ctx)
{
   _EGLContext *c = _eglLookupContext(ctx);
   return (DemoContext *) c;
}


static DemoSurface *
LookupDemoSurface(EGLSurface surf)
{
   _EGLSurface *s = _eglLookupSurface(surf);
   return (DemoSurface *) s;
}



static EGLContext
demoCreateContext(_EGLDriver *drv, EGLDisplay dpy, EGLConfig config, EGLContext share_list, const EGLint *attrib_list)
{
   _EGLConfig *conf;
   DemoContext *c;
   _EGLDisplay *disp = _eglLookupDisplay(dpy);
   int i;

   conf = _eglLookupConfig(drv, dpy, config);
   if (!conf) {
      _eglError(EGL_BAD_CONFIG, "eglCreateContext");
      return EGL_NO_CONTEXT;
   }

   for (i = 0; attrib_list && attrib_list[i] != EGL_NONE; i++) {
      switch (attrib_list[i]) {
         /* no attribs defined for now */
      default:
         _eglError(EGL_BAD_ATTRIBUTE, "eglCreateContext");
         return EGL_NO_CONTEXT;
      }
   }

   c = (DemoContext *) calloc(1, sizeof(DemoContext));
   if (!c)
      return EGL_NO_CONTEXT;

   _eglInitContext(&c->Base);
   c->Base.Display = disp;
   c->Base.Config = conf;
   c->Base.DrawSurface = EGL_NO_SURFACE;
   c->Base.ReadSurface = EGL_NO_SURFACE;
   c->DemoStuff = 1;
   printf("demoCreateContext\n");

   /* generate handle and insert into hash table */
   _eglSaveContext(&c->Base);
   assert(c->Base.Handle);

   return c->Base.Handle;
}


static EGLSurface
demoCreateWindowSurface(_EGLDriver *drv, EGLDisplay dpy, EGLConfig config, NativeWindowType window, const EGLint *attrib_list)
{
   int i;
   for (i = 0; attrib_list && attrib_list[i] != EGL_NONE; i++) {
      switch (attrib_list[i]) {
         /* no attribs at this time */
      default:
         _eglError(EGL_BAD_ATTRIBUTE, "eglCreateWindowSurface");
         return EGL_NO_SURFACE;
      }
   }
   printf("eglCreateWindowSurface()\n");
   /* XXX unfinished */

   return EGL_NO_SURFACE;
}


static EGLSurface
demoCreatePixmapSurface(_EGLDriver *drv, EGLDisplay dpy, EGLConfig config, NativePixmapType pixmap, const EGLint *attrib_list)
{
   _EGLConfig *conf;
   EGLint i;

   conf = _eglLookupConfig(drv, dpy, config);
   if (!conf) {
      _eglError(EGL_BAD_CONFIG, "eglCreatePixmapSurface");
      return EGL_NO_SURFACE;
   }

   for (i = 0; attrib_list && attrib_list[i] != EGL_NONE; i++) {
      switch (attrib_list[i]) {
         /* no attribs at this time */
      default:
         _eglError(EGL_BAD_ATTRIBUTE, "eglCreatePixmapSurface");
         return EGL_NO_SURFACE;
      }
   }

   if (conf->Attrib[EGL_SURFACE_TYPE - FIRST_ATTRIB] == 0) {
      _eglError(EGL_BAD_MATCH, "eglCreatePixmapSurface");
      return EGL_NO_SURFACE;
   }

   printf("eglCreatePixmapSurface()\n");
   return EGL_NO_SURFACE;
}


static EGLSurface
demoCreatePbufferSurface(_EGLDriver *drv, EGLDisplay dpy, EGLConfig config,
                         const EGLint *attrib_list)
{
   DemoSurface *surf = (DemoSurface *) calloc(1, sizeof(DemoSurface));
   if (!surf)
      return EGL_NO_SURFACE;

   if (_eglInitPbufferSurface(&surf->Base, drv, dpy, config, attrib_list)
       == EGL_NO_SURFACE) {
      free(surf);
      return EGL_NO_SURFACE;
   }

   /* a real driver would allocate the pbuffer memory here */

   return surf->Base.Handle;
}


static EGLBoolean
demoDestroySurface(_EGLDriver *drv, EGLDisplay dpy, EGLSurface surface)
{
   DemoSurface *fs = LookupDemoSurface(surface);
   _eglRemoveSurface(&fs->Base);
   if (fs->Base.IsBound) {
      fs->Base.DeletePending = EGL_TRUE;
   }
   else {
      free(fs);
   }
   return EGL_TRUE;
}


static EGLBoolean
demoDestroyContext(_EGLDriver *drv, EGLDisplay dpy, EGLContext context)
{
   DemoContext *fc = LookupDemoContext(context);
   _eglRemoveContext(&fc->Base);
   if (fc->Base.IsBound) {
      fc->Base.DeletePending = EGL_TRUE;
   }
   else {
      free(fc);
   }
   return EGL_TRUE;
}


static EGLBoolean
demoMakeCurrent(_EGLDriver *drv, EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext context)
{
   /*DemoDriver *demo = DEMO_DRIVER(dpy);*/
   DemoSurface *readSurf = LookupDemoSurface(read);
   DemoSurface *drawSurf = LookupDemoSurface(draw);
   DemoContext *ctx = LookupDemoContext(context);
   EGLBoolean b;

   b = _eglMakeCurrent(drv, dpy, draw, read, context);
   if (!b)
      return EGL_FALSE;

   /* XXX this is where we'd do the hardware context switch */
   (void) drawSurf;
   (void) readSurf;
   (void) ctx;

   printf("eglMakeCurrent()\n");
   return EGL_TRUE;
}


/**
 * The bootstrap function.  Return a new DemoDriver object and
 * plug in API functions.
 */
_EGLDriver *
_eglMain(_EGLDisplay *dpy)
{
   DemoDriver *demo;

   demo = (DemoDriver *) calloc(1, sizeof(DemoDriver));
   if (!demo) {
      return NULL;
   }

   /* First fill in the dispatch table with defaults */
   _eglInitDriverFallbacks(&demo->Base);
   /* then plug in our Demo-specific functions */
   demo->Base.Initialize = demoInitialize;
   demo->Base.Terminate = demoTerminate;
   demo->Base.CreateContext = demoCreateContext;
   demo->Base.MakeCurrent = demoMakeCurrent;
   demo->Base.CreateWindowSurface = demoCreateWindowSurface;
   demo->Base.CreatePixmapSurface = demoCreatePixmapSurface;
   demo->Base.CreatePbufferSurface = demoCreatePbufferSurface;
   demo->Base.DestroySurface = demoDestroySurface;
   demo->Base.DestroyContext = demoDestroyContext;

   /* enable supported extensions */
   demo->Base.MESA_screen_surface = EGL_TRUE;
   demo->Base.MESA_copy_context = EGL_TRUE;

   return &demo->Base;
}
