/*         ______   ___    ___ 
 *        /\  _  \ /\_ \  /\_ \ 
 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
 *                                           /\____/
 *                                           \_/__/
 *
 *      QNX Photon acceleration.
 *
 *      By Eric Botcazou.
 *
 *      Based on src/win/wddaccel by Stephan Schimanski and al.
 *
 *      See readme.txt for copyright information.
 */


#include "allegro.h"
#include "allegro/internal/aintern.h"
#include "allegro/platform/aintqnx.h"

#ifndef ALLEGRO_QNX
   #error Something is wrong with the makefile
#endif


/* state variables */
static PgColor_t chroma_color;
static int chroma_on = FALSE;

/* software version pointers */
static void (*_orig_hline) (BITMAP * bmp, int x1, int y, int x2, int color);
static void (*_orig_vline) (BITMAP * bmp, int x, int y1, int y2, int color);
static void (*_orig_rectfill) (BITMAP * bmp, int x1, int y1, int x2, int y2, int color);
static void (*_orig_draw_sprite) (BITMAP * bmp, BITMAP * sprite, int x, int y);
static void (*_orig_masked_blit) (BITMAP * source, BITMAP * dest, int source_x, int source_y, int dest_x, int dest_y, int width, int height);



/* photon_blit_to_self:
 *  Accelerated vram -> vram blitting routine.
 */
static void photon_blit_to_self(BITMAP *source, BITMAP *dest, int source_x, int source_y, int dest_x, int dest_y, int width, int height)
{
   struct Ph_rect source_rect = {
      { source_x + source->x_ofs,
        source_y + source->y_ofs },
      { source_x + source->x_ofs + width - 1,
        source_y + source->y_ofs + height - 1 }
   };
   
   struct Ph_rect dest_rect = {
      { dest_x + dest->x_ofs,
        dest_y + dest->y_ofs },
      { dest_x + dest->x_ofs + width - 1,
        dest_y + dest->y_ofs + height - 1 }
   };

   struct BITMAP *source_parent, *dest_parent;

   /* find parents */
   source_parent = source;
   while (source_parent->id & BMP_ID_SUB)
      source_parent = (BITMAP *)source_parent->extra;
   
   dest_parent = dest;
   while (dest_parent->id & BMP_ID_SUB)
      dest_parent = (BITMAP *)dest_parent->extra;

   PgContextBlit(BMP_EXTRA(source_parent)->context, &source_rect,
                 BMP_EXTRA(dest_parent)->context, &dest_rect);
   
   /* only for windowed mode */
   if (dest_parent == pseudo_screen)
      ph_update_window(&dest_rect);
}



/* ddraw_masked_blit:
 *  Accelerated masked blitting routine.
 */
static void photon_masked_blit(BITMAP *source, BITMAP *dest, int source_x, int source_y, int dest_x, int dest_y, int width, int height)
{
   struct Ph_rect source_rect = {
      { source_x + source->x_ofs,
        source_y + source->y_ofs },
      { source_x + source->x_ofs + width - 1,
        source_y + source->y_ofs + height - 1 }
   };
   
   struct Ph_rect dest_rect = {
      { dest_x + dest->x_ofs,
        dest_y + dest->y_ofs },
      { dest_x + dest->x_ofs + width - 1,
        dest_y + dest->y_ofs + height - 1 }
   };
   
   struct BITMAP *dest_parent, *source_parent;

   if (is_video_bitmap(source)) {
      if (!chroma_on) {
         PgChromaOn();
         chroma_on = TRUE;
      }
      
      /* find parents */
      source_parent = source;
      while (source_parent->id & BMP_ID_SUB)
         source_parent = (BITMAP *)source_parent->extra;
         
      dest_parent = dest;
      while (dest_parent->id & BMP_ID_SUB)
         dest_parent = (BITMAP *)dest_parent->extra;

      PgContextBlit(BMP_EXTRA(source_parent)->context, &source_rect,
                    BMP_EXTRA(dest_parent)->context, &dest_rect);

      /* only for windowed mode */
      if (dest_parent == pseudo_screen)
         ph_update_window(&dest_rect);
   }
   else {
      /* have to use the original software version */
      _orig_masked_blit(source, dest, source_x, source_y, dest_x, dest_y, width, height);
   }
}



/* photon_draw_sprite:
 *  Accelerated sprite drawing routine.
 */
static void photon_draw_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y)
{
   int sx, sy, w, h;

   if (is_video_bitmap(sprite)) {
      sx = sprite->x_ofs;
      sy = sprite->y_ofs;
      w = sprite->w;
      h = sprite->h;

      if (bmp->clip) {
	 if (x < bmp->cl) {
	    sx += bmp->cl - x;
	    w -= bmp->cl - x;
	    x = bmp->cl;
	 }

	 if (y < bmp->ct) {
	    sy += bmp->ct - y;
	    h -= bmp->ct - y;
	    y = bmp->ct;
	 }

	 if (x + w > bmp->cr)
	    w = bmp->cr - x;

	 if (w <= 0)
	    return;

	 if (y + h > bmp->cb)
	    h = bmp->cb - y;

	 if (h <= 0)
	    return;
      }

      photon_masked_blit(sprite, bmp, sx, sy, x, y, w, h);
   }
   else {
      /* have to use the original software version */
      _orig_draw_sprite(bmp, sprite, x, y);
   }
}



/* photon_clear_to_color:
 *  Accelerated screen clear routine.
 */
static void photon_clear_to_color(BITMAP *bmp, int color)
{
   struct Ph_rect dest_rect = {
      { bmp->cl + bmp->x_ofs,
        bmp->ct + bmp->y_ofs },
      { bmp->x_ofs + bmp->cr,
        bmp->y_ofs + bmp->cb }
   };

   struct BITMAP *parent;

   /* find parent */
   parent = bmp;
   while (parent->id & BMP_ID_SUB)
      parent = (BITMAP *)parent->extra;

   /* set fill color */
   /* if (bmp->vtable->color_depth == 8)
      PgSetFillColor(color);
   else */
      PgSetFillColor(PgRGB(getr(color), getg(color), getb(color)));
      
   PhDCSetCurrent(BMP_EXTRA(parent)->context);
   PgDrawRect(&dest_rect, Pg_DRAW_FILL);

   if (parent == pseudo_screen)
      ph_update_window(&dest_rect);
   else
      PgFlush();
}



/* photon_rectfill:
 *  Accelerated rectangle fill routine.
 */
static void photon_rectfill(BITMAP *bmp, int x1, int y1, int x2, int y2, int color)
{
   struct Ph_rect dest_rect;
   struct BITMAP *parent;

   if (_drawing_mode != DRAW_MODE_SOLID) {
      _orig_rectfill(bmp, x1, y1, x2, y2, color);
      return;
   }

   if (x2 < x1) {
      int tmp = x1;
      x1 = x2;
      x2 = tmp;
   }

   if (y2 < y1) {
      int tmp = y1;
      y1 = y2;
      y2 = tmp;
   }

   if (bmp->clip) {
      if (x1 < bmp->cl)
	 x1 = bmp->cl;

      if (x2 >= bmp->cr)
	 x2 = bmp->cr-1;

      if (x2 < x1)
	 return;

      if (y1 < bmp->ct)
	 y1 = bmp->ct;

      if (y2 >= bmp->cb)
	 y2 = bmp->cb-1;

      if (y2 < y1)
	 return;
   }

   dest_rect.ul.x = x1 + bmp->x_ofs;
   dest_rect.ul.y = y1 + bmp->y_ofs;
   dest_rect.lr.x = x2 + bmp->x_ofs;
   dest_rect.lr.y = y2 + bmp->y_ofs;

   /* find parent */
   parent = bmp;
   while (parent->id & BMP_ID_SUB)
      parent = (BITMAP *)parent->extra;

   /* set fill color */
   /* if (bmp->vtable->color_depth == 8)
      PgSetFillColor(color);
   else */
      PgSetFillColor(PgRGB(getr(color), getg(color), getb(color)));
      
   PhDCSetCurrent(BMP_EXTRA(parent)->context);
   PgDrawRect(&dest_rect, Pg_DRAW_FILL);

   if (parent == pseudo_screen)
      ph_update_window(&dest_rect);
   else
      PgFlush();
}



/* ddraw_hline:
 *  Accelerated scanline fill routine.
 */
static void ddraw_hline(BITMAP *bmp, int x1, int y, int x2, int color)
{
   struct Ph_rect dest_rect;
   struct BITMAP *parent;

   if (_drawing_mode != DRAW_MODE_SOLID) {
      _orig_hline(bmp, x1, y, x2, color);
      return;
   }

   if (x1 > x2) {
      int tmp = x1;
      x1 = x2;
      x2 = tmp;
   }

   if (bmp->clip) {
      if ((y < bmp->ct) || (y >= bmp->cb))
	 return;

      if (x1 < bmp->cl)
	 x1 = bmp->cl;

      if (x2 >= bmp->cr)
	 x2 = bmp->cr-1;

      if (x2 < x1)
	 return;
   }
   
   dest_rect.ul.x = x1 + bmp->x_ofs;
   dest_rect.ul.y = y + bmp->y_ofs;
   dest_rect.lr.x = x2 + bmp->x_ofs;
   dest_rect.lr.y = y + bmp->y_ofs;
   
   /* find parent */
   parent = bmp;
   while (parent->id & BMP_ID_SUB)
      parent = (BITMAP *)parent->extra;

   PhDCSetCurrent(BMP_EXTRA(parent)->context);
   PgDrawLine(&dest_rect.ul, &dest_rect.lr);

   if (parent == pseudo_screen)
      ph_update_window(&dest_rect);
   else
      PgFlush();
}


/* ddraw_vline:
 *  Accelerated vline routine.
 */
static void ddraw_vline(BITMAP *bmp, int x, int y1, int y2, int color)
{
   struct Ph_rect dest_rect;
   struct BITMAP *parent;

   if (_drawing_mode != DRAW_MODE_SOLID) {
      _orig_vline(bmp, x, y1, y2, color);
      return;
   }

   if (y1 > y2) {
      int tmp = y1;
      y1 = y2;
      y2 = tmp;
   }

   if (bmp->clip) {
      if ((x < bmp->cl) || (x >= bmp->cr))
	 return;

      if (y1 < bmp->ct)
	 y1 = bmp->ct;

      if (y2 >= bmp->cb)
	 y2 = bmp->cb-1;

      if (y2 < y1)
	 return;
   }

   dest_rect.ul.x = x + bmp->x_ofs;
   dest_rect.ul.y = y1 + bmp->y_ofs;
   dest_rect.lr.x = x + bmp->x_ofs;
   dest_rect.lr.y = y2 + bmp->y_ofs;

   /* find parent */
   parent = bmp;
   while (parent->id & BMP_ID_SUB)
      parent = (BITMAP *)parent->extra;

   PhDCSetCurrent(BMP_EXTRA(parent)->context);
   PgDrawLine(&dest_rect.ul, &dest_rect.lr);

   if (parent == pseudo_screen)
      ph_update_window(&dest_rect);
   else
      PgFlush();
}



/* enable_acceleration:
 *  Checks graphic driver for capabilities to accelerate Allegro.
 */
void enable_acceleration(GFX_DRIVER * drv)
{
   /* safe pointer to software versions */
   _orig_hline = _screen_vtable.hline;
   _orig_vline = _screen_vtable.vline;
   _orig_rectfill = _screen_vtable.rectfill;
   _orig_draw_sprite = _screen_vtable.draw_sprite;
   _orig_masked_blit = _screen_vtable.masked_blit;

   /* accelerated video to video blits? */
   if (ph_gfx_mode_info.mode_capabilities2 & PgVM_MODE_CAP2_BITBLT) {
      _screen_vtable.blit_to_self = photon_blit_to_self;
      
      /* overlapping blits seem not to be supported */
   #if 0
      _screen_vtable.blit_to_self_forward = photon_blit_to_self;
      _screen_vtable.blit_to_self_backward = photon_blit_to_self;
   #endif

      gfx_capabilities |= GFX_HW_VRAM_BLIT;
   }

   /* accelerated color fills? */
   if (ph_gfx_mode_info.mode_capabilities2 & PgVM_MODE_CAP2_RECTANGLE) {
      _screen_vtable.clear_to_color = photon_clear_to_color;
      _screen_vtable.rectfill = photon_rectfill;

      gfx_capabilities |= GFX_HW_FILL;
   }
   
   /* accelerated lines? */
   if (ph_gfx_mode_info.mode_capabilities2 & PgVM_MODE_CAP2_LINES) {
      _screen_vtable.hline = ddraw_hline;
      _screen_vtable.vline = ddraw_vline;

      gfx_capabilities |= GFX_HW_HLINE;
   }
   
   /* accelerated color key blits? */
   if (ph_gfx_mode_info.mode_capabilities2 & PgVM_MODE_CAP2_CHROMA) {
      _screen_vtable.masked_blit = photon_masked_blit;
      _screen_vtable.draw_sprite = photon_draw_sprite;

      if (_screen_vtable.color_depth == 8)
	 _screen_vtable.draw_256_sprite = photon_draw_sprite;

      gfx_capabilities |= GFX_HW_VRAM_BLIT_MASKED;
   }
}



/* enable_triple_buffering:
 *  Checks graphic driver for triple buffering capability.
 */
void enable_triple_buffering(GFX_DRIVER *drv)
{
   if (ph_gfx_mode_info.mode_capabilities1 & PgVM_MODE_CAP1_TRIPLE_BUFFER) {
      /* drv->poll_scroll = qnx_ph_poll_scroll; */
      gfx_capabilities |= GFX_CAN_TRIPLE_BUFFER;
   }
}
