/* ************************************************************************** */
/*                                                                            */
/*                                                        :::      ::::::::   */
/*   dda.c                                              :+:      :+:    :+:   */
/*                                                    +:+ +:+         +:+     */
/*   By: qmennen <qmennen@student.codam.nl>         +#+  +:+       +#+        */
/*                                                +#+#+#+#+#+   +#+           */
/*   Created: 2025/05/02 11:58:09 by whaffman          #+#    #+#             */
/*   Updated: 2025/06/04 18:32:57 by qmennen          ###   ########.fr       */
/*                                                                            */
/* ************************************************************************** */

#include "cub3d.h"
#include "vec_math.h"
#include <math.h>

t_vec2	get_delta_dist(t_vec2 ray_dir)
{
	t_vec2	delta_dist;

	if (ray_dir.x == 0)
		delta_dist.x = 1e30;
	else
		delta_dist.x = fabs(1 / ray_dir.x);
	if (ray_dir.y == 0)
		delta_dist.y = 1e30;
	else
		delta_dist.y = fabs(1 / ray_dir.y);
	return (delta_dist);
}

t_vec2	get_step(t_vec2 ray_dir)
{
	t_vec2	step;

	step.x = 2 * (ray_dir.x >= 0) - 1;
	step.y = 2 * (ray_dir.y >= 0) - 1;
	return (step);
}

t_vec2	get_side_dist(t_vec2 ray_dir, t_vec2 pos, t_vec2 delta_dist)
{
	const t_vec2	frac_pos = (t_vec2){pos.x - (int)pos.x, pos.y - (int)pos.y};
	const int		raydir_x_pos = (ray_dir.x >= 0);
	const int		raydir_y_pos = (ray_dir.y >= 0);
	t_vec2			side_dist;

	side_dist.x = ((1 - raydir_x_pos) * frac_pos.x
			+ raydir_x_pos * (1 - frac_pos.x)) * delta_dist.x;
	side_dist.y = ((1 - raydir_y_pos) * frac_pos.y
			+ raydir_y_pos * (1 - frac_pos.y)) * delta_dist.y;
	return (side_dist);
}

int	dda_main(t_vec2 ray_dir, t_vec2_int map_pos, t_vec2 *side_dist, t_map *map)
{
	const t_vec2	delta_dist = get_delta_dist(ray_dir);
	const t_vec2	step = get_step(ray_dir);
	int				side;
	int				hit;

	hit = 0;
	while (hit == 0)
	{
		side = (side_dist->x >= side_dist->y);
		side_dist->x += delta_dist.x * (1 - side);
		side_dist->y += delta_dist.y * side;
		map_pos.x += step.x * (1 - side);
		map_pos.y += step.y * side;
		if (map_pos.x < 0
			|| map_pos.x >= map->width
			|| map_pos.y < 0
			|| map_pos.y >= map->height)
			printf("Out of bounds: %d %d\n", map_pos.x, map_pos.y);
		if (map->grid[map_pos.y][map_pos.x] == TILE_EMPTY)
			map->grid[map_pos.y][map_pos.x] = TILE_VISITED;
		hit = (map->grid[map_pos.y][map_pos.x] == TILE_WALL) - (map->grid[map_pos.y][map_pos.x] == TILE_DOOR);
	}
	side = -1 * (1 - side) + side;
	if (hit == -1)
		side *= 2;
	return (side);
}

// Returns the distance to the wall hit if the wall is hit in y
// direction the result is negative, if the wall is hit
// in x direction the result is positive
double	dda(t_render *render, t_vec2 ray_dir, t_vec2 pos, t_map *map)
{
	const t_vec2	delta_dist = get_delta_dist(ray_dir);
	t_vec2			side_dist;
	int				side;

	side_dist = get_side_dist(ray_dir, pos, delta_dist);
	side = dda_main(ray_dir,
			(t_vec2_int){(int)pos.x, (int)pos.y},
			&side_dist,
			map);
	if (abs(side) == 2)
	{
		render->door = 1;
		side /= 2;	
	}
	side = (side + 1)/2;
	return ((1 - side) * (side_dist.x - delta_dist.x)
		- side * (side_dist.y - delta_dist.y));
}
