#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <math.h>
#include "SDL/SDL.h"
#include "SDL/SDL_mixer.h"
#include "include.h"

using namespace std;

unsigned int blend_avg(unsigned int source, unsigned int target)
{
    unsigned int sourcer = (source >>  0) & 0xff;
    unsigned int sourceg = (source >>  8) & 0xff;
    unsigned int sourceb = (source >> 16) & 0xff;
    unsigned int targetr = (target >>  0) & 0xff;
    unsigned int targetg = (target >>  8) & 0xff;
    unsigned int targetb = (target >> 16) & 0xff;

    targetr = (sourcer + targetr) / 2;
    targetg = (sourceg + targetg) / 2;
    targetb = (sourceb + targetb) / 2;

    return (targetr <<  0) |
           (targetg <<  8) |
           (targetb << 16);
}

unsigned int blend_mul(unsigned int source, unsigned int target)
{
    unsigned int sourcer = (source >>  0) & 0xff;
    unsigned int sourceg = (source >>  8) & 0xff;
    unsigned int sourceb = (source >> 16) & 0xff;
    unsigned int targetr = (target >>  0) & 0xff;
    unsigned int targetg = (target >>  8) & 0xff;
    unsigned int targetb = (target >> 16) & 0xff;

    targetr = (sourcer * targetr) >> 8;
    targetg = (sourceg * targetg) >> 8;
    targetb = (sourceb * targetb) >> 8;

    return (targetr <<  0) |
           (targetg <<  8) |
           (targetb << 16);
}

unsigned int blend_add(unsigned int source, unsigned int target)
{
    unsigned int sourcer = (source >>  0) & 0xff;
    unsigned int sourceg = (source >>  8) & 0xff;
    unsigned int sourceb = (source >> 16) & 0xff;
    unsigned int targetr = (target >>  0) & 0xff;
    unsigned int targetg = (target >>  8) & 0xff;
    unsigned int targetb = (target >> 16) & 0xff;

    targetr += sourcer;
    targetg += sourceg;
    targetb += sourceb;

    if (targetr > 0xff) targetr = 0xff;
    if (targetg > 0xff) targetg = 0xff;
    if (targetb > 0xff) targetb = 0xff;

    return (targetr <<  0) |
           (targetg <<  8) |
           (targetb << 16);
}

void drawcircle(int x, int y, int r, int c)
{
  int i, j;
  for (i = 0; i < 2 * r; i++)
  {
        // vertical clipping: (top and bottom)
    if ((y - r + i) >= 0 && (y - r + i) < HEIGHT)
    {
      int len = (int)sqrt((float)(r * r - (r - i) * (r - i))) * 2;
      int xofs = x - len / 2;

      // left border
      if (xofs < 0)
      {
        len += xofs;
        xofs = 0;
      }

      // right border
      if (xofs + len >= WIDTH)
      {
        len -= (xofs + len) - WIDTH;
      }
      int ofs = (y - r + i) * (Screen->pitch / 4) + xofs;

      // note that len may be 0 at this point,
      // and no pixels get drawn!
      for (j = 0; j < len; j++)
        ((unsigned int*)Screen->pixels)[ofs + j] = c;
    }
  }
}


void drawrect(int x, int y, int width, int height, int c)
{
  int i, j;
  for (i = 0; i < height; i++)
  {
    // vertical clipping: (top and bottom)
    if ((y + i) >= 0 && (y + i) < HEIGHT)
    {
      int len = width;
      int xofs = x;

      // left border
      if (xofs < 0)
      {
        len += xofs;
        xofs = 0;
      }

      // right border
      if (xofs + len >= WIDTH)
      {
        len -= (xofs + len) - WIDTH;
      }
      int ofs = (i + y) * (Screen->pitch / 4) + xofs;

      // note that len may be 0 at this point,
      // and no pixels get drawn!
      for (j = 0; j < len; j++)
        ((unsigned int*)Screen->pixels)[ofs + j] = c;
    }
  }
}

void drawtile(int x, int y, int tile)
{
  // Lock surface if needed
  if (SDL_MUSTLOCK(Tiles))
    if (SDL_LockSurface(Tiles) < 0)
      return;

  int i, j;
  for (i = 0; i < TILESIZE; i++)
  {
    // vertical clipping: (top and bottom)
    if ((y + i) >= 0 && (y + i) < HEIGHT)
    {
      int len = TILESIZE;
      int xofs = x;
      int tilexofs = 0;

      // left border
      if (xofs < 0)
      {
        tilexofs -= xofs;
        len += xofs;
        xofs = 0;
      }

      // right border
      if (xofs + len >= WIDTH)
      {
        len -= (xofs + len) - WIDTH;
      }
      int ofs = (i + y) * (Screen->pitch / 4) + xofs;

      // note that len may be 0 at this point,
      // and no pixels get drawn!

      int tileofs = (i + tile * TILESIZE) *
                    (Tiles->pitch / 4) + tilexofs;
      for (j = 0; j < len; j++)
      {
        ((unsigned int*)Screen->pixels)[ofs] =
          ((unsigned int*)Tiles->pixels)[tileofs];
        ofs++;
        tileofs++;
      }
    }
  }

  // Unlock if needed
    if (SDL_MUSTLOCK(Tiles))
        SDL_UnlockSurface(Tiles);
}

void drawcharacter(int x, int y, int character, int blend)
{
  if (character == 32)
    return;

  character -= 33; // our font does not have the first 33 characters.

  // Lock surface if needed
  if (SDL_MUSTLOCK(Font))
    if (SDL_LockSurface(Font) < 0)
      return;

  int i, j;
  for (i = 0; i < Font->w; i++)
  {
    int screenofs = x + (y + i) * (Screen->pitch / 4);
    int charofs = (i + character * Font->w) *
                (Font->pitch / 4) + FontOfs[character];
    for (j = 0; j < FontWidth[character]; j++)
    {
      int pixel = ((unsigned int*)Font->pixels)[charofs];
      if (pixel != 0)
      {
        int oldpixel = ((unsigned int*)Screen->pixels)[screenofs];
        switch (blend)
        {
        case -1:
          pixel = blend_mul(pixel ^ 0xffffff, oldpixel);
          break;
        case 0:
          pixel = blend_avg(pixel, oldpixel);
          break;
        case 1:
          pixel = blend_add(pixel, oldpixel);
          break;
        }
        ((unsigned int*)Screen->pixels)[screenofs] = pixel;
      }
      screenofs++;
      charofs++;
    }
  }

  // Unlock if needed
    if (SDL_MUSTLOCK(Font))
        SDL_UnlockSurface(Font);
}

void drawstring(int x, int y, char *s)
{
  while (*s != 0)
  {
    drawcharacter(x + 1, y + 1, *s, -1);
    drawcharacter(x, y, *s, 0);
    if (*s == 32)
      x += Font->w / 2;
    else
      x += FontWidth[*s - 33];
    s++;
  }
}

void transition(float v)
{
	if (SDL_MUSTLOCK(ScreenTransition))
		if (SDL_LockSurface(ScreenTransition) < 0)
			return;

	int i, j;

	int ypos = (int)(-pow(v, 5) * (HEIGHT + 64));

	int xdist[WIDTH];
	int ydist[HEIGHT];

	for (i = 0; i < WIDTH; i++)
	{
		xdist[i] = (int)((sin((v * (i + 10)) * 0.002) -
				sin((v * (i + 23)) * 0.032) *
				sin((v * (i + 54)) * 0.072)) * pow(v, 2) * 32);

	}

	for (i = 0; i < HEIGHT; i++)
	{
		ydist[i] = (int)((sin((v * (i + 90)) * 0.002) -
				sin((v * (i + 31)) * 0.021) *
				sin((v * (i + 57)) * 0.051)) * pow(v, 2) * 32);
	}

	for (i = 0; i < HEIGHT; i++)
	{
		for (j = 0; j < WIDTH; j++)
		{
			int u = j + ydist[i] + xdist[j];
			int v = i + xdist[j] + ydist[i] + ypos;

			if (u >= 0 && u < WIDTH &&
						 v >= 0 && v < HEIGHT)
			{
				((unsigned int*)Screen->pixels)[(j) + (i) * (Screen->pitch / 4)] =
						((unsigned int*)ScreenTransition->pixels)[(u) + (v) * (ScreenTransition->pitch / 4)];
			}
			else
			{
				((unsigned int*)Screen->pixels)[(j) + (i) * (Screen->pitch / 4)] = 0;
			}
		}
	}

	if (SDL_MUSTLOCK(ScreenTransition))
		SDL_UnlockSurface(ScreenTransition);
}
