/*
 * Stonage: a puzzle game
 * Copyright (C) 2001 Luc Saillard <luc@saillard.org>
 *
 * Source for loading/drawing images
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

#include "stonage.h"
#include "image.h"
#include "game.h"
#include <stdlib.h>
#include <stdio.h>
#include <SDL.h>
#if defined(HAVE_SDL_SDL_IMAGE_H)
#include <SDL/SDL_image.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>


SDL_Surface *
Image_Load(const char *filename, int convert2screenformat)
{
  int i;
  char tmp[1024];
  SDL_Surface *image, *image2;
  i=0; image=NULL;
  while (datadir_path[i])
   {
     snprintf(tmp,1023,"%s/%s",datadir_path[i],filename);
     trace("Trying to load %s",tmp);
#if defined(HAVE_SDL_SDL_IMAGE_H)
     image = IMG_Load(tmp);
#else
     image = SDL_LoadBMP(tmp);
#endif
     if (image)
       break;
     i++;
   }
  if (image==NULL)
   {
     fprintf(stderr,_("Can't load this image %s\n"),filename);
     return NULL;
   }

  if (convert2screenformat)
   {
     image2 = SDL_DisplayFormat(image);
     SDL_FreeSurface(image);
     return image2;
   }
  else
    return image;
}

void
Image_Free(SDL_Surface *img)
{
  if (img!=NULL)
   {
     SDL_FreeSurface(img);
   }
}

/*
 * Description:
 *   Load a image at random in this directory
 */
SDL_Surface *
Image_Load_Random(const char *directory)
{
  int n,i,j;
  DIR *d=NULL;
  struct dirent *dir;
  SDL_Surface *img;
  char path[1024];
  char realdirectory[1024];

  img=NULL;
  i=n=0;

  while (datadir_path[i])
   {
     snprintf(realdirectory,1023,"%s/%s",datadir_path[i],directory);
     trace("Trying to open directory %s",realdirectory);

     d=opendir(realdirectory);
     if (d!=NULL)
       break;
     trace(_("Can't open directory %s (%s)"),realdirectory,strerror(errno));
     i++;
   }
  if (d==NULL)
   {
     trace("Found no valid directory to load background");
     return NULL;
   }

  while ((dir=readdir(d))!=NULL)
   {
     if (dir->d_name[0]=='.')
       continue;
     /* We don't accept Makefile has a file image :) */
     if (strstr(dir->d_name,"Makefile"))
       continue;

#ifdef ENABLE_DEBUG_TRACE
     snprintf(path,1023,"%s/%s",realdirectory,dir->d_name);
     trace("%s",path);
#endif
     n++;
   }

  rewinddir(d);

  j=1 + RANDOM()%n;

  trace("Image N°%d/%d",j,n);

  while (j && (dir=readdir(d))!=NULL)
   {
     if (dir->d_name[0]=='.')
       continue;
     /* We don't accept Makefile has a file image :) */
     if (strstr(dir->d_name,"Makefile"))
       continue;
#ifdef ENABLE_DEBUG_TRACE
     snprintf(path,1023,"%s/%s",realdirectory,dir->d_name);
#endif
     if (--j==0)
      {
	snprintf(path,1023,"%s/%s",realdirectory,dir->d_name);
	trace("%s",path);
#if defined(HAVE_SDL_SDL_IMAGE_H)
	img = IMG_Load(path);
#else
	img = SDL_LoadBMP(path);
#endif
	break;
      }
   }
  closedir(d);

  return img;
}

/**
 * Load a image in the image folder given a theme
 *
 * @param theme name of the theme where the image is located
 * @param filename name of the fil to load
 * @return a SDL_Surface
 */
SDL_Surface *
Image_ThemeLoad(const char *theme, const char *filename)
{
  int i;
  char tmp[1024];
  SDL_Surface *image;
  i=0; image=NULL;
  while (datadir_path[i])
   {
     snprintf(tmp,1023,"image/%s/%s",theme,datadir_path[i]);
     trace("Trying to load %s",tmp);
#if defined(HAVE_SDL_SDL_IMAGE_H)
     image = IMG_Load(tmp);
#else
     image = SDL_LoadBMP(tmp);
#endif
     if (image)
       break;
     i++;
   }
  if (image==NULL)
   {
     /* We have not found this image, perhaps it's not in the theme folder, but
      * we need to retry only in the theme is not null */
     if (strlen(theme)>0)
       return Image_ThemeLoad("",filename);
     fprintf(stderr,_("Can't load this image %s\n"),filename);
     return NULL;
   }
  return image;
}


/**
 *
 * TODO: Need to optimize ...
 * h: entre 0 et 1
 * s: entre 0 et 1
 * v: entre 0 et 1
 *
 */
static void 
Image_ConvertHSV2RGB(float h, float s, float v, unsigned char *rgb)
{
  double hue, saturation, value;
  double f, p, q, t;

  //rgb[3]=0;
  if (s == 0)
   {
     rgb[0]=v;
     rgb[1]=v;
     rgb[2]=v;
   }
  else
   {
     hue = h * 6.0;
     saturation = s;
     value = v;

     if (hue == 6.0)
       hue = 0;

     f = hue - (int) hue;
     p = value * (1.0 - saturation);
     q = value * (1.0 - saturation * f);
     t = value * (1.0 - saturation * (1.0 - f));

     switch ((int) hue)
      {
       case 0:
	 rgb[0] = value * 255;
	 rgb[1] = t * 255;
	 rgb[2] = p * 255;
	 break;

       case 1:
	 rgb[0] = q * 255;
	 rgb[1] = value * 255;
	 rgb[2] = p * 255;
	 break;
       case 2:
	 rgb[0] = p * 255;
	 rgb[1] = value * 255;
	 rgb[2] = t * 255;
	 break;

       case 3:
	 rgb[0] = p * 255;
	 rgb[1] = q * 255;
	 rgb[2] = value * 255;
	 break;

       case 4:
	 rgb[0] = t * 255;
	 rgb[1] = p * 255;
	 rgb[2] = value * 255;
	 break;

       case 5:
	 rgb[0] = value * 255;
	 rgb[1] = p * 255;
	 rgb[2] = q * 255;
	 break;
      }
   }
  //return *(uint32 *)rgb;
}


/**
 * Create a special image who have a gradient
 * @param width size the image (height is 1 pixel)
 * @return a SDL_Surface we need to free.
 */
static SDL_Surface *
Image_Gradient(int width)
{
  SDL_Surface *img;
  unsigned char *pixels, *pp;
  int i;

  pixels=(unsigned char *)malloc(width*3);
  if (pixels==NULL)
   {
     fprintf(stderr,"Not enought memory to create the timebar\n");
     return NULL;
   }

  pp=pixels;
  for (i=0;i<width;i++)
   {
     Image_ConvertHSV2RGB((float)((i*3)%width)/width,1,1,pp);
     pp+=3;
   }

  img = SDL_CreateRGBSurfaceFrom(pixels,width,1,24,4*width,0xff0000,0xff00,0xff,0);
  return img;
}

/**
 *
 * Make a 8 height bar line to represet the time elapsed
 *
 */
SDL_Surface *
Image_Timebar(int width)
{
  int i;

  SDL_Surface *oneline, *bar, *bar2;
  SDL_Rect src,dest;

  oneline = Image_Gradient(width);
  bar = SDL_CreateRGBSurface(SDL_HWSURFACE,width,8,32,
      oneline->format->Rmask, oneline->format->Gmask,
      oneline->format->Bmask, oneline->format->Amask);

  dest.x=0; 
  src.y=0;
  src.h=1;
  for (i=0;i<4;i++)
   {
     dest.y=i;
     src.x=i*2;
     src.w=width-(i*2);
     SDL_BlitSurface(oneline,&src, bar, &dest);
   }
  for (i=4;i<8;i++)
   {
     dest.y=i;
     src.x=(16-i*2);
     src.w=width-(16-i*2);
     SDL_BlitSurface(oneline,&src, bar, &dest);
   }

  bar2 = SDL_DisplayFormat(bar);
  free(oneline->pixels);
  SDL_FreeSurface(oneline);
  SDL_FreeSurface(bar);

  return bar2;
}

/**
 * Make the kinesist sprite
 * @param width size of sprite (it's a square)
 * @param n number of image for the animation
 * @param type (1:square selected, 0:no square selected)
 * @return a list of SDL_Surface
 */
List *
Image_Create_Kinesist(int width, int n, int type)
{
  SDL_Surface *img;
  List *imgs;
  unsigned char *pixels, *pixels2;
  int i,j,k;
  float h;

  imgs = NULL;

  for (j=0;j<n;j++)
   {
     img = SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCCOLORKEY,
	 width,width,24,0xff0000,0xff00,0xff,0);
     imgs=List_Append(imgs,img);
     SDL_SetColorKey(img,SDL_SRCCOLORKEY|SDL_RLEACCEL,0);

     SDL_LockSurface(img);

     for (k=0;k<3;k++)
      {
	/* TODO: optimize the calcul */
	pixels = ((unsigned char *)img->pixels)+(k*width*3)+k*3;
	pixels2 = ((unsigned char *)img->pixels)+(width*(width-k)*3)-(k+1)*3;
	/* Top and bottom */
	for (i=k;i<width-k;i++)
	 {
	   h=(float)(j+i)/(width-(k*2));
	   if (h>=1.0)
	     h-=1.0;
	   Image_ConvertHSV2RGB(h,type?1:0.5,1,pixels);
	   pixels2[0]=pixels[0];
	   pixels2[1]=pixels[1];
	   pixels2[2]=pixels[2];
	   pixels+=3;
	   pixels2-=3;
	 }
	/* Left and right */
	pixels-=3;
	pixels2+=3;
	for (i=k;i<width-k;i++)
	 {
	   h=(float)(j+i)/(width-(k*2));
	   if (h>=1.0)
	     h-=1.0;
	   Image_ConvertHSV2RGB(h,type?1:0.5,1,pixels);
	   pixels2[0]=pixels[0];
	   pixels2[1]=pixels[1];
	   pixels2[2]=pixels[2];

	   pixels+=img->pitch;
	   pixels2-=img->pitch;
	 }
      }

     SDL_UnlockSurface(img);
   }
  return imgs;
}



static unsigned int
Color_Fade2Black(unsigned int color32, SDL_PixelFormat *format, int amount)
{
  unsigned char r,g,b;

  SDL_GetRGB(color32,format,&r,&g,&b);
  if (r-amount>=0)
    r-=amount;
  else
    r=0;
  if (g-amount>=0)
    g-=amount;
  else
    g=0;
  if (b-amount>=0)
    b-=amount;
  else
    b=0;
  return SDL_MapRGB(format,r,g,b);
}

/**
 *
 * Fade a surface defined by a rectangle
 * Note: This is a slow operation
 *
 */
void
Image_Fade(SDL_Surface *s, SDL_Rect *rect)
{
  int x,y,skip;
  const int amount=40;

  trace("bytesperpixel=%d",s->format->BytesPerPixel);
  switch (s->format->BytesPerPixel)
   {
    case 2:
       {
	 unsigned short *p;
	 p=(unsigned short *)((unsigned char *)s->pixels + rect->y * s->pitch);
	 p+=rect->x;
	 skip = s->pitch - (rect->w * sizeof(unsigned short));
	 for (y=0; y < rect->h ; y++)
	  {
	    for (x=0; x < rect->w ; x++, p++)
	     {
	       *p=Color_Fade2Black(*p,s->format,amount);
	     }
	    (unsigned char *)p+=skip;
	  }
       }
      break;
    case 3:
       {
	 unsigned char *p;
	 p=(unsigned char *)s->pixels + rect->y * s->pitch;
	 p+=rect->x*3;
	 skip = s->pitch - (rect->w * 3);
	 for (y=0; y < rect->h ; y++)
	  {
	    for (x=0; x < rect->w ; x++, p+=3)
	     {
	       if (p[0]-amount>=0)
		 p[0]-=amount;
	       else
		 p[0]=0;
	       if (p[1]-amount>=0)
		 p[1]-=amount;
	       else
		 p[1]=0;
	       if (p[2]-amount>=0)
		 p[2]-=amount;
	       else
		 p[2]=0;
	     }
	    p+=skip;
	  }
       }
      break;
    case 4:
       {
	 unsigned int *p;
	 p=(unsigned int *)((unsigned char *)s->pixels + rect->y * s->pitch);
	 p+=rect->x;
	 skip = s->pitch - (rect->w * sizeof(unsigned int));
	 for (y=0; y < rect->h ; y++)
	  {
	    for (x=0; x < rect->w ; x++, p++)
	     {
	       *p=Color_Fade2Black(*p,s->format,amount);
	     }
	    (unsigned char *)p+=skip;
	  }
       }
      break;
    default:
      fprintf(stderr,"This depth %d isn't supported by Image_Fade()\n",s->format->BitsPerPixel);
   }
}

/**
 *
 * Fade the background which is exterieur to the game field
 *
 */
void
Background_Fade(SDL_Surface *s, struct t_level *l)
{
  int x,y,sw,sh;
  SDL_Rect r;

  /* All squares have the same size (for now) */
  sw = l->squares[1].images[0]->w;
  sh = l->squares[1].images[0]->h;

  x = (s->w - (sw * l->sizex))/2;
  y = (s->h - (sh * l->sizey))/2;
  if (x<=0 || y<=0)
   {
     fprintf(stderr,"Sanity Check: error your background is smaller than the playfield\n");
     return;
   }
  trace("x=%d y=%d surfw=%d surfh=%d pw=%d ph=%d",x,y,s->w,s->h,sw * l->sizex,sh * l->sizey);

  /* The top border */
  r.x=0; r.y=0; r.w=s->w; r.h=y;
  Image_Fade(s,&r);
  /* The bottom border */
  r.x=0; r.y=s->h-y;
  Image_Fade(s,&r);
  /* The left border */
  r.x=0; r.y=y; r.w=x; r.h=sh * l->sizey;
  Image_Fade(s,&r);
  /* The right border */
  r.x=s->w - x; r.y=y; r.w=x;
  Image_Fade(s,&r);

}


