/* Bezel support, from the mame artwork.
   I just want very basic support, the idea is to use this to fill the ugly black borders
   in vertical games, I don't care about transparency or anything displayed over the
   the game screen area...
   I handle the alphafile anyway because mame uses it to hide unneeded pixels in some
   artwork files... */

#include "unzip.h"
#include "raine.h"
#include "games.h"
#include "png/loadpng.h"
#include "video/res.h"
#include "blit.h"
#include "files.h"

// the people taking care of libc were really crazy when they decided that you had
// to define this to get the definition of round when including math.h...
#define __USE_ISOC99 1
#include <math.h>

#ifdef RAINE_DOS
// djgpp seems to have specificities for rounding...
// why on earth non one implements cleanly such basic function ???
#define round rint
#endif

static BITMAP *bezel_bitmap;
static PALETTE bezel_pal;
static float x,y,w,h;
static int bezel_width, bezel_height; // Size for the unscaled game screen

#define BUFFER_SIZE 1024
static  char buffer[BUFFER_SIZE];

static char* get_next_line() {
  static  char *next_line, *next_eol;
  if (!next_line) {
    next_line = buffer;
    next_eol = buffer;
  }
  if (next_eol) {
    next_line = next_eol+1;
    next_eol = strchr(next_line,'\n');
    if (next_eol) {
      *next_eol = 0;
      if (*(next_eol-1) == '\r') // stupid windows with its cr lf...
	*(next_eol-1) = 0;
    }
    while (*next_line == ' ' || *next_line == '\t')
      next_line++;
    return next_line;
  }
  next_line = NULL;
  return NULL;
}

static char *get_arg(char *s) {
  char *d = strchr(s,'=');
  if (d) {
    d++;
    while (*d == ' ' || *d == '\t')
      d++;
  }
  return d;
}

void clear_bezel() {
  if (bezel_bitmap) {
    destroy_bitmap(bezel_bitmap);
    bezel_bitmap = NULL;
    x = y = w = h = bezel_width = bezel_height = 0;
    print_debug("bezel destroyed\n");
  }
}

void load_bezel() {
  /* zip support only */
  unzFile uf;
  int err;
  char name[256],game_name[32];
  char *s,file[30],*arg;
  char alphafile[30];
  BITMAP *alpha_bitmap;

  if (display_cfg.bpp == 8)
    return;

  alphafile[0] = 0;

  clear_bezel();
  sprintf(name,"artwork/%s.zip",current_game->main_name);
  strcpy(game_name,current_game->main_name);

   uf = unzOpen(get_shared(name));

   if (!uf) {
     /* Try with the parents... */
     const DIR_INFO *head = current_game->dir_list;
     char *dir;
     for (; head; head++) {
       dir = head[0].maindir;

       if( dir ){

	 if( IS_ROMOF(dir) ){

	   GAME_MAIN *game_romof;

	   game_romof = find_game(dir+1);

	   strcpy(game_name,game_romof->main_name);
	   sprintf(name,"artwork/%s.zip",game_name);
	   uf = unzOpen(get_shared(name));
	   if (uf) break;
	 }
       } else
	 break;
     }
   }

   if(!uf) {			// Fail: Unable to find/open zipfile
     print_debug("bezel: not found %s\n",name);
     return;
   }
   sprintf(name,"%s.art",game_name);
   err = unzLocateFile(uf,name,2);
   if(err!=UNZ_OK){
     print_debug("bezel: not found %s\n",name);
     unzClose(uf);
     return;		// Fail: File not in zip
   }

   err = unzOpenCurrentFile(uf);
   if(err!=UNZ_OK){
      print_debug("unzOpenCurrentFile(): Error #%d\n",err);
      unzCloseCurrentFile(uf);	// Is this needed when open failed?
      unzClose(uf);
      return;			// Fail: Something internal
   }

   err = unzReadCurrentFile(uf,buffer,BUFFER_SIZE);
   if(err<0){
      print_debug("unzReadCurrentFile(): Error #%d\n",err);
      unzCloseCurrentFile(uf);
      unzClose(uf);
      return;			// Fail: Something internal
   }

   do {
     s = get_next_line();
     if (!s) {
       print_debug("bezel: eof while looking for bezel:\n");
       unzCloseCurrentFile(uf);
       unzClose(uf);
       return;			// Fail: Something internal
     }
   } while (strncasecmp(s,"bezel:",6) && strncasecmp(s,"backdrop:",9));

   *file = 0;
   do {
     s = get_next_line();
     if (s) {
       arg = get_arg(s);
       if (!strncasecmp(s,"file",4))
	 strcpy(file,arg);
       else if (!strncasecmp(s,"position",8))
	 sscanf(arg,"%f,%f,%f,%f",&x,&y,&w,&h);
       else if (!strncasecmp(s,"alphafile",9))
	 strcpy(alphafile,arg);
     }
   } while (s);

   unzCloseCurrentFile(uf);

   if (*file == 0) {
     print_debug("bezel: didn't find file argument\n");
     unzClose(uf);
     return;			// Fail: Something internal
   }

   err = unzLocateFile(uf,file,2);
   if(err!=UNZ_OK){
     print_debug("bezel: not found %s.\n",file);
     unzClose(uf);
     return;		// Fail: File not in zip
   }

   err = unzOpenCurrentFile(uf);
   if(err!=UNZ_OK){
      print_debug("unzOpenCurrentFile(): Error #%d\n",err);
      unzCloseCurrentFile(uf);	// Is this needed when open failed?
      unzClose(uf);
      return;			// Fail: Something internal
   }

   bezel_bitmap = load_png_from_zip(uf,bezel_pal);

   if (alphafile[0]) {
     err = unzLocateFile(uf,alphafile,2);
     if(err!=UNZ_OK){
       print_debug("bezel: not found %s.\n",alphafile);
       unzClose(uf);
       return;		// Fail: Alphafile not in zip
     }

     err = unzOpenCurrentFile(uf);
     if(err!=UNZ_OK){
       print_debug("unzOpenCurrentFile(): Error #%d\n",err);
       unzCloseCurrentFile(uf);	// Is this needed when open failed?
       unzClose(uf);
       return;			// Fail: Something internal
     }

     alpha_bitmap = load_png_from_zip(uf,bezel_pal);
     if (alpha_bitmap) {
       if (alpha_bitmap->w != bezel_bitmap->w || alpha_bitmap->h != bezel_bitmap->h) {
	 print_debug("bezel: alphafile dimensions != bezel dimensions, ignoring alphafile\n");
       } else {
	 /* I would have prefered to simply ignore the alphafile parameter since we can't
	    render the alpha channel in an efficient way without a 3d rendering driver,
	    and raine has no 3d rendering for now.
	    But some bezels do not have very precise coordinates for the game areas, and
	    hide the "bad pixels" with an alpha channel at 100%, so I must do something
	    for these broken artworks (which happen to be official ones sometimes !).
	    Solution for now : paint every pixel in the bezel in black if it's transparent*/
	 int x,y;
	 if (display_cfg.bpp == 32) {
	   UINT32 *pixel;
	   for (y=0; y<bezel_bitmap->h; y++) {
	     pixel = (UINT32*)alpha_bitmap->line[y];
	     for (x=0; x<bezel_bitmap->w; x++) {
	       if (!*pixel++) { // black pixel -> transp
		 WriteLong(((UINT32*)bezel_bitmap->line[y])+x,0);
	       }
	     }
	   }
	 } else { // bpp 15 or 16
	   UINT16 *pixel;
	   for (y=0; y<bezel_bitmap->h; y++) {
	     pixel = (UINT16*)alpha_bitmap->line[y];
	     for (x=0; x<bezel_bitmap->w; x++) {
	       if (!*pixel++) { // black pixel -> transp
		 WriteWord(((UINT16*)bezel_bitmap->line[y])+x,0);
	       }
	     }
	   }
	 }
       }
       destroy_bitmap(alpha_bitmap);
     } // if (alpha_bitmap...
   } // if (alphafile[0]...
   unzClose(uf);

   if (!bezel_bitmap) {
     print_debug("could not load png %s\n",file);
   }

   print_debug("bezel loaded %s\n",file);

   return;
}

void bezel_fix_screen_size(int *myw,int *myh) {
  /* Tell raine that we need a biger screen to fit the bezel */
  if (bezel_bitmap) {
    print_debug("bezel_fix_screen_size before : %d,%d factors %g,%g\n",*myw,*myh,w-x,h-y);
    *myw = round(*myw*(w-x));
    *myh = round(*myh*(h-y));
    bezel_width = *myw;
    bezel_height = *myh;
    print_debug("bezel_fix_screen_size after : %d,%d\n",*myw,*myh);
  }
}

void bezel_fix_screen_coordinates(int *destx,int *desty,int viewx,int viewy, int screenx, int screeny) {
  /* This thing is wicked, it can also move the game screen from the center of the
     display... We must recalculate from screenx and screeny to avoid the rounding errors */
  int rotate_screen = (current_game->video_info->flags ^ display_cfg.user_rotate) & 3;

  if (bezel_bitmap) {
    print_debug("bezel: before coords fix %d,%d view %d,%d rotate %d\n",*destx,*desty,viewx,viewy,rotate_screen);
    if (rotate_screen == 3 || rotate_screen == 1) { // 270
      *desty = round((screeny-viewy*(w+x))/2);
      *destx = round((screenx-viewx*(h+y))/2);
    } else {
      *destx = round((screenx-viewx*(w+x))/2);
      *desty = round((screeny-viewy*(h+y))/2);
    }
    print_debug("bezel: after coords fix %d,%d\n",*destx,*desty);
  }
}

void display_bezel() {
  if (bezel_bitmap && display_cfg.bpp > 8) {
    int rotate_screen = (current_game->video_info->flags ^ display_cfg.user_rotate) & 3;
    int w = bezel_width, h = bezel_height;
    int multw =1, multh = 1;
    int xoff2,yoff2,destx,desty,xxx,yyy;

    if (!w || !h) {
	    // if wants_switch_res = 0, then the bezel is not initalised when arriving here...
	    int x = current_game->video_info->screen_x,y = current_game->video_info->screen_y;
	    bezel_fix_screen_size(&x,&y);
	    w = bezel_width;
	    h = bezel_height;
    }
    get_screen_coordinates(&xoff2,&yoff2,&destx,&desty,&xxx,&yyy);
    if (display_cfg.scanlines == 2) {// halfheight...
      multh*=2;
    }
    if (display_cfg.pixel_double==1 || display_cfg.eagle_fx || use_scale2x) {
      multh*=2; multw*=2;
    } else if (display_cfg.pixel_double==2) {// double width
      multw*=2;
    }

    w *= multw; h *= multh;
    print_debug("bezel: create bitmap %dx%d\n",w,h);
    BITMAP *scaled_bezel = create_bitmap_ex(bitmap_color_depth(bezel_bitmap),w,h);
    stretch_blit(bezel_bitmap,scaled_bezel,0,0,bezel_bitmap->w-1,bezel_bitmap->h-1,0,0,w,h);

    if (rotate_screen == 3 || rotate_screen == 1) { // 270
      destx *= multh;
      desty *= multw;
      desty += yyy*x*multh;
      destx += xxx*y*multw;
      if (rotate_screen == 3)
	pivot_sprite(screen,scaled_bezel,destx,desty,scaled_bezel->w-1,0,itofix(-64));
      else
	pivot_sprite(screen,scaled_bezel,destx,desty,0,scaled_bezel->h-1,itofix(64));
    } else if (rotate_screen == 0) {
      destx *= multw;
      desty *= multh;
      desty += yyy*y*multh;
      destx += xxx*x*multw;
      blit(scaled_bezel,screen,0,0,destx,desty,scaled_bezel->w,scaled_bezel->h);
    }
    destroy_bitmap(scaled_bezel);
  }
}
