/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2004 Sam Lantinga

    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
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Sam Lantinga
    slouken@libsdl.org
*/

#ifdef SAVE_RCSID
static char rcsid =
 "@(#) $Id: SDL_ph_image.c,v 1.17 2004/02/14 20:22:20 slouken Exp $";
#endif

#include <stdlib.h>
#include <Ph.h>
#include <photon/Pg.h>

#include "SDL.h"
#include "SDL_error.h"
#include "SDL_endian.h"
#include "SDL_video.h"
#include "SDL_pixels_c.h"
#include "SDL_ph_image_c.h"
#include "SDL_ph_modes_c.h"

int ph_SetupImage(_THIS, SDL_Surface *screen)
{
    PgColor_t* palette=NULL;
    int type=0;
    int bpp;
    
    bpp=screen->format->BitsPerPixel;

    /* Determine image type */
    switch(bpp)
    {
        case 8:{
            type = Pg_IMAGE_PALETTE_BYTE;
        }
        break;
        case 15:{
            type = Pg_IMAGE_DIRECT_555; 
        }
        break;
        case 16:{
            type = Pg_IMAGE_DIRECT_565; 
        }
        break;
        case 24:{
            type = Pg_IMAGE_DIRECT_888;
        }
        break;
        case 32:{
            type = Pg_IMAGE_DIRECT_8888;
        }
        break;
        default:{
            SDL_SetError("ph_SetupImage(): unsupported bpp=%d !\n", bpp);
            return -1;
        }
        break;
    }

    /* palette emulation code */
    if ((bpp==8) && (desktoppal==SDLPH_PAL_EMULATE))
    {
        /* creating image palette */
        palette=malloc(_Pg_MAX_PALETTE*sizeof(PgColor_t));
        if (palette==NULL)
        {
            SDL_SetError("ph_SetupImage(): can't allocate memory for palette !\n");
            return -1;
        }
        PgGetPalette(palette);

        /* using shared memory for speed (set last param to 1) */
        if ((SDL_Image = PhCreateImage(NULL, screen->w, screen->h, type, palette, _Pg_MAX_PALETTE, 1)) == NULL)
        {
            SDL_SetError("ph_SetupImage(): PhCreateImage() failed for bpp=8 !\n");
            free(palette);
            return -1;
        }
    }
    else
    {
        /* using shared memory for speed (set last param to 1) */
        if ((SDL_Image = PhCreateImage(NULL, screen->w, screen->h, type, NULL, 0, 1)) == NULL)
        {
            SDL_SetError("ph_SetupImage(): PhCreateImage() failed for bpp=%d !\n", bpp);
            return -1;
        }
    }

    screen->pixels = SDL_Image->image;
    screen->pitch = SDL_Image->bpl;

    this->UpdateRects = ph_NormalUpdate;

    return 0;
}

int ph_SetupOCImage(_THIS, SDL_Surface *screen)
{
    int type = 0;
    int bpp;

    OCImage.flags = screen->flags;
    
    bpp=screen->format->BitsPerPixel;

    /* Determine image type */
    switch(bpp)
    {
        case 8: {
                    type = Pg_IMAGE_PALETTE_BYTE;
                }
                break;
        case 15:{
                    type = Pg_IMAGE_DIRECT_555; 
		}
		break;
        case 16:{
                    type = Pg_IMAGE_DIRECT_565; 
                }
                break;
        case 24:{
                    type = Pg_IMAGE_DIRECT_888;
                }
                break;
        case 32:{
                    type = Pg_IMAGE_DIRECT_8888;
                }
                break;
        default:{
                    SDL_SetError("ph_SetupOCImage(): unsupported bpp=%d !\n", bpp);
                    return -1;
                }
                break;
    }

    /* Currently only offscreen contexts with the same bit depth as the
     * display can be created. */
    OCImage.offscreen_context = PdCreateOffscreenContext(0, screen->w, screen->h, Pg_OSC_MEM_PAGE_ALIGN);

    if (OCImage.offscreen_context == NULL)
    {
        SDL_SetError("ph_SetupOCImage(): PdCreateOffscreenContext() function failed !\n");
        return -1;
    }

    screen->pitch = OCImage.offscreen_context->pitch;

    OCImage.dc_ptr = (unsigned char *)PdGetOffscreenContextPtr(OCImage.offscreen_context);

    if (OCImage.dc_ptr == NULL)
    {
        SDL_SetError("ph_SetupOCImage(): PdGetOffscreenContextPtr function failed !\n");
        PhDCRelease(OCImage.offscreen_context);
        return -1;
    }

    OCImage.FrameData0 = OCImage.dc_ptr;
    OCImage.CurrentFrameData = OCImage.FrameData0;
    OCImage.current = 0;

    PhDCSetCurrent(OCImage.offscreen_context);

    screen->pixels = OCImage.CurrentFrameData;

    this->UpdateRects = ph_OCUpdate;

    return 0;
}

int ph_SetupFullScreenImage(_THIS, SDL_Surface* screen)
{
    OCImage.flags = screen->flags;

    /* Begin direct mode */
    if (!ph_EnterFullScreen(this, screen))
    {
        return -1;
    }

    /* store palette for fullscreen */
    if ((screen->format->BitsPerPixel==8) && (desktopbpp!=8))
    {
        PgGetPalette(savedpal);
        PgGetPalette(syspalph);
    }

    OCImage.offscreen_context = PdCreateOffscreenContext(0, 0, 0, Pg_OSC_MAIN_DISPLAY | Pg_OSC_MEM_PAGE_ALIGN | Pg_OSC_CRTC_SAFE);
    if (OCImage.offscreen_context == NULL)
    {
        SDL_SetError("ph_SetupFullScreenImage(): PdCreateOffscreenContext() function failed !\n");
        return -1;
    }
    
    if ((screen->flags & SDL_DOUBLEBUF) == SDL_DOUBLEBUF)
    {
        OCImage.offscreen_backcontext = PdDupOffscreenContext(OCImage.offscreen_context, Pg_OSC_MEM_PAGE_ALIGN | Pg_OSC_CRTC_SAFE);
        if (OCImage.offscreen_backcontext == NULL)
        {
            SDL_SetError("ph_SetupFullScreenImage(): PdCreateOffscreenContext(back) function failed !\n");
            return -1;
        }
    }

    OCImage.FrameData0 = (unsigned char *)PdGetOffscreenContextPtr(OCImage.offscreen_context);
    if (OCImage.FrameData0 == NULL)
    {
        SDL_SetError("ph_SetupFullScreenImage(): PdGetOffscreenContextPtr() function failed !\n");
        ph_DestroyImage(this, screen);
        return -1;
    }

    if ((screen->flags & SDL_DOUBLEBUF) == SDL_DOUBLEBUF)
    {
        OCImage.FrameData1 = (unsigned char *)PdGetOffscreenContextPtr(OCImage.offscreen_backcontext);
        if (OCImage.FrameData1 == NULL)
        {
            SDL_SetError("ph_SetupFullScreenImage(back): PdGetOffscreenContextPtr() function failed !\n");
            ph_DestroyImage(this, screen);
            return -1;
        }
    }

    /* wait for the hardware */
    PgFlush();
    PgWaitHWIdle();

    if ((screen->flags & SDL_DOUBLEBUF) == SDL_DOUBLEBUF)
    {
        OCImage.current = 0;
        PhDCSetCurrent(OCImage.offscreen_context);
        screen->pitch = OCImage.offscreen_context->pitch;
        screen->pixels = OCImage.FrameData0;
        
        /* emulate 640x400 videomode */
        if (videomode_emulatemode==1)
        {
           int i;
           
           for (i=0; i<40; i++)
           {
              memset(screen->pixels+screen->pitch*i, 0x00, screen->pitch);
           }
           for (i=440; i<480; i++)
           {
              memset(screen->pixels+screen->pitch*i, 0x00, screen->pitch);
           }
           screen->pixels+=screen->pitch*40;
        }
        PgSwapDisplay(OCImage.offscreen_backcontext, 0);
    }
    else
    {
        OCImage.current = 0;
        PhDCSetCurrent(OCImage.offscreen_context);
        screen->pitch = OCImage.offscreen_context->pitch;
        screen->pixels = OCImage.FrameData0;

        /* emulate 640x400 videomode */
        if (videomode_emulatemode==1)
        {
           int i;
           
           for (i=0; i<40; i++)
           {
              memset(screen->pixels+screen->pitch*i, 0x00, screen->pitch);
           }
           for (i=440; i<480; i++)
           {
              memset(screen->pixels+screen->pitch*i, 0x00, screen->pitch);
           }
           screen->pixels+=screen->pitch*40;
        }
    }

    this->UpdateRects = ph_OCDCUpdate;

    /* wait for the hardware */
    PgFlush();
    PgWaitHWIdle();

    return 0;
}

#ifdef HAVE_OPENGL

static int ph_SetupOpenGLContext(_THIS, int width, int height, int bpp, Uint32 flags)
{
    PhDim_t dim;
    uint64_t OGLAttrib[PH_OGL_MAX_ATTRIBS];
    int exposepost=0;
    int OGLargc;

    dim.w=width;
    dim.h=height;
    
    if ((oglctx!=NULL) && (oglflags==flags) && (oglbpp==bpp))
    {
       PdOpenGLContextResize(oglctx, &dim);
       PhDCSetCurrent(oglctx);
       return 0;
    }
    else
    {
       if (oglctx!=NULL)
       {
          PhDCSetCurrent(NULL);
          PhDCRelease(oglctx);
          oglctx=NULL;
          exposepost=1;
       }
    }

    OGLargc=0;
    if (this->gl_config.depth_size)
    {
        OGLAttrib[OGLargc++]=PHOGL_ATTRIB_DEPTH_BITS;
        OGLAttrib[OGLargc++]=this->gl_config.depth_size;
    }
    if (this->gl_config.stencil_size)
    {
        OGLAttrib[OGLargc++]=PHOGL_ATTRIB_STENCIL_BITS;
        OGLAttrib[OGLargc++]=this->gl_config.stencil_size;
    }
    OGLAttrib[OGLargc++]=PHOGL_ATTRIB_FORCE_SW;
    if (flags & SDL_FULLSCREEN)
    {
        OGLAttrib[OGLargc++]=PHOGL_ATTRIB_FULLSCREEN;
        OGLAttrib[OGLargc++]=PHOGL_ATTRIB_DIRECT;
        OGLAttrib[OGLargc++]=PHOGL_ATTRIB_FULLSCREEN_BEST;
        OGLAttrib[OGLargc++]=PHOGL_ATTRIB_FULLSCREEN_CENTER;
    }
    OGLAttrib[OGLargc++]=PHOGL_ATTRIB_NONE;

    if (this->gl_config.double_buffer)
    {
        oglctx=PdCreateOpenGLContext(2, &dim, 0, OGLAttrib);
    }
    else
    {
        oglctx=PdCreateOpenGLContext(1, &dim, 0, OGLAttrib);
    }

    if (oglctx==NULL)
    {
        SDL_SetError("ph_SetupOpenGLContext(): cannot create OpenGL context !\n");
        return (-1);
    }

    PhDCSetCurrent(oglctx);

    PtFlush();

    oglflags=flags;
    oglbpp=bpp;

    if (exposepost!=0)
    {
        /* OpenGL context has been recreated, so report about this fact */
        SDL_PrivateExpose();
    }

    return 0;
}

int ph_SetupOpenGLImage(_THIS, SDL_Surface* screen)
{
   this->UpdateRects = ph_OpenGLUpdate;
   screen->pixels=NULL;
   screen->pitch=NULL;

   if (ph_SetupOpenGLContext(this, screen->w, screen->h, screen->format->BitsPerPixel, screen->flags)!=0)
   {
      screen->flags &= ~SDL_OPENGL;
      return -1;
   }
   
   return 0;
}

#endif /* HAVE_OPENGL */

void ph_DestroyImage(_THIS, SDL_Surface* screen)
{

#ifdef HAVE_OPENGL
    if ((screen->flags & SDL_OPENGL)==SDL_OPENGL)
    {
        if (oglctx)
        {
            PhDCSetCurrent(NULL);
            PhDCRelease(oglctx);
            oglctx=NULL;
            oglflags=0;
            oglbpp=0;
        }
        return;
    }
#endif /* HAVE_OPENGL */

    if (currently_fullscreen)
    {
        /* if we right now in 8bpp fullscreen we must release palette */
        if ((screen->format->BitsPerPixel==8) && (desktopbpp!=8))
        {
            PgSetPalette(syspalph, 0, -1, 0, 0, 0);
            PgSetPalette(savedpal, 0, 0, _Pg_MAX_PALETTE, Pg_PALSET_GLOBAL | Pg_PALSET_FORCE_EXPOSE, 0);
            PgFlush();
        }
        ph_LeaveFullScreen(this);
    }

    if (OCImage.offscreen_context != NULL)
    {
        PhDCRelease(OCImage.offscreen_context);
        OCImage.offscreen_context = NULL;
        OCImage.FrameData0 = NULL;
    }
    if (OCImage.offscreen_backcontext != NULL)
    {
        PhDCRelease(OCImage.offscreen_backcontext);
        OCImage.offscreen_backcontext = NULL;
        OCImage.FrameData1 = NULL;
    }
    OCImage.CurrentFrameData = NULL;

    if (SDL_Image)
    {
        /* if palette allocated, free it */
        if (SDL_Image->palette)
        {
            free(SDL_Image->palette);
        }
        PgShmemDestroy(SDL_Image->image);
        free(SDL_Image);
    }

    /* Must be zeroed everytime */
    SDL_Image = NULL;

    if (screen)
    {
        screen->pixels = NULL;
    }
}

int ph_UpdateHWInfo(_THIS)
{
    PgVideoModeInfo_t vmode;
    PgHWCaps_t hwcaps;

    /* Update video ram amount */
    if (PgGetGraphicsHWCaps(&hwcaps) < 0)
    {
        SDL_SetError("ph_UpdateHWInfo(): GetGraphicsHWCaps() function failed !\n");
        return -1;
    }
    this->info.video_mem=hwcaps.currently_available_video_ram/1024;

    /* obtain current mode capabilities */
    if (PgGetVideoModeInfo(hwcaps.current_video_mode, &vmode) < 0)
    {
        SDL_SetError("ph_UpdateHWInfo(): GetVideoModeInfo() function failed !\n");
        return -1;
    }

    this->info.blit_hw = 1;

    if ((vmode.mode_capabilities2 & PgVM_MODE_CAP2_ALPHA_BLEND) == PgVM_MODE_CAP2_ALPHA_BLEND)
    {
       this->info.blit_hw_A = 1;
    }
    else
    {
       this->info.blit_hw_A = 0;
    }
    
    if ((vmode.mode_capabilities2 & PgVM_MODE_CAP2_CHROMA) == PgVM_MODE_CAP2_CHROMA)
    {
       this->info.blit_hw_CC = 1;
    }
    else
    {
       this->info.blit_hw_CC = 0;
    }
    
    return 0;
}

int ph_SetupUpdateFunction(_THIS, SDL_Surface* screen, Uint32 flags)
{
    int setupresult=-1;

    ph_DestroyImage(this, screen);
    
#ifdef HAVE_OPENGL
    if ((flags & SDL_OPENGL)==SDL_OPENGL)
    {
        setupresult=ph_SetupOpenGLImage(this, screen);
    }
    else
    {
#endif /* HAVE_OPENGL */
       if ((flags & SDL_FULLSCREEN)==SDL_FULLSCREEN)
       {
           setupresult=ph_SetupFullScreenImage(this, screen);
       }
       else
       {
          if ((flags & SDL_HWSURFACE)==SDL_HWSURFACE)
          {
              setupresult=ph_SetupOCImage(this, screen);
          }
          else
          {
              setupresult=ph_SetupImage(this, screen);
          }
       }
#ifdef HAVE_OPENGL
    }
#endif /* HAVE_OPENGL */
    if (setupresult!=-1)
    {
       ph_UpdateHWInfo(this);
    }
    
    return setupresult;
}

int ph_AllocHWSurface(_THIS, SDL_Surface* surface)
{
    PgHWCaps_t hwcaps;

    if (surface->hwdata!=NULL)
    {
       SDL_SetError("ph_AllocHWSurface(): hwdata already exists!\n");
       return -1;
    }
    surface->hwdata=malloc(sizeof(struct private_hwdata));
    memset(surface->hwdata, 0x00, sizeof(struct private_hwdata));
    surface->hwdata->offscreenctx=PdCreateOffscreenContext(0, surface->w, surface->h, Pg_OSC_MEM_PAGE_ALIGN);
    if (surface->hwdata->offscreenctx == NULL)
    {
        SDL_SetError("ph_AllocHWSurface(): PdCreateOffscreenContext() function failed !\n");
        return -1;
    }
    surface->pixels=PdGetOffscreenContextPtr(surface->hwdata->offscreenctx);
    if (surface->pixels==NULL)
    {
        PhDCRelease(surface->hwdata->offscreenctx);
        SDL_SetError("ph_AllocHWSurface(): PdGetOffscreenContextPtr() function failed !\n");
        return -1;
    }
    surface->pitch=surface->hwdata->offscreenctx->pitch;
    surface->flags|=SDL_HWSURFACE;
    surface->flags|=SDL_PREALLOC;
    
#if 0 /* FIXME */
    /* create simple offscreen lock */
    surface->hwdata->crlockparam.flags=0;
    if (PdCreateOffscreenLock(surface->hwdata->offscreenctx, &surface->hwdata->crlockparam)!=EOK)
    {
        PhDCRelease(surface->hwdata->offscreenctx);
        SDL_SetError("ph_AllocHWSurface(): Can't create offscreen lock !\n");
        return -1;
    }
#endif /* 0 */

    /* Update video ram amount */
    if (PgGetGraphicsHWCaps(&hwcaps) < 0)
    {
        PdDestroyOffscreenLock(surface->hwdata->offscreenctx);
        PhDCRelease(surface->hwdata->offscreenctx);
        SDL_SetError("ph_AllocHWSurface(): GetGraphicsHWCaps() function failed !\n");
        return -1;
    }
    this->info.video_mem=hwcaps.currently_available_video_ram/1024;

    return 0;
}

void ph_FreeHWSurface(_THIS, SDL_Surface* surface)
{
    PgHWCaps_t hwcaps;

    if (surface->hwdata==NULL)
    {
       SDL_SetError("ph_FreeHWSurface(): no hwdata!\n");
       return;
    }
    if (surface->hwdata->offscreenctx == NULL)
    {
       SDL_SetError("ph_FreeHWSurface(): no offscreen context to delete!\n");
       return;
    }

#if 0 /* FIXME */
    /* unlock the offscreen context if it has been locked before destroy it */
    if (PdIsOffscreenLocked(surface->hwdata->offscreenctx)==Pg_OSC_LOCKED)
    {
       PdUnlockOffscreen(surface->hwdata->offscreenctx);
    }

    PdDestroyOffscreenLock(surface->hwdata->offscreenctx);
#endif /* 0 */

    PhDCRelease(surface->hwdata->offscreenctx);
    
    free(surface->hwdata);
    surface->hwdata=NULL;

    /* Update video ram amount */
    if (PgGetGraphicsHWCaps(&hwcaps) < 0)
    {
        SDL_SetError("ph_FreeHWSurface(): GetGraphicsHWCaps() function failed !\n");
        return;
    }
    this->info.video_mem=hwcaps.currently_available_video_ram/1024;

    return;
}

int ph_CheckHWBlit(_THIS, SDL_Surface *src, SDL_Surface *dst)
{
   if ((src->hwdata==NULL) && (src != this->screen))
   {
      SDL_SetError("ph_CheckHWBlit(): Source surface haven't hardware specific data.\n");
      src->flags&=~SDL_HWACCEL;
      return -1;
   }
   if ((src->flags & SDL_HWSURFACE) != SDL_HWSURFACE)
   {
      SDL_SetError("ph_CheckHWBlit(): Source surface isn't a hardware surface.\n");
      src->flags&=~SDL_HWACCEL;
      return -1;
   }

   if ((src->flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY)
   {
       if (this->info.blit_hw_CC!=1)
       {
           src->flags&=~SDL_HWACCEL;
           src->map->hw_blit=NULL;
           return -1;
       }
   }

   if ((src->flags & SDL_SRCALPHA) == SDL_SRCALPHA)
   {
       if (this->info.blit_hw_A!=1)
       {
           src->flags&=~SDL_HWACCEL;
           src->map->hw_blit=NULL;
           return -1;
       }
   }

   src->flags|=SDL_HWACCEL;
   src->map->hw_blit = ph_HWAccelBlit;

   return 1;
}

PgColor_t ph_ExpandColor(_THIS, SDL_Surface* surface, Uint32 color)
{
    Uint32 truecolor;

    /* Photon API accepts true colors only during hw filling operations */
    switch(surface->format->BitsPerPixel)
    {
       case 8:
            {
                if ((surface->format->palette) && (color<=surface->format->palette->ncolors))
                {
                    truecolor=PgRGB(surface->format->palette->colors[color].r,
                                    surface->format->palette->colors[color].g,
                                    surface->format->palette->colors[color].b);
                }
                else
                {
                    SDL_SetError("ph_ExpandColor(): Color out of range for the 8bpp mode !\n");
                    return 0xFFFFFFFFUL;
                }
            }
            break;
       case 15: 
            {
                truecolor = ((color & 0x00007C00UL) << 9) |   /* R */
                            ((color & 0x000003E0UL) << 6) |   /* G */
                            ((color & 0x0000001FUL) << 3) |   /* B */
                            ((color & 0x00007000UL) << 4) |   /* R compensation */
                            ((color & 0x00000380UL) << 1) |   /* G compensation */
                            ((color & 0x0000001CUL) >> 2);    /* B compensation */
            }
            break;
       case 16: 
            {
                truecolor = ((color & 0x0000F800UL) << 8) |   /* R */
                            ((color & 0x000007E0UL) << 5) |   /* G */
                            ((color & 0x0000001FUL) << 3) |   /* B */
                            ((color & 0x0000E000UL) << 3) |   /* R compensation */
                            ((color & 0x00000600UL) >> 1) |   /* G compensation */
                            ((color & 0x0000001CUL) >> 2);    /* B compensation */

            }
            break;
       case 24: 
            {
                truecolor=color & 0x00FFFFFFUL;
            }
            break;
       case 32: 
            {
                truecolor=color;
            }
            break;
       default:
            {
                SDL_SetError("ph_ExpandColor(): Unsupported depth for the hardware operations !\n");
                return 0xFFFFFFFFUL;
            }
    }

    return truecolor;
}

int ph_FillHWRect(_THIS, SDL_Surface* surface, SDL_Rect* rect, Uint32 color)
{
    PgColor_t oldcolor;
    Uint32 truecolor;
    int ydisp=0;

    truecolor=ph_ExpandColor(this, surface, color);
    if (truecolor==0xFFFFFFFFUL)
    {
        return -1;
    }

    oldcolor=PgSetFillColor(truecolor);

    /* 640x400 videomode emulation */
    if (videomode_emulatemode==1)
    {
        ydisp+=40;
    }

    PgDrawIRect(rect->x, rect->y+ydisp, rect->w+rect->x-1, rect->h+rect->y+ydisp-1, Pg_DRAW_FILL);
    PgSetFillColor(oldcolor);
    PgFlush();
    PgWaitHWIdle();

    return 0;
}

int ph_FlipHWSurface(_THIS, SDL_Surface* screen)
{
    PhArea_t farea;

    if ((screen->flags & SDL_FULLSCREEN) == SDL_FULLSCREEN)
    {
        /* flush all drawing ops before blitting */
        PgFlush();
        PgWaitHWIdle();

        farea.pos.x=0;
        farea.pos.y=0;
        farea.size.w=screen->w;
        farea.size.h=screen->h;

        /* emulate 640x400 videomode */
        if (videomode_emulatemode==1)
        {
            farea.pos.y+=40;
        }

        PgContextBlitArea(OCImage.offscreen_context, &farea, OCImage.offscreen_backcontext, &farea);

        /* flush the blitting */
        PgFlush();
        PgWaitHWIdle();
    }
    return 0;
}

int ph_LockHWSurface(_THIS, SDL_Surface* surface)
{

#if 0 /* FIXME */
    int lockresult;

    if (surface->hwdata == NULL)
    {
        return;
    }

    surface->hwdata->lockparam.flags=0;
    surface->hwdata->lockparam.time_out=NULL;
    lockresult=PdLockOffscreen(surface->hwdata->offscreenctx, &surface->hwdata->lockparam);

    switch (lockresult)
    {
       case EOK:
                 break;
       case Pg_OSC_LOCK_DEADLOCK: 
                 SDL_SetError("ph_LockHWSurface(): Deadlock detected !\n");
                 return -1;
       case Pg_OSC_LOCK_INVALID:
                 SDL_SetError("ph_LockHWSurface(): Lock invalid !\n");
                 return -1;
       default:
                 SDL_SetError("ph_LockHWSurface(): Can't lock the surface !\n");
                 return -1;
    }
#endif /* 0 */

    return 0;
}

void ph_UnlockHWSurface(_THIS, SDL_Surface* surface)
{

#if 0 /* FIXME */
    int unlockresult;

    if ((surface == NULL) || (surface->hwdata == NULL))
    {
        return;
    }

    if (PdIsOffscreenLocked(surface->hwdata->offscreenctx)==Pg_OSC_LOCKED)
    {
        unlockresult=PdUnlockOffscreen(surface->hwdata->offscreenctx);
    }
#endif /* 0 */

    return;
}

int ph_HWAccelBlit(SDL_Surface* src, SDL_Rect* srcrect, SDL_Surface* dst, SDL_Rect* dstrect)
{
    SDL_VideoDevice* this=current_video;
    PhArea_t srcarea;
    PhArea_t dstarea;
    int ydisp=0;

    /* 640x400 videomode emulation */
    if (videomode_emulatemode==1)
    {
       ydisp+=40;
    }

    srcarea.pos.x=srcrect->x;
    srcarea.pos.y=srcrect->y;
    srcarea.size.w=srcrect->w;
    srcarea.size.h=srcrect->h;

    dstarea.pos.x=dstrect->x;
    dstarea.pos.y=dstrect->y;
    dstarea.size.w=dstrect->w;
    dstarea.size.h=dstrect->h;

    if (((src == this->screen) || (src->hwdata!=NULL)) && ((dst == this->screen) || (dst->hwdata!=NULL)))
    {
        if ((src->flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY)
        {
            ph_SetHWColorKey(this, src, src->format->colorkey);
            PgChromaOn();
        }

        if (dst == this->screen)
        {
            if (src == this->screen)
            {
                /* blitting from main screen to main screen */
                dstarea.pos.y+=ydisp;
                srcarea.pos.y+=ydisp;
                PgContextBlitArea(OCImage.offscreen_context, &srcarea, OCImage.offscreen_context, &dstarea);
            }
            else
            {
                /* blitting from offscreen to main screen */
                dstarea.pos.y+=ydisp;
                PgContextBlitArea(src->hwdata->offscreenctx, &srcarea, OCImage.offscreen_context, &dstarea);
            }
        }
        else
        {
            if (src == this->screen)
            {
                /* blitting from main screen to offscreen */
                srcarea.pos.y+=ydisp;
                PgContextBlitArea(OCImage.offscreen_context, &srcarea, dst->hwdata->offscreenctx, &dstarea);
            }
            else
            {
                /* blitting offscreen to offscreen */
                PgContextBlitArea(src->hwdata->offscreenctx, &srcarea, dst->hwdata->offscreenctx, &dstarea);
            }
        }

        if ((src->flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY)
        {
            PgChromaOff();
        }
    }
    else
    {
        SDL_SetError("ph_HWAccelBlit(): Source or target surface is not a hardware surface !\n");
        return -1;
    }

    PgFlush();
    PgWaitHWIdle();

    return 0;
}

int ph_SetHWColorKey(_THIS, SDL_Surface *surface, Uint32 key)
{
    if (surface->hwdata!=NULL)
    {
        surface->hwdata->colorkey=ph_ExpandColor(this, surface, key);
        if (surface->hwdata->colorkey==0xFFFFFFFFUL)
        {
            return -1;
        }
    }
    PgSetChroma(surface->hwdata->colorkey, Pg_CHROMA_SRC_MATCH | Pg_CHROMA_NODRAW);

    return 0;
}

int ph_SetHWAlpha(_THIS, SDL_Surface* surface, Uint8 alpha)
{
    return -1;
}

#ifdef HAVE_OPENGL
void ph_OpenGLUpdate(_THIS, int numrects, SDL_Rect* rects)
{
   this->GL_SwapBuffers(this);
   
   return;
}
#endif /* HAVE_OPENGL */

void ph_NormalUpdate(_THIS, int numrects, SDL_Rect *rects)
{
    PhPoint_t ph_pos;
    PhRect_t ph_rect;
    int i;

    for (i=0; i<numrects; ++i) 
    {
    	if (rects[i].w==0) /* Clipped? dunno why but this occurs sometime. */
        { 
            continue;
        }

    	if (rects[i].h==0) /* Clipped? dunno why but this occurs sometime. */
        { 
            continue;
        }

        ph_pos.x = rects[i].x;
        ph_pos.y = rects[i].y;
        ph_rect.ul.x = rects[i].x;
        ph_rect.ul.y = rects[i].y;
        ph_rect.lr.x = rects[i].x + rects[i].w;
        ph_rect.lr.y = rects[i].y + rects[i].h;

        if (PgDrawPhImageRectmx(&ph_pos, SDL_Image, &ph_rect, 0) < 0)
        {
            SDL_SetError("ph_NormalUpdate(): PgDrawPhImageRectmx failed!\n");
            return;
        }
    }

    if (PgFlush() < 0)
    {
    	SDL_SetError("ph_NormalUpdate(): PgFlush() function failed!\n");
    }
}

void ph_OCUpdate(_THIS, int numrects, SDL_Rect *rects)
{
    int i;

    PhPoint_t zero = {0, 0};
    PhArea_t src_rect;
    PhArea_t dest_rect;

    PgSetTranslation(&zero, 0);
    PgSetRegion(PtWidgetRid(window));
    PgSetClipping(0, NULL);

    PgFlush();
    PgWaitHWIdle();

    for (i=0; i<numrects; ++i)
    {
        if (rects[i].w == 0)  /* Clipped? */
        {
            continue;
        }

        if (rects[i].h == 0)  /* Clipped? */
        {
            continue;
        }
        
        src_rect.pos.x=rects[i].x;
        src_rect.pos.y=rects[i].y;
        dest_rect.pos.x=rects[i].x;
        dest_rect.pos.y=rects[i].y;

        src_rect.size.w=rects[i].w;
        src_rect.size.h=rects[i].h;
        dest_rect.size.w=rects[i].w;
        dest_rect.size.h=rects[i].h;
        
        PgContextBlitArea(OCImage.offscreen_context, &src_rect, NULL, &dest_rect);
    }

    if (PgFlush() < 0)
    {
        SDL_SetError("ph_OCUpdate(): PgFlush failed.\n");
    }
}

void ph_OCDCUpdate(_THIS, int numrects, SDL_Rect *rects)
{
    PgWaitHWIdle();

    if (PgFlush() < 0)
    {
        SDL_SetError("ph_OCDCUpdate(): PgFlush failed.\n");
    }
}
