#include "raine.h"
#include "blit.h"
#include "SDL.h"
#include "games.h"
#include "video/tilemod.h"
#include "compat.h"
#include "ingame.h"
#include "profile.h"
#include "gen_conv.h"
#include "video/res.h"
#include "loadpng.h"
#ifndef NEO
#include "bezel.h"
#endif
#include "video/scale2x.h"
#include "video/scale3x.h"
#include "sdl/SDL_gfx/SDL_framerate.h"
#include "sasound.h" // sa_pause_sound
#include "palette.h"
#include <math.h>
#include "cpuid.h"
#include "sdl/display_sdl.h"
#include "emudx.h"

SDL_Surface *sdl_game_bitmap;
extern int disp_screen_x, disp_screen_y;
BITMAP *GameBitmap = NULL; 	// *Full* Bitmap for generating the game screen (eg. 0,0 to 320+32,240+32)
BITMAP *GameViewBitmap; 	// *Viewable* Bitmap for saving (pcx) the game screen (eg. 16,16 to 320+16,240+16)

UINT32 pause_time;
int recording_video = 0,last_video_frame,video_fps;

typedef struct RAINEBITMAP
{
   int xfull;		// Full bitmap width
   int yfull;		// Full bitmap height
   int xtop;		// X Offset of viewable area
   int ytop;		// Y offset of viewable area
   int xview;		// Viewable bitmap width
   int yview;		// Viewable bitmap height
} RAINEBITMAP;

RAINEBITMAP GameScreen;

static int destx, desty, xxx, yyy, xoff2, yoff2;
static BITMAP *pause_buffer;
static int show_fps_mode_store;
static UINT32 pause_timer;

static void my_save_png(char *full_name) {
  save_png(full_name, GameViewBitmap, pal);
}

static void do_save_screen(void)
{
   char full_name[256];
   char file_name[32];
   char *extension = "png";

   raine_cfg.req_save_screen = 0;

   /*

   first try gamename.pcx

   */

   sprintf(file_name, "%s.%s", current_game->main_name,extension);

   sprintf(full_name, "%s%s", dir_cfg.screen_dir, file_name);


   if (recording_video) {
     int current_video_frame = cpu_frame_count*video_fps / fps;

     if (current_video_frame != last_video_frame) {
       sprintf(full_name,"%s%s_%06d.%s",dir_cfg.screen_dir, current_game->main_name,cpu_frame_count,extension);
       my_save_png(full_name);
       last_video_frame = current_video_frame;
     }
   } else {

     /*

     otherwise, find the next gamename_NNN.pcx (or gamenNNN.pcx)

     */

     while( exists(full_name) ){
       if(dir_cfg.last_screenshot_num > 999)
	 return;
       if(dir_cfg.long_file_names)
	 sprintf(file_name, "%s_%03d.%s", current_game->main_name, dir_cfg.last_screenshot_num++,extension);
       else
	 sprintf(file_name, "%.5s%03d.%s", current_game->main_name, dir_cfg.last_screenshot_num++,extension);
       sprintf(full_name, "%s%s", dir_cfg.screen_dir, file_name);
     };
     my_save_png(full_name);
     print_ingame(120, "Screen Saved to %s", file_name);
   }

}

static SDL_Rect area_overlay,area2;

void get_overlay_area(int *x, int *y, int *w, int *h) {
  *x = area_overlay.x;
  *y = area_overlay.y;
  *w = area_overlay.w;
  *h = area_overlay.h;
}

void ReClipScreen(void)
{
   // clip x

  // I need to use 2 sets of xview/yview because destx/desty must be calculated
  // taking into account the scaling factor, but the width and height must be
  // unscaled (it's automatically multiplied during the blit)
  int xview,yview,xview2,yview2;

  xview2 = xview = GameScreen.xview;
  yview2 = yview = GameScreen.yview;

  // bezel_fix_screen_size(&xview,&yview);

  if (use_scale2x == 1) {
    xview2 *= 2;
    yview2 *= 2;
  } else if (use_scale2x == 2) {
    xview2 *= 3;
    yview2 *= 3;
  }

  if (display_cfg.scanlines == 2) { // fullheight
    yview2 *= 2;
  }

  if(xview2 < disp_screen_x){
    destx = (disp_screen_x - xview2)>>1;
  }
  else{
    destx = 0;
  }
  if (xview < disp_screen_x || sdl_overlay) {
    // We can't write outside the game bitmap so we have to use xview
    xxx = xview;
    xoff2 = GameScreen.xtop;
  } else {
    // If the game bitmap is bigger than the screen, then no risk (if stretched)
    if(xview <= disp_screen_x){
      xxx = xview;
      xoff2 = GameScreen.xtop;
    } else {
      xxx = disp_screen_x;
      xoff2 = GameScreen.xtop + ((xview - disp_screen_x)>>1);
    }
  }

  // clip y

  if(yview2 < disp_screen_y){
    desty = (disp_screen_y - yview2)>>1;
  }
  else{
    desty = 0;
  }
#ifndef NEO
  if(yview2 <= disp_screen_y && xview2 <= disp_screen_x && current_game) 
    bezel_fix_screen_coordinates(&destx,&desty,xview2,yview2,disp_screen_x,disp_screen_y);
#endif
  if(yview < disp_screen_y){
    yyy = yview;
    yoff2 = GameScreen.ytop;
  } else {
    if(yview <= disp_screen_y){
      yyy = yview;
      yoff2 = GameScreen.ytop;
    } else {
      yyy = disp_screen_y;
      yoff2 = GameScreen.ytop + ((yview - disp_screen_y)>>1);
    }
  }

  // GameScreen.xview = oldxview;
  // GameScreen.yview = oldyview;
}

void InitDrawPaused(void)
{
   sa_pause_sound();

   show_fps_mode_store=raine_cfg.show_fps_mode;
   raine_cfg.show_fps_mode=0;

   pause_time = 0;

   raine_cfg.req_pause_scroll = 0;

   pause_buffer = create_bitmap(GameScreen.xfull, GameScreen.yfull);
   SDL_Surface *s = get_surface_from_bmp(pause_buffer);
   if (display_cfg.bpp == 8) {
     SDL_SetPalette(s,SDL_LOGPAL,(SDL_Color*)pal,0,256);
   }

   SDL_BlitSurface(
     sdl_game_bitmap, NULL,
     s,NULL);

   pause_timer = read_ingame_timer();
}

void EndDrawPaused(void)
{
   raine_cfg.show_fps_mode = show_fps_mode_store;

   destroy_bitmap(pause_buffer);

   sa_unpause_sound();

   reset_ingame_timer();
   restore_ingame_timer(pause_timer);
}

void DrawPaused(void)
{
   cpu_frame_count++;

   // blit(pause_buffer, GameBitmap, xoff2, yoff2, xoff2, yoff2, xxx, yyy);
   SDL_BlitSurface(
     get_surface_from_bmp(pause_buffer), NULL,
     get_surface_from_bmp(GameBitmap), NULL);

   DrawNormal();		// Overlay text interface, blit to screen

   pause_time++;

   if(display_cfg.limit_speed==1){
	      SDL_framerateDelay(&fpsm);
   }
}

static void SetScreenBitmap(int xfull, int yfull, int xtop, int ytop, int xview, int yview)
{
   const VIDEO_INFO *vid_info;

   vid_info = current_game->video_info;

   display_cfg.rotate = 0;
   display_cfg.flip = 0;

   if(vid_info->flags & VIDEO_ROTATABLE){

      /*

      check if we use the game rotation

      */

      if(!display_cfg.no_rotate)

	 display_cfg.rotate = VIDEO_ROTATE( vid_info->flags );

      /*

      check if we use the game flipping

      */

      if(!display_cfg.no_flip)

	 display_cfg.flip = VIDEO_FLIP( vid_info->flags );

      /*

      add user rotation

      */

      display_cfg.rotate += display_cfg.user_rotate;
      display_cfg.rotate &= 3;

      /*

      add user flipping

      */

      display_cfg.flip ^= display_cfg.user_flip;

   }

   switch(display_cfg.rotate){
   case 0x00:
   case 2:
      GameScreen.xfull=xfull;
      GameScreen.yfull=yfull;
      GameScreen.xtop =xtop;
      GameScreen.ytop =ytop;
      GameScreen.xview=xview;
      GameScreen.yview=yview;
   break;
   case 0x01:
   case 3:
      GameScreen.xfull=yfull;
      GameScreen.yfull=xfull;
      GameScreen.xtop =ytop;
      GameScreen.ytop =xtop;
      GameScreen.xview=yview;
      GameScreen.yview=xview;
   break;
   }

   check_tile_rotation();

   /*

   these modes double the image size (it's simpler to consider
   them as halving the display resolution)

   */


   disp_screen_y = display_cfg.screen_y;
   disp_screen_x = display_cfg.screen_x;

   ReClipScreen();
}

void SetupScreenBitmap(void)
{
   const VIDEO_INFO *vid_info;
   int game_x,game_y,oldbpp;
   double ratio1, ratio2;

   if (!current_game)
     return;

   vid_info = current_game->video_info;

   SetScreenBitmap(
      vid_info->screen_x + vid_info->border_size + vid_info->border_size,
      vid_info->screen_y + vid_info->border_size + vid_info->border_size,
      vid_info->border_size,
      vid_info->border_size,
      vid_info->screen_x,
      vid_info->screen_y
   );

   do {
     oldbpp = display_cfg.bpp;
     if (GameBitmap) {
       DestroyScreenBitmap();
     }
     GameBitmap = sdl_create_bitmap_ex(internal_bpp(display_cfg.bpp), GameScreen.xfull, GameScreen.yfull);
     sdl_game_bitmap = get_surface_from_bmp(GameBitmap);
     // GameBitmap = sdl_create_overlay(GameScreen.xfull, GameScreen.yfull);
     // GameBitmap = create_system_bitmap( GameScreen.xfull, GameScreen.yfull);
     GameViewBitmap = sdl_create_sub_bitmap(GameBitmap, GameScreen.xtop, GameScreen.ytop, GameScreen.xview, GameScreen.yview);
   } while (oldbpp != display_cfg.bpp);

   game_x = xxx;
   game_y = yyy;
   ratio1 = display_cfg.screen_x*1.0/game_x;
   ratio2 = display_cfg.screen_y*1.0/game_y;
   // printf("ratio1 %g ratio2 %g game_x %d game_y %d\n",ratio1,ratio2,game_x,game_y);

   if (sdl_overlay) {
     if (display_cfg.fix_aspect_ratio ) {
       /* Fixing the aspect ratio must be done on the game resolution and not
	* on the screen resolution, otherwise the screen size will change
	* everytime we return to the game */
       if (game_x > game_y) {
	 double ratio = game_y*1.0/game_x;
	 double dif_ratio = fabs(ratio-0.75);
	 if (dif_ratio > 0.001 && dif_ratio < 0.05) {
	   game_y = 0.75*game_x;
	   printf("fixing aspect ratio to 0.75 - new res %d x %d\n",game_x,game_y);
	 }
       } else {
	 double ratio = game_x*1.0/game_x;
	 double dif_ratio = fabs(ratio-0.75);
	 if (dif_ratio > 0.001 && dif_ratio < 0.05) {
	   game_x = 0.75*game_y;
	   printf("fixing aspect ratio to 0.75 - new res %d x %d\n",game_x,game_y);
	 }
       }
     }

     /* Now fix the aspect ratio of the overlay inside the game screen */
     int xxx2,yyy2,destx2,desty2;
     if (ratio1 < ratio2) {
       xxx2 = display_cfg.screen_x;
       yyy2 = ratio1 * game_y;
       destx2 = 0;
       desty2 = (display_cfg.screen_y - yyy2)/2;
     } else {
       yyy2 = display_cfg.screen_y;
       xxx2 = ratio2 * game_x;
       desty2 = 0;
       destx2 = (display_cfg.screen_x - xxx2) /2;
     }
#ifndef NEO
     bezel_fix_screen_size(&xxx2,&yyy2);
     bezel_fix_screen_coordinates(&destx2,&desty2,xxx2,yyy2,display_cfg.screen_x,display_cfg.screen_y);
#endif
     area_overlay.x = destx2;
     area_overlay.y = desty2;
     area_overlay.w = xxx2;
     area_overlay.h = yyy2;
     // printf("area overlay %d %d %d %d with xxx %d yyy %d display %d %d ratio1 %g ratio2 %g\n",destx2,desty2,xxx2,yyy2,xxx,yyy,display_cfg.screen_x,display_cfg.screen_y,ratio1,ratio2);
   } else {
     if (display_cfg.stretch >= 1 && !display_cfg.scanlines) {
       if (ratio1 >= 3.0 && ratio2 >= 3.0 && display_cfg.stretch == 2) {
	 if (use_scale2x != 2) {
	   use_scale2x = 2;
	   return SetupScreenBitmap();
	 }
       } else if (ratio1 >= 2.0 && ratio2 >= 2.0) {
	 if (use_scale2x != 1) {
	   use_scale2x = 1;
	   return SetupScreenBitmap();
	 }
       }
     } else
       use_scale2x = 0;
   }
   area2.x = destx; // x2;
   area2.y = desty; // y2;
   if (use_scale2x == 1) {
     area2.w = xxx*2;
     area2.h = yyy*2;
   } else if (use_scale2x == 2) {
     area2.w = xxx*3;
     area2.h = yyy*3;
   } else {
     area2.w = xxx;
     area2.h = yyy;
   }
#ifndef NEO
   if (!sdl_overlay) {
     int x = area2.x, y = area2.y, w = area2.w, h = area2.h;
     // printf("fixing bezel %d %d %d %d - zone du jeu %d %d %d %d scale %d\n",x,y,w,h,xxx,yyy,destx,desty,use_scale2x);
     bezel_fix_screen_size(&w,&h);
     bezel_fix_screen_coordinates(&x,&y,area2.w,area2.h,display_cfg.screen_x,display_cfg.screen_y);
     area2.x = x; area2.y = y; // area2.w = w; area2.h = h;
     // printf("result bezel %d %d %d %d\n",x,y,w,h);
   }

   display_bezel();
#endif
   clear(GameViewBitmap);

   RefreshBuffers=1;
}

void DestroyScreenBitmap(void)
{
  destroy_bitmap(GameBitmap);
  destroy_bitmap(GameViewBitmap);
  GameBitmap = NULL;
  GameViewBitmap = NULL;
}

extern void *old_draw; // dlg_sound

static void raine_blit_scale2x(BITMAP *src, BITMAP *dest, int s_x, int s_y, int d_x, int d_y, int w, int h)
{
   UINT32 ta,d_w;

   d_w = dest->w;

   ta = 0;
   if (d_y >= dest->h) return;
   h = MIN(h,(dest->h - d_y)/2);

#define SCALE2X(DEPTH,TYPE)							\
   scale2x_##DEPTH##_##TYPE(							\
			  (((UINT##DEPTH *)dest->line[d_y])+d_x),		\
			  (((UINT##DEPTH *)dest->line[d_y+1])+d_x),		\
			  (((UINT##DEPTH *)src->line[s_y])+s_x),		\
			  (((UINT##DEPTH *)src->line[s_y])+s_x),		\
			  (((UINT##DEPTH *)src->line[s_y+1])+s_x),		\
			  (UINT16) w						\
   );										\
										\
   for(ta=1;ta<(UINT32)(h-1);ta++){						\
										\
     scale2x_##DEPTH##_##TYPE(							\
			    (((UINT##DEPTH *)dest->line[ta*2+d_y])+d_x),	\
			    (((UINT##DEPTH *)dest->line[ta*2+d_y+1])+d_x),	\
			    (((UINT##DEPTH *)src->line[ta+s_y-1])+s_x),		\
			    (((UINT##DEPTH *)src->line[ta+s_y])+s_x),		\
			    (((UINT##DEPTH *)src->line[ta+s_y+1])+s_x),		\
			    (UINT16) w						\
			    );							\
										\
   }										\
										\
   ta = (h-1);									\
										\
   scale2x_##DEPTH##_##TYPE(							\
			  (((UINT##DEPTH *)dest->line[ta*2+d_y])+d_x),		\
			  (((UINT##DEPTH *)dest->line[ta*2+d_y+1])+d_x),	\
			  (((UINT##DEPTH *)src->line[ta+s_y-1])+s_x),		\
			  (((UINT##DEPTH *)src->line[ta+s_y])+s_x),		\
			  (((UINT##DEPTH *)src->line[ta+s_y])+s_x),		\
			  (UINT16) w						\
			  );

#ifdef RAINE_DOS
   es = dest->seg;
   asm(" mov %es,_oldes\n mov _es,%es");
#endif
   if (raine_cpu_capabilities & CPU_MMX) {
     switch (display_cfg.bpp) {
     case 8:
       SCALE2X(8,mmx); break;
     case 15:
     case 16:
       SCALE2X(16,mmx);
       break;
     case 32:
       SCALE2X(32,mmx); break;
     }
     __asm__ __volatile__("finit\n"); // restore fpu status after mmx code
   } else {
     switch (display_cfg.bpp) {
     case 8:
       SCALE2X(8,def); break;
     case 15:
     case 16:
       SCALE2X(16,def); break;
     case 32:
       SCALE2X(32,def); break;
     }
   }
#ifdef RAINE_DOS
   asm("mov _oldes,%es");
#endif
}

static void raine_blit_scale3x(BITMAP *src, BITMAP *dest, int s_x, int s_y, int d_x, int d_y, int w, int h)
{
   UINT32 ta,d_w;

   d_w = dest->w;

   ta = 0;
   if (d_y >= dest->h) return;
   h = MIN(h,(dest->h - d_y)/2);

#define SCALE3X(DEPTH,TYPE)							\
   scale3x_##DEPTH##_##TYPE(							\
			  (((UINT##DEPTH *)dest->line[d_y])+d_x),		\
			  (((UINT##DEPTH *)dest->line[d_y+1])+d_x),		\
			  (((UINT##DEPTH *)dest->line[d_y+2])+d_x),		\
			  (((UINT##DEPTH *)src->line[s_y])+s_x),		\
			  (((UINT##DEPTH *)src->line[s_y])+s_x),		\
			  (((UINT##DEPTH *)src->line[s_y+1])+s_x),		\
			  (UINT16) w						\
   );										\
										\
   for(ta=1;ta<(UINT32)(h-2);ta++){						\
										\
     scale3x_##DEPTH##_##TYPE(							\
			    (((UINT##DEPTH *)dest->line[ta*3+d_y])+d_x),	\
			    (((UINT##DEPTH *)dest->line[ta*3+d_y+1])+d_x),	\
			    (((UINT##DEPTH *)dest->line[ta*3+d_y+2])+d_x),	\
			    (((UINT##DEPTH *)src->line[ta+s_y-1])+s_x),		\
			    (((UINT##DEPTH *)src->line[ta+s_y])+s_x),		\
			    (((UINT##DEPTH *)src->line[ta+s_y+1])+s_x),		\
			    (UINT16) w						\
			    );							\
										\
   }										\
										\
   ta = (h-2);									\
										\
   scale3x_##DEPTH##_##TYPE(							\
			  (((UINT##DEPTH *)dest->line[ta*3+d_y])+d_x),		\
			  (((UINT##DEPTH *)dest->line[ta*3+d_y+1])+d_x),	\
			  (((UINT##DEPTH *)dest->line[ta*3+d_y+1])+d_x),	\
			  (((UINT##DEPTH *)src->line[ta+s_y-1])+s_x),		\
			  (((UINT##DEPTH *)src->line[ta+s_y])+s_x),		\
			  (((UINT##DEPTH *)src->line[ta+s_y+1])+s_x),		\
			  (UINT16) w						\
			  );

#ifdef RAINE_DOS
   es = dest->seg;
   asm(" mov %es,_oldes\n mov _es,%es");
#endif
/*
 * No mmx version for scale3x !
   if (raine_cpu_capabilities & CPU_MMX) {
     switch (display_cfg.bpp) {
     case 8:
       SCALE3X(8,mmx); break;
     case 15:
     case 16:
       SCALE3X(16,mmx);
       break;
     case 32:
       SCALE3X(32,mmx); break;
     }
     __asm__ __volatile__("finit\n"); // restore fpu status after mmx code
   } else {
     */
     switch (display_cfg.bpp) {
     case 8:
       SCALE3X(8,def); break;
     case 15:
     case 16:
       SCALE3X(16,def); break;
     case 32:
       SCALE3X(32,def); break;
     }
/*   } */
#ifdef RAINE_DOS
   asm("mov _oldes,%es");
#endif
}
#if 0
static void RGBtoYUV(Uint8 *rgb, int *yuv, int monochrome, int luminance)
{
    if (monochrome)
    {
#if 1 /* these are the two formulas that I found on the FourCC site... */
        yuv[0] = 0.299*rgb[2] + 0.587*rgb[1] + 0.114*rgb[0];
        yuv[1] = 128;
        yuv[2] = 128;
#else
        yuv[0] = (0.257 * rgb[0]) + (0.504 * rgb[1]) + (0.098 * rgb[2]) + 16;
        yuv[1] = 128;
        yuv[2] = 128;
#endif
    }
    else
    {
#if 1 /* these are the two formulas that I found on the FourCC site... */
        yuv[0] = 0.299*rgb[2] + 0.587*rgb[1] + 0.114*rgb[0];
        yuv[1] = (rgb[0]-yuv[0])*0.565 + 128;
        yuv[2] = (rgb[2]-yuv[0])*0.713 + 128;
#else
        yuv[0] = (0.257 * rgb[0]) + (0.504 * rgb[1]) + (0.098 * rgb[2]) + 16;
        yuv[1] = 128 - (0.148 * rgb[0]) - (0.291 * rgb[1]) + (0.439 * rgb[2]);
        yuv[2] = 128 + (0.439 * rgb[0]) - (0.368 * rgb[1]) - (0.071 * rgb[2]);
#endif
    }

    if (luminance!=100)
    {
        yuv[0]=yuv[0]*luminance/100;
        if (yuv[0]>255)
            yuv[0]=255;
    }
}

static void ConvertRGBtoYV12(SDL_Surface *s, SDL_Overlay *o, int monochrome, int luminance, int border)
{
	int x,y;
	int yuv[3];
	Uint8 *p,*op[3];

	/* Convert */
	for(y=0; y<s->h && y<o->h; y++)
	{
		p=((Uint8 *) s->pixels)+s->pitch*(y+border)+border*s->format->BytesPerPixel;
		op[0]=o->pixels[0]+o->pitches[0]*y;
		op[1]=o->pixels[1]+o->pitches[1]*(y/2);
		op[2]=o->pixels[2]+o->pitches[2]*(y/2);
		for(x=0; x<s->w && x<o->w; x++)
		{
			RGBtoYUV(p, yuv, monochrome, luminance);
			*(op[0]++)=yuv[0];
			if(x%2==0 && y%2==0)
			{
				*(op[1]++)=yuv[2];
				*(op[2]++)=yuv[1];
			}
			p+=s->format->BytesPerPixel;
		}
	}
}
#endif

static void raine_fast_blit(BITMAP *source, BITMAP *dest, UINT32 x1, UINT32 y1, UINT32 x2, UINT32 y2, UINT32 w, UINT32 h)
{
  SDL_Rect area1;
  int ret;

  if (sdl_overlay) {
    // convert source to yuy2 format in the overlay
    unsigned char *src,*dest;
    SDL_LockYUVOverlay(sdl_overlay);
    if (sdl_overlay->format == SDL_YUY2_OVERLAY) {
      src = GameBitmap->line[GameScreen.ytop]+x1*2;
      dest = sdl_overlay->pixels[0]; // +y*sdl_overlay->pitches[0];
      /* Principle : when using an overlay, the color format is forced to
       * overlay_format in compat.c so that we are able to use the rgb565_
       * conversion functions everywhere. 
       * Of course if you switch to normal blits after having loaded an emudx
       * game with an overlay enabled, don't complain if the red and blue get
       * inverted. This is an extreme case, and it would be bothersome to work
       * around it, so just don't do that ! */
	mmx_rgb565_yuyv(dest, src, w, sdl_overlay->h, sdl_overlay->pitches[0], sdl_game_bitmap->pitch);
    } else if (sdl_overlay->format == SDL_YV12_OVERLAY) {
	src = GameBitmap->line[GameScreen.ytop /* +y */ ]+x1*2;
	mmx_rgb565_yuv420(sdl_overlay->pixels[0], // +y*sdl_overlay->pitches[0],
	    sdl_overlay->pixels[2], // +(y/2)*sdl_overlay->pitches[2],
	    sdl_overlay->pixels[1], // +(y/2)*sdl_overlay->pitches[1],
	    src, w, sdl_overlay->h, sdl_overlay->pitches[0], sdl_overlay->pitches[1], sdl_game_bitmap->pitch);
    }
    SDL_UnlockYUVOverlay(sdl_overlay);

    SDL_DisplayYUVOverlay(sdl_overlay,&area_overlay);
    /* The SDL_DisplayYUVOverlay is a magical function :
     *  - it stretches the overlay to the desired size without bothering the cpu
     *  - it's automatically updated on screen (no SDL_UpdateRect to call)
     *  - it automatically synchronizes on the monitor refresh rate (verified
     *  by playing without limiting the framerate).
     *  So no SDL_Flip nor SDL_UpdateRect required here */
    return;
  } else { // no overlay, classical blit

    if (use_scale2x == 1) {
      int locked = lock_surface(sdl_screen);
      raine_blit_scale2x(source, dest, x1, y1, x2, y2, w, h);
      if (locked) SDL_UnlockSurface(sdl_screen);
    } else if (use_scale2x == 2) {
      int locked = lock_surface(sdl_screen);
      raine_blit_scale3x(source, dest, x1, y1, x2, y2, w, h); 
      if (locked) SDL_UnlockSurface(sdl_screen);
    } else {
	SDL_Surface *s1 = get_surface_from_bmp(source),
		    *s2 = get_surface_from_bmp(dest);
	area1.x = x1;
	area1.y = y1;
	area1.w = w;
	area1.h = h;

	/* Notice : SDL_BlitSurface takes care of the palette handling,
	 * which forces me to update the logical palette of the game
	 * screen and the game bitmap at the same time before the blit.
	 * Maybe it would be faster to update only the screen palette and
	 * then use some memcpys here instead of SDL_BlitSurface for 8bpp.
	 * But since the vast majority of games now run in 16bpp, it's probably
	 * not worth the trouble */
	ret = SDL_BlitSurface(s1, &area1, s2, &area2);
	if (ret) printf("got %d\n",ret);
    }
  }
  if ( sdl_screen->flags & SDL_DOUBLEBUF ) {
    SDL_Flip(sdl_screen);
  } else {
    SDL_UpdateRects(sdl_screen,1,&area2);
  }
}

static void update_screen_palette(PALETTE palette)
{
  int ret;
/*  PALETTE pal2;

  memcpy(pal2,pal,sizeof(PALETTE));
  for (ret=0; ret<256; ret++) {
    pal2[ret].r <<= 2;
    pal2[ret].g <<= 2;
    pal2[ret].b <<= 2;
  } */

  if (display_cfg.bpp != 8) return;
#ifdef RDTSC_PROFILE
   if(raine_cfg.show_fps_mode>2){
      ProfileStop(PRO_BLIT);
      ProfileStart(PRO_PAL);
   }
#endif

   // ret = SDL_SetColors(sdl_game_bitmap,(SDL_Color*)pal2,0,256);
   // ret = SDL_SetColors(sdl_screen,(SDL_Color*)pal2,0,256);
   ret = SDL_SetPalette(sdl_game_bitmap,SDL_LOGPAL,(SDL_Color*)pal,0,256);
   ret = SDL_SetPalette(sdl_screen,SDL_LOGPAL|SDL_PHYSPAL,(SDL_Color*)pal,0,256);

#ifdef RDTSC_PROFILE
   if(raine_cfg.show_fps_mode>2){
      ProfileStop(PRO_PAL);
      ProfileStart(PRO_BLIT);
   }
#endif
}

void DrawNormal(void)
{
  int i;
  SDL_Rect s1,s2;

   /*

   save screenshots now (before we overwrite the image)

   */
  if (old_draw) return;

   /*

   scroll game screen

   */

   if (raine_cfg.req_pause_scroll) {
     if(raine_cfg.req_pause_scroll & 1)
        if(yoff2 > GameScreen.ytop)
  	  yoff2--;

     if(raine_cfg.req_pause_scroll & 2)
        if((yoff2 + disp_screen_y) < (GameScreen.ytop + GameScreen.yview))
	  yoff2++;

     if(raine_cfg.req_pause_scroll & 4)
        if(xoff2 > GameScreen.xtop)
	  xoff2--;

     if(raine_cfg.req_pause_scroll & 8)
        if((xoff2 + disp_screen_x) < (GameScreen.xtop + GameScreen.xview))
	  xoff2++;

     raine_cfg.req_pause_scroll = 0;
   }

   if(raine_cfg.req_save_screen == 1)
      do_save_screen();

   /*

   draw the ingame 'interface' (message list, fps)

   */

   overlay_ingame_interface();

   /*

   update host (pc) palette

   */

   update_screen_palette(pal);

   /*

   blit image to host (pc) videoram

   */

   switch(display_cfg.scanlines){
     // scanlines must be able to work with other video effects, at least
     // for double width. But there were too many modes which worked badly
     // with the previous hack (modifying directly the line pointers in the
     // screen bitmap). This way should be a little slower but much more
     // reliable.

     case 0:
       raine_fast_blit(GameBitmap, screen, xoff2, yoff2, destx, desty, xxx, yyy);
       break;
     case 1:
       // half height
       s1.x = xoff2;
       s1.w = xxx;
       s1.h = 1;
       s2.x = destx;
       s2.w = s1.w;
       s2.h = s2.h;
       for (i=0; i<yyy; i+=2) {
         s1.y = yoff2+i;
	 s2.y = desty+i;
	 SDL_BlitSurface(sdl_game_bitmap, &s1, sdl_screen, &s2);
       }
       if ( sdl_screen->flags & SDL_DOUBLEBUF ) {
	 SDL_Flip(sdl_screen);
       } else {
	 SDL_UpdateRects(sdl_screen,1,&area2);
       }
       break;
     case 2: {
	       int end = MIN(2*yyy,screen->h-1)/2;
	       // full height scanlines
	       s1.x = xoff2;
	       s1.w = xxx;
	       s1.h = 1;
	       s2.x = destx;
	       s2.w = s1.w;
	       s2.h = s2.h;
	       for (i=0; i<end; i++) {
		 s1.y = yoff2+i;
		 s2.y = desty+i*2;
		 SDL_BlitSurface(sdl_game_bitmap, &s1, sdl_screen, &s2);
	       }
	       s2.y = desty;
	       s2.h = end*2;
	       if ( sdl_screen->flags & SDL_DOUBLEBUF ) {
		 SDL_Flip(sdl_screen);
	       } else {
		 SDL_UpdateRects(sdl_screen,1,&s2);
	       }
	       break;
	     }
   }
   RefreshBuffers = 0;
}

void clear_game_screen(int pen)
{
  int x,y,len;
  switch(display_cfg.bpp) {
  case 8:
    len = GameViewBitmap->w;
    for (y=0; y<GameViewBitmap->h; y++)
      memset(GameViewBitmap->line[y],pen,len);
    break;
  case 15:
  case 16:
    len = GameViewBitmap->w*2;
    pen = (pen & 0xffff) | (pen<<16);
    for (y=0; y<GameViewBitmap->h; y++) {
      unsigned char *src = GameViewBitmap->line[y];
      for (x=0; x<len; x+= 4) {
	WriteLong(&src[x],pen);
      }
    }
    break;
  case 32:
    len = GameViewBitmap->w*4;
    for (y=0; y<GameViewBitmap->h; y++) {
      unsigned char *src = GameViewBitmap->line[y];
      for (x=0; x<len; x+= 4) {
	WriteLong(&src[x],pen);
      }
    }
    break;
  }
}

void get_screen_coordinates(int *Xoff2,int *Yoff2, int *Destx, int *Desty, int *Xxx, int *Yyy){
  *Xoff2 = xoff2;
  *Yoff2 = yoff2;
  *Xxx = xxx;
  *Yyy = yyy;
  *Destx = destx;
  *Desty = desty;
}

