/*
 * collider.c
 * This file is part of Bean Counters Classic
 *
 * Copyright (C) 2018 - Félix Arreola Rodríguez
 *
 * Bean Counters Classic 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.
 *
 * Bean Counters Classic 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 Bean Counters Classic; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, 
 * Boston, MA  02110-1301  USA
 */

#include <SDL.h>

#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "collider.h"
#include "sdl2_rect.h"

struct _Collider {
	Uint32 offset_x, offset_y;
	Uint32 size_w, size_h;
	
	Uint32 pitch;
	
	Uint32 *pixels;
};

Uint32 collider_extract_block (Collider *c, int y, int x, int size) {
	int bit_pos;
	int align;
	Uint32 res;
	
	bit_pos = c->pitch * y + (x / 32);
	align = 32 - (x % 32);
	
	if (align == 32) {
		res = c->pixels[bit_pos];
	} else {
		res = c->pixels[bit_pos] << (32 - align);
		res = res | (c->pixels[bit_pos + 1] >> align);
	}
	
	if (size < 32) {
		/* Quitar los bits sobrantes */
		res = res ^ (res & ((1 << (32 - size)) - 1));
	}
	
	return res;
}

Collider * collider_new_from_file (const char *filename) {
	int fd;
	Collider *new;
	Uint32 temp;
	Uint32 four_int[4];
	int res, g, h;
	int map_size;
	
	fd = open (filename, O_RDONLY);
	
	if (fd < 0) {
		return NULL;
	}
	
	new = (Collider *) malloc (sizeof (Collider));
	
	if (new == NULL) goto bad_load;
	
	/* Empezar a leer los bytes iniciales para saber cuánto reservar en pixeles */
	res = read (fd, &temp, sizeof (temp));
	if (res < 0) goto bad_load_and_free;
	
	/* Versión incorrecta */
	if (temp != 1) goto bad_load_and_free;
	
	/* Consumir el byte extra de alineación de bytes */
	res = read (fd, &temp, sizeof (temp));
	if (res < 0) goto bad_load_and_free;
	
	res = read (fd, four_int, 4 * sizeof (Uint32));
	if (res < 4 * sizeof (Uint32)) goto bad_load_and_free;
	
	new->offset_x = four_int[0];
	new->offset_y = four_int[1];
	new->size_w = four_int[2];
	new->size_h = four_int[3];
	
	if (new->size_w % 32 != 0) {
		new->pitch = (new->size_w / 32) + 2;
	} else {
		new->pitch = (new->size_w / 32) + 1;
	}
	map_size = new->pitch * new->size_h;
	
	new->pixels = (Uint32 *) malloc (sizeof (Uint32) * map_size);
	if (new->pixels == NULL) goto bad_load_and_free;
	
	memset (new->pixels, 0, sizeof (Uint32) * map_size);
	
	for (h = 0; h < new->size_h; h++) {
		for (g = 0; g < new->pitch; g++) {
			res = read (fd, &temp, sizeof (Uint32));
			if (res < 0) goto bad_load_and_free_pixels;
			
			new->pixels[(h * new->pitch) + g] = temp;
		}
	}
	
	close (fd);
	
	return new;
	
bad_load_and_free_pixels:
	free (new->pixels);
bad_load_and_free:
	free (new);
bad_load:
	close (fd);
	
	return NULL;
}

Collider * collider_new_block (int w, int h) {
	Collider *new;
	int map_size;
	
	new = (Collider *) malloc (sizeof (Collider));
	
	if (new == NULL) return NULL;
	
	new->size_w = w;
	new->size_h = h;
	new->offset_x = new->offset_y = 0;
	
	if (new->size_w % 32 != 0) {
		new->pitch = (new->size_w / 32) + 2;
	} else {
		new->pitch = (new->size_w / 32) + 1;
	}
	map_size = new->pitch * new->size_h;
	
	/* Reservar los bytes necesarios */
	new->pixels = (Uint32 *) malloc (sizeof (Uint32) * map_size);
	
	if (new->pixels == NULL) {
		free (new);
		return NULL;
	}
	
	memset (new->pixels, -1, sizeof (Uint32) * map_size);
	
	return new;
}

int collider_hittest (Collider *a, int x1, int y1, Collider *b, int x2, int y2) {
	SDL_Rect rect_left, rect_right, result;
	int first = SDL_FALSE;
	
	int g, h;
	int s, x;
	int offset_a_x, offset_a_y;
	int offset_b_x, offset_b_y;
	
	rect_left.x = x1 + a->offset_x; // Sumar los offsets del collider
	rect_left.y = y1 + a->offset_y;
	rect_left.w = a->size_w;
	rect_left.h = a->size_h;
	
	rect_right.x = x2 + b->offset_x;
	rect_right.y = y2 + b->offset_y;
	rect_right.w = b->size_w;
	rect_right.h = b->size_h;
	
	first = SDL_IntersectRect (&rect_left, &rect_right, &result);
	
	if (first == SDL_FALSE) {
		/* Ni siquiera cercas */
		//printf ("Ni siquiera cercas de colision\n");
		return 0;
	}
	
	offset_a_y = result.y - rect_left.y;
	offset_a_x = result.x - rect_left.x;
	offset_b_y = result.y - rect_right.y;
	offset_b_x = result.x - rect_right.x;
	
	Uint32 block_a, block_b;
	
	for (h = result.h; h >= 0; h++) {
		s = result.w;
		x = 0;
		while (s > 0) {
			block_a = collider_extract_block (a, h + offset_a_y, x + offset_a_x, s);
			block_b = collider_extract_block (b, h + offset_b_y, x + offset_b_x, s);
			
			block_a = block_a & block_b;
			
			if (block_a != 0) {
				return 1;
			}
			
			x = x + 32;
			s = s - 32;
		}
	}
	
	return 0;
}

