#include "Util.h"
#include <math.h>

// special effects
#define NO_EFFECT 0
#define SHIMMER_EFFECT 1
#define DAMAGE_EFFECT 2
int effect = NO_EFFECT;
SDL_Rect fx_rect;
SDL_Surface *fx_surface;
int fx_x, fx_y;

int
getSectionDiff(int a1, int a2, int b1, int b2)
{
  if(a1 <= b1 && a2 > b1) {
    return a2 - b1;
  } else if(a1 >= b1 && a1 < b2) {
    return b2 - a1;
  } else {
    return 0;
  }
}

int
contains(SDL_Rect * a, int x, int y)
{
  return (x >= a->x && x < a->x + a->w &&
          y >= a->y && y < a->y + a->h ? 1 : 0);
}

int
sectionIntersects(int a1, int a2, int b1, int b2)
{
  return ((a1 <= b1 && a2 > b1) || (a1 >= b1 && a1 < b2));
}

int
intersects(SDL_Rect * a, SDL_Rect * b)
{
  return (sectionIntersects(a->x, a->x + a->w, b->x, b->x + b->w) &&
          sectionIntersects(a->y, a->y + a->h, b->y, b->y + b->h));
}

void
writeEndianData(SDL_RWops * rwop, Uint16 * block, size_t size)
{
  size_t n;
  for(n = 0; n < size; n++) {
    SDL_WriteLE16(rwop, *(block + n));
  }
}

int
intersectsBy(SDL_Rect * a, SDL_Rect * b, int value)
{
  int w, h;
  w = getSectionDiff(a->x, a->x + a->w, b->x, b->x + b->w);
  h = getSectionDiff(a->y, a->y + a->h, b->y, b->y + b->h);
  return (w > value && h > value ? (w > h ? w : h) : 0);
}

/**
   Simple run-length compression.
   For n entries of the same value, if n > 3 then write (each entry an int):
   key n value
   (Key should not appear as a valid image_index, currently key is f0f0f0f0.)
   return 0 on error, number of entries written on success
*/
int
compress(Uint16 * buff, size_t size, SDL_RWops * rwop)
{
  size_t size_written = 0;
#ifdef USE_COMPRESSION
  //  printf("Compressing...\n");
  Uint16 *block;
  size_t i, t = 0;
  Uint16 block_note = BLOCK_NOTE;
  if(!(block = (Uint16 *) malloc(sizeof(Uint16) * size))) {
    fprintf(stderr, "Out of memory when writing compressed file");
    fflush(stderr);
    exit(-1);
  }
  for(i = 0; i < size; i++) {
    if(i > 0 && buff[i] != block[t - 1]) {
      //    printf("\tdiff. int found i=%ld t=%ld buff[i]=%ld block[t - 1]=%ld - ", i, t, buff[i], block[t - 1]);
      if(t > 3) {
        //    printf("\tcompressed block\n");
        // compressed write
        SDL_WriteLE16(rwop, block_note);
        SDL_WriteLE16(rwop, t);
        SDL_WriteLE16(rwop, *block);

        size_written += 3;
      } else {
        //    printf("\tnormal block\n");
        // normal write
        writeEndianData(rwop, block, t);

        size_written += t;
      }
      t = 0;
    }
    block[t++] = buff[i];
  }
  // write the last block
  //  printf("\tlast diff. int found i=%ld t=%ld buff[i]=%ld block[t - 1]=%ld - ", i, t, buff[i], block[t - 1]);
  if(t > 3) {
    //  printf("\tcompressed block\n");
    // compressed write
    SDL_WriteLE16(rwop, block_note);
    SDL_WriteLE16(rwop, t);
    SDL_WriteLE16(rwop, *block);

    size_written += 3;
  } else {
    //  printf("\tnormal block\n");
    // normal write
    writeEndianData(rwop, block, t);

    size_written += t;
  }
  free(block);
#else
  //  printf("Writing uncompressed...\n");
  for(n = 0; n < size; n++) {
    SDL_WriteLE16(rwop, *(buff + n));
  }
  size_written += size;
#endif
  return size_written;
}

/**
   Read above compression and decompress into buff.
   return number of items read into buff (should equal size on success)
*/
int
decompress(Uint16 * buff, size_t size, SDL_RWops * rwop)
{
  Uint16 *block;
  size_t real_size;
  size_t i = 0, t = 0, r = 0, start = 0, n = 0;
  size_t count;
  Uint16 value;

  // read the file
  if(!(block = (Uint16 *) malloc(sizeof(Uint16) * size))) {
    fprintf(stderr, "Out of memory when writing compressed file");
    fflush(stderr);
    exit(-1);
  }
  //  real_size = fread(block, sizeof(Uint16), size, fp);
  real_size = SDL_RWread(rwop, block, sizeof(Uint16), size);
  if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
    for(n = 0; n < real_size; n++) {
      *(block + n) = SDL_SwapLE16(*(block + n));
    }
  }
#ifdef USE_COMPRESSION
  printf("Decompressing... real_size=%ld\n", (long int) real_size);
  for(t = 0; t < real_size;) {

    if((Uint16) block[t] == BLOCK_NOTE) {
      // write the previous block
      if(start != t) {
        count = t - start;
        //    printf("\tuncompressed block: i=%ld start=%ld t=%d count=%ld\n", i, start, t, count);
        memcpy(buff + i, block + start, count * sizeof(Uint16));
        i += count;
      }
      // write the compressed block
      // (these are for readability.)
      count = block[t + 1];
      value = block[t + 2];
      //    printf("\tcompressed block start: i=%ld start=%ld t=%ld count=%ld\n", i, start, t, count);
      for(r = 0; r < count; r++) {
        buff[i++] = value;
      }
      t += 3;
      start = t;
      //    printf("\tcompressed block end: i=%ld start=%ld t=%ld count=%ld\n", i, start, t, count);
    } else {
      t++;
    }
  }
  // write the last block
  if(start != t) {
    count = t - start;
    //  printf("\tuncompressed block: i=%ld start=%ld t=%d count=%ld\n", i, start, t, count);
    memcpy(buff + i, block + start, count * sizeof(Uint16));
    i += count;
  }
  //  printf("\tdone: i=%ld start=%ld t=%d count=%ld\n", i, start, t, count);
  free(block);
  return i;
#else
  //  printf("Read uncompressed.");
  memcpy(buff, block, real_size * sizeof(Uint16));
  free(block);
  return real_size * sizeof(Uint16);
#endif
}

void
createBack(SDL_Surface ** back_surface)
{
  int x, y;
  SDL_Rect pos;
  SDL_Surface *img = images[img_back]->image;

  if(!(*back_surface = SDL_CreateRGBSurface(SDL_HWSURFACE,
                                            screen->w + img->w,
                                            screen->h + img->h,
                                            screen->format->BitsPerPixel,
                                            0, 0, 0, 0))) {
    fprintf(stderr, "Error creating surm_face: %s\n", SDL_GetError());
    fflush(stderr);
    return;
  }

  /*
     for(y = 0; y < screen->h / TILE_H; y+=img->w/TILE_H) {
     for(x = 0; x < screen->w / TILE_W; x+=img->h/TILE_W) {
     pos.x = x * TILE_W;
     pos.y = y * TILE_H;
     pos.w = img->w;
     pos.h = img->h;
     SDL_BlitSurface(img, NULL, *back_surface, &pos);
     }
     }
   */
  for(y = 0; y < (*back_surface)->h; y += img->h) {
    for(x = 0; x < (*back_surface)->w; x += img->w) {
      pos.x = x;
      pos.y = y;
      pos.w = img->w;
      pos.h = img->h;
      SDL_BlitSurface(img, NULL, *back_surface, &pos);
    }
  }
}

void
shimmerEffect(SDL_Rect * rect, SDL_Surface * surface)
{
  effect = SHIMMER_EFFECT;
  memcpy(&fx_rect, rect, sizeof(fx_rect));
  fx_surface = surface;
  fx_y = rect->h;
}

void
damageEffect(SDL_Rect * rect, SDL_Surface * surface)
{
  effect = DAMAGE_EFFECT;
  memcpy(&fx_rect, rect, sizeof(fx_rect));
  fx_surface = surface;
  fx_y = rect->h;
}

void
processShimmer()
{
  int row[3][100], size[3][100];
  Uint32 color[3][100];
  int count;
  int x, j, r;
  SDL_Rect pos;
  int done;

  for(r = 0; r < 3; r++) {
    if(fx_y < 0) {
      effect = NO_EFFECT;
      break;
    }
    count = 0;
    for(x = 0; x < fx_rect.w; x += 2) {
      j = (int) (7.0 * rand() / (RAND_MAX));
      if(j == 1) {
        row[r][count] = x;
        size[r][count] = (int) (5.0 * rand() / (RAND_MAX)) + 2;
        if(effect == SHIMMER_EFFECT) {
          color[r][count] = SDL_MapRGBA(fx_surface->format,
                                        128 +
                                        (int) (100.0 * rand() / (RAND_MAX)),
                                        128 +
                                        (int) (100.0 * rand() / (RAND_MAX)),
                                        128 +
                                        (int) (100.0 * rand() / (RAND_MAX)),
                                        0x00);
        } else {
          if((int) (2.0 * rand() / (RAND_MAX)) == 1) {
            color[r][count] = SDL_MapRGBA(fx_surface->format,
                                          255,
                                          40 +
                                          (int) (50.0 * rand() / (RAND_MAX)),
                                          40 +
                                          (int) (50.0 * rand() / (RAND_MAX)),
                                          0x00);
          } else {
            color[r][count] = SDL_MapRGBA(fx_surface->format,
                                          255,
                                          255,
                                          40 +
                                          (int) (50.0 * rand() / (RAND_MAX)),
                                          0x00);
          }
        }
        count++;
      }
    }
    done = 0;
    while(!done) {
      done = 1;
      for(x = 0; x < count; x++) {
        if(size[r][x] > 0) {
          pos.x = fx_rect.x + row[r][x] - (size[r][x] / 2);
          if(effect == SHIMMER_EFFECT)
            pos.y = fx_rect.y + fx_rect.h - (fx_y - (size[r][x] / 2));
          else
            pos.y = fx_rect.y + fx_y - (size[r][x] / 2);
          pos.w = size[r][x];
          pos.h = size[r][x];
          SDL_FillRect(fx_surface, &pos, color[r][x]);
          if(--size[r][x] > 0)
            done = 0;
        }
      }
    }
    fx_y -= 5;
  }
  SDL_UpdateRects(fx_surface, 1, &fx_rect);
  //SDL_Flip(surface);
  //SDL_Delay(10);
}

void
processEffects()
{
  switch (effect) {
  case SHIMMER_EFFECT:
  case DAMAGE_EFFECT:
    processShimmer();
    break;
  }
}
