/*
    Bazoo_worms the game
    Copyright (C) 2011  Pavel Prochazka

    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 3 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, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "MG_lib.h"
#include "WRMS_include.h"
#include "SDL/SDL_rotozoom.h"

#define PI 3.14159265

int *
make_int (int value)
{
  int *out = (int *) malloc (sizeof (int));
  *out = value;
  return out;
}

void
sprite_align_mid (SPRITE * to_align, SPRITE * source)
{
  POINT src = sprite_mid (source);
  POINT target =
    { src.x - (to_align->dest.w / 2), src.y - (to_align->dest.h / 2) };

  MG_sprite_set_position (to_align, target.x, target.y);
}

SDL_Rect
rect (int x, int y, int w, int h)
{
  SDL_Rect out = { x, y, w, h };
  return out;
}

int
rect_cmp (SDL_Rect r1, SDL_Rect r2)
{
  return ((r1.x == r2.x) && (r1.y == r2.y) && (r1.w == r2.w)
	  && (r1.h == r2.h));
}

SDL_Color
color (Uint8 r, Uint8 g, Uint8 b)
{
  SDL_Color out = { r, g, b, 0 };
  return out;
}

double
rad_to_degrees (double rad)
{
  return (rad * 180.0f) / PI;
}

double
deg_to_radians (double deg)
{
  return deg * (PI / 180.0f);
}

VECTOR
vector (float x, float y)
{
  VECTOR out = { x, y };
  return out;
}

double
vector_angle (VECTOR v)
{
  return asin (v.y / vector_length (v));
}

VECTOR
vector_normalize (VECTOR v, float len)
{
  float k = sqrt ((len * len) / ((v.x * v.x) + (v.y * v.y)));
  VECTOR out = { (v.x * k), (v.y * k) };
  return out;
}

VECTOR
vector_from_points (POINT p1, POINT p2)
{
  VECTOR out = { p2.x - p1.x, p2.y - p1.y };
  return out;
}

POINT
point_from_vector (VECTOR v, int len)
{
  VECTOR v_out = vector_normalize (v, (float) len);
  POINT out = { (int) v_out.x, (int) v_out.y };
  return out;
}

POINT
sprite_mid (SPRITE * s)
{
  POINT out = { s->dest.x + (s->dest.w / 2), s->dest.y + (s->dest.h / 2) };
  return out;
}

VECTOR
rotate_vector (VECTOR v, double rad)
{
  VECTOR out;
  out.x = (v.x * cos (rad)) - (v.y * sin (rad));
  out.y = (v.x * sin (rad)) + (v.y * cos (rad));
  return out;
}

float
vector_length (VECTOR v)
{
  return sqrt ((v.x * v.x) + (v.y * v.y));
}

void
vector_print (VECTOR v)
{
  printf ("v< %f , %f >\n", v.x, v.y);
}

void
point_print (POINT p)
{
  printf ("p< %d , %d >\n", p.x, p.y);
}

void
print_rect (SDL_Rect r)
{
  printf ("x: %d, y: %d, w: %d, h: %d\n", r.x, r.y, r.w, r.h);
}

double
points_distance (POINT p1, POINT p2)
{
  VECTOR tmp = { p2.x - p1.x, p2.y - p1.y };
  return sqrt ((tmp.x * tmp.x) + (tmp.y * tmp.y));
}

SDL_Rect
screen_rect ()
{
  MACHINE *m = MG_get_machine ();
  SDL_Rect out = { 0, 0, m->screen->w, m->screen->h };
  return out;
}

SDL_Rect
rects_cut (SDL_Rect r1, SDL_Rect r2)
{
  SDL_Rect out;
  POINT p1;
  POINT p2;

  out.x = MAX (r1.x, r2.x);
  out.y = MAX (r1.y, r2.y);

  p1.x = r1.x + r1.w;
  p1.y = r1.y + r1.h;

  p2.x = r2.x + r2.w;
  p2.y = r2.y + r2.h;

  out.w = MIN (p1.x, p2.x) - out.x;
  out.h = MIN (p1.y, p2.y) - out.y;
  return out;
}

POINT
pixel_perfect_collision_p (SPRITE * s1, SPRITE * s2)
{
  int x, y;
  Uint8 *p1;
  Uint8 *p2;

  SDL_Rect src1, src2;
  SDL_Rect cut = rects_cut (s1->dest, s2->dest);

  Uint32 row1, row2;
  Uint32 color1 = 0x0, color2 = 0x0;
  Uint32 tcolor1, tcolor2 = 0x0;

  Uint8 bpp1, bpp2;
  POINT out = { -1, -1 };
  float min_length = (float) (2 * (MAX (cut.w, cut.h)));
  float tmp_length = 0;
  POINT mid;
  mid.x = cut.w / 2;
  mid.y = cut.h / 2;

  src1 = rects_cut (s1->dest, cut);
  src2 = rects_cut (s2->dest, cut);

  src1.x -= s1->dest.x;
  src1.y -= s1->dest.y;
  src2.x -= s2->dest.x;
  src2.y -= s2->dest.y;

  bpp1 = s1->img->format->BytesPerPixel;
  bpp2 = s2->img->format->BytesPerPixel;

  tcolor1 = s1->img->format->colorkey;
  tcolor2 = s2->img->format->colorkey;

  SDL_LockSurface (s1->img);
  SDL_LockSurface (s2->img);

  row1 = s1->img->pitch;
  row2 = s2->img->pitch;

  for (y = 0; y < cut.h; y++)
    {
      p1 = (Uint8 *) (s1->img->pixels);
      p2 = (Uint8 *) (s2->img->pixels);
      p1 += ((src1.y + y) * row1);
      p2 += ((src2.y + y) * row2);

      p1 += (src1.x * bpp1);
      p2 += (src2.x * bpp2);

      for (x = 0; x < cut.w; x++)
	{
	  memcpy (&color1, p1, bpp1);
	  memcpy (&color2, p2, bpp2);
	  if ((color1 != tcolor1) && (color2 != tcolor2))
	    {
	      tmp_length =
		sqrt (((x - mid.x) * (x - mid.x)) +
		      ((y - mid.y) * (y - mid.y)));
	      if (tmp_length < min_length)
		{
		  min_length = tmp_length;
		  out.x = x;
		  out.y = y;
		}
	    }

	  p1 += (bpp1);
	  p2 += (bpp2);
	}
    }
  SDL_UnlockSurface (s1->img);
  SDL_UnlockSurface (s2->img);

  return out;
}

/* unsafe general function for getting pixel from surface */
Uint32
get_pixel (SDL_Surface * s, POINT pix)
{
  Uint32 out = 0;
  SDL_LockSurface (s);

  Uint8 *ptr_surf = (Uint8 *) s->pixels;

  ptr_surf += ((s->pitch * pix.y) + (s->format->BytesPerPixel * pix.x));
  memcpy (&out, ptr_surf, s->format->BytesPerPixel);

  SDL_UnlockSurface (s);

  return out;
}

/* checks if point lies inside a rectangle */
int
pixel_in_rect_p (SDL_Rect r, POINT pix)
{
  return (pix.x >= r.x && pix.x < (r.x + r.w) && pix.y >= r.y
	  && pix.y < (r.y + r.h));
}

/* checks if point lies on non transparent pixel in pixmap */
int
pixel_collide_sprite_p (SPRITE * s, POINT pix)
{
  if (!pixel_in_rect_p (s->dest, pix))
    return 0;

  POINT real = { pix.x - s->dest.x, pix.y - s->dest.y };
  Uint32 pixel;

  pixel = get_pixel (s->img, real);

  if (pixel == s->img->format->colorkey)
    return 0;

  return 1;
}

/* TEXT DRAWING implementation */
/* destructor */
void
destroy_sprite_surface (SPRITE * s)
{
  SDL_FreeSurface (s->img);
}

/* creates sprite with drawn text */
typedef struct
{
  char *path;
  Uint32 size;
  TTF_Font *font;
} FONT_DESC;

static LIST *font_cache = NULL;

TTF_Font *
font_from_cache (const char *text, Uint8 size)
{
  LIST *fit = font_cache;
  FONT_DESC *fd = NULL;
  FONT_DESC *out = NULL;

  while (fit)
    {
      fd = (FONT_DESC *) fit->value;
      if (fd)
	{
	  if (!strcmp (text, fd->path))
	    {
	      if (fd->size == size)
		{
		  out = fd;
		  break;
		}
	    }
	}
      fit = fit->node;
    }

  if (!out)
    {
      TTF_Font *f = TTF_OpenFont (text, size);

      if (!f)
	{
	  printf ("err: fontcache could not resolve file: %s\n", text);
	  return NULL;
	}

      out = (FONT_DESC *) malloc (sizeof (FONT_DESC));
      out->size = size;
      out->path = strdup (text);
      out->font = f;
    }
  return out->font;
}

void
font_desc_free (FONT_DESC * fd)
{
  free (fd->path);
  TTF_CloseFont (fd->font);
  free (fd);
}

void
WRMS_font_cache_clear ()
{
  MG_remove_list (font_cache, (void (*)(void *)) font_desc_free);
  font_cache = NULL;
}

SDL_Surface *
render_text (const char *text, SDL_Color color, Uint8 size, const char *font)
{
  TTF_Font *f = TTF_OpenFont (font, size);	//font_from_cache( font , size );
  if (!f)
    {
      printf ("err: user has removed font quitting!\n");
      exit (-1);
      return NULL;
    }

  SDL_Surface *text_surf = TTF_RenderUTF8_Solid (f, text, color);

  TTF_CloseFont (f);

  return text_surf;
}

SPRITE *
WRMS_text (const char *text, SDL_Color color, Uint8 size, const char *font)
{
  SPRITE *out = NULL;

  SDL_Surface *text_surf = render_text (text, color, size, font);

  if (!text_surf)
    return NULL;

  out = MG_make_sprite (text_surf, NULL);
  MG_install_callback (out, "on_destroy", destroy_sprite_surface);

  return out;
}

/* surface atoms */
SDL_Surface *
surface_new (Uint32 width, Uint32 height, Uint32 surf_flags)
{
  SDL_Surface *screen = SDL_GetVideoSurface ();
  SDL_PixelFormat *PixelFormat = screen->format;
  SDL_Surface *surf;

  surf = SDL_CreateRGBSurface (SDL_SWSURFACE, width, height,
			       PixelFormat->BitsPerPixel,
			       PixelFormat->Rmask, PixelFormat->Gmask,
			       PixelFormat->Bmask, PixelFormat->Amask);

  if (!surf)
    return NULL;

  if (PixelFormat->palette && surf->format->palette)
    {
      memcpy (surf->format->palette->colors,
	      PixelFormat->palette->colors,
	      PixelFormat->palette->ncolors * sizeof (SDL_Color));
      surf->format->palette->ncolors = PixelFormat->palette->ncolors;
    }

  SDL_SetColorKey (surf, surf_flags, SDL_MapRGB (surf->format, 255, 0, 255));
  SDL_FillRect (surf, NULL, SDL_MapRGB (surf->format, 255, 0, 255));

  return surf;
}

void
sprite_clear_surface (SPRITE * s)
{
  SDL_FreeSurface (s->img);
  s->img = NULL;
}

SDL_Surface *
surface_make_rect (int w, int h, SDL_Color background_color,
		   SDL_Color border_color, int border_width)
{
  SDL_Surface *out = surface_new (w, h, SDL_SRCCOLORKEY);
  SDL_Rect r = { 0, 0, out->w, out->h };
  SDL_FillRect (out, NULL,
		SDL_MapRGB (out->format, border_color.r, border_color.g,
			    border_color.b));
  r.x += border_width;
  r.y += border_width;
  r.w -= (2 * border_width);
  r.h -= (2 * border_width);

  SDL_FillRect (out, &r,
		SDL_MapRGB (out->format, background_color.r,
			    background_color.g, background_color.b));

  return out;
}

/* file ops */
long
file_size (FILE * pfile)
{
  long size = 0;
  fseek (pfile, 0, SEEK_END);
  size = ftell (pfile);
  rewind (pfile);
  return size;
}

char *
file_read (const char *path)
{
  FILE *pf = fopen (path, "r");
  if (!pf)
    return NULL;

  long sz = file_size (pf) + 1;
  char *buffer = (char *) malloc (sizeof (char) * sz);
  memset (buffer, 0, sz * (sizeof (char)));

  long read = fread (buffer, sizeof (char), sz - 1, pf);
  if (!read)
    printf ("warn: nothing read!\n");

  fclose (pf);
  return buffer;
}

char *
WRMS_parent_path (const char *path)
{
  char *out = strdup (path);
  int len = strlen (out);
  int it = len - 1;

  while (it >= 0)
    {
      char c = out[it];
      if (c == '/' && it != len - 1)
	{
	  out[it + 1] = '\0';
	  return out;
	}
      it--;
    }

  return out;
}

SDL_Surface *
surface_scale (SDL_Surface * in, int w, int h)
{
  double dx = (double) w / (double) in->w;
  double dy = (double) h / (double) in->h;

  return rotozoomSurfaceXY (in, 0, dx, dy, 0);
}
