#include "bumprace.h"
#include "SDL_image.h"
#include <stdlib.h>
#include <string.h>

#define PATHNUM 8
#define BPP 0
#define RECTS_NUM 40000
#ifndef DATADIR
#define DATADIR "BumpRace.app/Contents/Resources/data"
#endif
char DATAPATH[200]=DATADIR;
const char PATH[PATHNUM][200]={DATADIR,".","data","/usr/local/share/bumprace"
,"/usr/lib/bumprace","../data","/usr/share/bumprace", DATADIR};
SDL_Surface *Screen,*BackBuffer,*fadebuffer;
SDL_Rect blitrect,blitrects[RECTS_NUM];
int blitrects_num=0;
#ifdef DATADIR
void ComplainAndExit(void)
{
        fprintf(stderr, "Problem: %s\n", SDL_GetError());
        exit(1);
}
#endif
int abrand(int a,int b)  //random number between a and b (inclusive)
{
  return(a+(rand() % (b-a+1)));
}

int (*_PutPixel)(SDL_Surface *Surface, Sint32 X, Sint32 Y, Uint32 Color);

int fast_putpixel1(SDL_Surface *Surface, Sint32 X, Sint32 Y, Uint32 Color)
{
  if (X < 0 || X > Surface->w || Y < 0 || Y > Surface->h) 
    return -1;

  *((Uint8 *)Surface->pixels + Y * Surface->pitch + X) = Color;

  return 0;
}

int fast_putpixel2(SDL_Surface *Surface, Sint32 X, Sint32 Y, Uint32 Color)
{
  if (X < 0 || X > Surface->w || Y < 0 || Y > Surface->h) 
    return -1;

 *((Uint16 *)Surface->pixels + Y * Surface->pitch/2 + X) = Color;

  return 0;
}

int fast_putpixel3(SDL_Surface *Surface, Sint32 X, Sint32 Y, Uint32 Color)
{
  Uint8 *pix;
  int shift;

  if (X < 0 || X > Surface->w || Y < 0 || Y > Surface->h) 
    return -1;

  /* Gack - slow, but endian correct */
  pix = (Uint8 *)Surface->pixels + Y * Surface->pitch + X*3;
  shift = Surface->format->Rshift;
  *(pix+shift/8) = Color>>shift;
  shift = Surface->format->Gshift;
  *(pix+shift/8) = Color>>shift;
  shift = Surface->format->Bshift;
  *(pix+shift/8) = Color>>shift;

  return 0;
}

int fast_putpixel4(SDL_Surface *Surface, Sint32 X, Sint32 Y, Uint32 Color)
{
  if (X < 0 || X > Surface->w || Y < 0 || Y > Surface->h) 
    return -1;

  *((Uint32 *)Surface->pixels + Y * Surface->pitch/4 + X) = Color;

  return 0;
}

void init_SDL()  // sets the video mode
{
  int bpp=BPP;
  const SDL_VideoInfo *info;

  if ( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_AUDIO) < 0 ) {ComplainAndExit();}
  info = SDL_GetVideoInfo();
  if (info->vfmt->BitsPerPixel==8) bpp=16;
  atexit(SDL_Quit);
// Set the video mode (800x600 at 16-bit depth)  
  if (fullscreen)  
    Screen = SDL_SetVideoMode(800, 600, BPP, SDL_FULLSCREEN);
  else 
    { Screen = SDL_SetVideoMode(800, 600, BPP, 0); }
  if ( Screen == NULL ) {ComplainAndExit();}
// create BackBuffer
  BackBuffer = SDL_AllocSurface(Screen->flags,
                               800,
                               600,
                               Screen->format->BitsPerPixel,
                               Screen->format->Rmask,
                               Screen->format->Gmask,
                               Screen->format->Bmask, 0);
  if (BackBuffer == NULL)
  printf("ERROR: Couldn't create BackBuffer: %s\n", SDL_GetError());
  fadebuffer = SDL_AllocSurface(Screen->flags,
                               800,
                               600,
                               Screen->format->BitsPerPixel,
                               Screen->format->Rmask,
                               Screen->format->Gmask,
                               Screen->format->Bmask, 0);
  if (fadebuffer == NULL)
  printf("ERROR: Couldn't create fadebuffer: %s\n", SDL_GetError());
// Figure out what putpixel routine to use
   switch (Screen->format->BytesPerPixel)
   {
    case 1:
      _PutPixel = fast_putpixel1;
      break;
    case 2:
      _PutPixel = fast_putpixel2;
      break;
    case 3:
      _PutPixel = fast_putpixel3;
      break;
    case 4:
      _PutPixel = fast_putpixel4;
      break;
   }
}

void lock()
{
        if ( SDL_MUSTLOCK(Screen) ) {
                if ( SDL_LockSurface(Screen) < 0 )
		return;        }                                                                       
}

void unlock()
{
        if ( SDL_MUSTLOCK(Screen) ) {                                           
	                SDL_UnlockSurface(Screen); }                                                                       
}

// Performs Callback at each line point. This came straight from the
// asphyxia vga trainers
int DoLine (SDL_Surface *Surface, Sint32 X1, Sint32 Y1, Sint32 X2, Sint32 Y2, Uint32 Color, int Callback (SDL_Surface *Surf, Sint32 X, Sint32 Y, Uint32 Color))
{ 
  Sint32 dx, dy, sdx, sdy, x, y, px, py; 
 
  dx = X2 - X1; 
  dy = Y2 - Y1; 
 
  sdx = (dx < 0) ? -1 : 1; 
  sdy = (dy < 0) ? -1 : 1; 
 
  dx = sdx * dx + 1; 
  dy = sdy * dy + 1; 
 
  x = y = 0; 
 
  px = X1; 
  py = Y1; 
 
  if (dx >= dy) 
    { 
      for (x = 0; x < dx; x++) 
	{ 
          Callback(Surface, px, py, Color);
	 
	  y += dy; 
	  if (y >= dx) 
	    { 
	      y -= dx; 
	      py += sdy; 
	    } 
	  px += sdx; 
	} 
    } 
  else 
    { 
      for (y = 0; y < dy; y++) 
	{ 
 
          Callback(Surface, px, py, Color);
 
	  x += dx; 
	  if (x >= dy) 
	    { 
	      x -= dy; 
	      px += sdx; 
	    } 
	  py += sdy; 
	} 
    } 
   return 0;
}

void fadeout()
{
	int y;
	SDL_Rect rect;
	Uint32 time;

	rect.x = 0;
	rect.w = 800;
	rect.h = 1;
	for (y=0; y<600; y+=2) {
		time = SDL_GetTicks();
		rect.y = y;
		SDL_FillRect(Screen, &rect, SDL_MapRGB(Screen->format, 0,0,0));
		SDL_UpdateRects(Screen, 1, &rect);
		if (time == SDL_GetTicks())
			SDL_Delay(1);
	}
	for (y=599; y>0; y-=2) {
		rect.y = y;
		SDL_FillRect(Screen, &rect, SDL_MapRGB(Screen->format, 0,0,0));
		SDL_UpdateRects(Screen, 1, &rect);
	}
	
	/*
  int x,y;
  
  if (dofadeout==0) return;
  
  for (x=0;x<800;x++)
  {
    lock();
    for (y=0;y<300;y++)
    {
      PutPixel(Screen,x,y*2,SDL_MapRGB(Screen->format,0,0,0));
    }
    unlock();
    Update();
  }
  for (x=799;x>=0;x--)
  {
    lock();
    for (y=299;y>0;y--)
    {
      PutPixel(Screen,x,y*2+1,SDL_MapRGB(Screen->format,0,0,0));
    }
    unlock();
    Update();
  }*/
}

void Update()
{
  SDL_UpdateRects(Screen,blitrects_num,blitrects);
  blitrects_num=0;
}

void AddRect(int x1, int y1, int x2, int y2)
{
  int temp;

   /* Make sure X1 is before X2 */
   if (x2 < x1){
      temp = x2;
      x2 = x1;
      x1 = temp;
   }
   /* Make sure Y1 is before Y2 */
   if (y2 < y1){
      temp = y2;
      y2 = y1;
      y1 = temp;
   }
  blitrect.x = x1;
  blitrect.y = y1;
  blitrect.w = x2-x1+1;
  blitrect.h = y2-y1+1;
  if (x1<0) printf("x is too small in function AddRect!  -  %d\n",x1);else
  if (y1<0) printf("y is too small in function AddRect!  -  %d\n",y1);else
  if (x2>=800) printf("x is too big in function AddRect!  -  %d\n",x2);else
  if (y2>=600) printf("y is too big in function AddRect!  -  %d\n",y2);else {
      blitrects[blitrects_num]=blitrect;
      if (++blitrects_num>=RECTS_NUM-2)
      {printf("Too many blits!\n");blitrects_num--;Update();}
  }
}

void AddThisRect(SDL_Rect blitrect)
{
    blitrects[blitrects_num]=blitrect;
    if (++blitrects_num>=RECTS_NUM-2)
        {printf("Too many blits!\n");blitrects_num--;Update();}
}

void Blit(int Xpos,int Ypos,SDL_Surface *image)  //blits one GIF or BMP from the memory to the screen
{
  blitrect.x = Xpos;
  blitrect.y = Ypos;
  blitrect.w = image->w;
  blitrect.h = image->h;

  if (Xpos<-image->w) printf("WRONG BLIT: Xpos is too small! - %d\n",Xpos); else
  if (Xpos>=800) printf("WRONG BLIT: Xpos is too big! - %d\n",Xpos); else
  if (Ypos<-image->h) printf("WRONG BLIT: Ypos is too small! - %d\n",Ypos); else
  if (Ypos>=600) printf("WRONG BLIT: Ypos is too big! - %d\n",Ypos); else
  if ( SDL_BlitSurface(image, NULL, Screen, &blitrect) < 0 ) 
  {
    SDL_FreeSurface(image);
    ComplainAndExit();
  }
  blitrects[blitrects_num]=blitrect;
  blitrects_num++;
}

SDL_Surface *LoadImage(char *datafile, int transparent)   // reads one png into the memory
{
  SDL_Surface *pic,*pic2;
  char filename[200];
  int i=0;

  sprintf(filename,"%s/gfx/%s",DATAPATH,datafile);
  pic=IMG_Load(filename);
  while ( pic == NULL ) {
    strcpy(DATAPATH,PATH[i]);
    sprintf(filename,"%s/gfx/%s",DATAPATH,datafile);
    pic=IMG_Load(filename);
    i++;
    
    if (i>=PATHNUM)
    {
      fprintf(stderr,"Couldn't load %s: %s\n", filename, SDL_GetError());
      exit(2);
    }
  }
  if (transparent==3) return(pic);
  if (transparent==1)
    SDL_SetColorKey(pic,SDL_SRCCOLORKEY|SDL_RLEACCEL,SDL_MapRGB(pic->format,0xFF,0xFF,0xFF));
  if (transparent==2)
    SDL_SetColorKey(pic,SDL_SRCCOLORKEY|SDL_RLEACCEL,SDL_MapRGB(pic->format,0x00,0x00,0x00));
  pic2 = SDL_DisplayFormat(pic);
  SDL_FreeSurface(pic);
//  blit(0,0,pic2);
//  SDL_UpdateRect(screen,0,0,0,0);
  return (pic2);
}

void BlitToBB(int Xpos,int Ypos,SDL_Surface *image)  //blits one GIF or BMP from the memory to the screen
{
  blitrect.x = Xpos;
  blitrect.y = Ypos;
  blitrect.w = image->w;
  blitrect.h = image->h;
  if ( SDL_BlitSurface(image, NULL, BackBuffer, &blitrect) < 0 )
  {
    SDL_FreeSurface(image);
    ComplainAndExit();
  }
}

void BlitPart(int Xpos,int Ypos,SDL_Surface *image, SDL_Rect srcrect)
{
  blitrect.x = srcrect.x;											
  blitrect.y = srcrect.y;											
  blitrect.w = srcrect.w;											
  blitrect.h = srcrect.h;
  if ( SDL_BlitSurface(image, &srcrect , Screen, &blitrect) < 0 )
  {
    SDL_FreeSurface(image);
    ComplainAndExit();
  }
  blitrects[blitrects_num]=blitrect;
  blitrects_num++;
}

void FadeScreen(float speed)
{
  Sint32 now,i;

  SDL_BlitSurface(Screen,NULL,fadebuffer,NULL);
  now=SDL_GetTicks();
  for (i=255*speed;i>=0;i-=SDL_GetTicks()-now)
  {
    now=SDL_GetTicks();
    SDL_BlitSurface(fadebuffer,&blitrect,Screen,&blitrect);
    SDL_SetAlpha(BackBuffer,SDL_SRCALPHA,255-(int)(i/speed));
    Blit(0,0,BackBuffer);
    Update();
  }
  SDL_SetAlpha(BackBuffer,SDL_SRCALPHA,255);
  Blit(0,0,BackBuffer);
  Update();
}

int PutPixel(SDL_Surface *Surface, Sint32 X, Sint32 Y, Uint32 Color)
{
    if (X<0) printf("X < 0 in function PutPixel! - %d\n",X); else
    if (X>=800) printf("X >= 800 in function PutPixel! - %d\n",X); else
    if (Y<0) printf("Y < 0 in function PutPixel! - %d\n",Y); else
    if (Y>=600) printf("Y >= 600 in function PutPixel! - %d\n",Y); else
    {
        _PutPixel(Surface,X,Y,Color);
        AddRect(X,Y,X,Y);
    }
    return 0;
}

int PutPixelC(SDL_Surface *Surface, Sint32 X, Sint32 Y, Uint32 Color)
{
    if (X<0) printf("X < 0 in function PutPixelC! - %d\n",X); else
    if (X>=800) printf("X >= 800 in function PutPixelC! - %d\n",X); else
    if (Y<0) printf("Y < 0 in function PutPixelC! - %d\n",Y); else
    if (Y>=600) printf("Y >= 600 in function PutPixelC! - %d\n",Y); else
    {
        _PutPixel(Surface,X,Y,Color);
        AddRect(X,Y,X,Y);
    }
    return 0;
}

void PutBackPixel(SDL_Surface *Surface, Sint32 X, Sint32 Y)
{
    SDL_Rect rect;
    
    rect.w=1;
    rect.h=1;
    rect.x=X;
    rect.y=Y;
    SDL_BlitSurface(BackBuffer, &rect, Surface, &rect);
    AddThisRect(rect);
}
