#include <cuda_runtime.h>
#define _USE_MATH_DEFINES
#include <math.h>

#include "naive_update.h"
#include "main.h"
#include "utils.h"

__global__ void
update(Projectile* projs, Projectile* projs_snapshot, float delta_t)
{
	int i = blockIdx.x * blockDim.x + threadIdx.x;

	if (i >= MAX_PROJS)
	{
		return;
	}

	/* intentionally not a pointer */
	Projectile p = projs_snapshot[i];

	if (!p.active)
	{
		return;
	}


	float normalized_dt = delta_t * 60.0f;

	/* Gravity from all other projectiles */
	float grav_x = 0.0f;
	float grav_y = 0.0f;

	/* gravity computation: */
	for (int j = 0; j < MAX_PROJS; j++)
	{

		/* don't apply gravity to itself; */
		Projectile pj = projs_snapshot[j];
		if (j == i)
		{
			continue;
		}

		/* if it has no gravity, it can be
		 * ignored */
		if (!pj.active || pj.gravity == 0)
		{
			continue;
		}

		float dx = pj.pos.x - p.pos.x;
		float dy = pj.pos.y - p.pos.y;
		float dist_sq = dx * dx + dy * dy;

		/* avoid singularity if the projectiles are nearly on top of each other
		 */
		if (dist_sq < 1.0f)
		{
			continue;
		}

		/* strength of gravity should fall off with distance, scaled by the
		 * other projectile's gravity, per the inverse square law */

		float dist = sqrtf(dist_sq);
		float force = pj.gravity / dist_sq;

		grav_x += (dx / dist) * force;
		grav_y += (dy / dist) * force;

		// projs_snapshot[j] = pj;
	}

	update_standard_projectile_velocity(&p, grav_x, grav_y, delta_t,
	                                    normalized_dt);

	/* update the projectile in the global array */
	projs[i] = p;
}

void
update_cpu(Projectile* h_projs, float delta_t)
{
	/* needs a read-only buffer to avoid race conditions when updating position,
	 * etc. */
	Projectile snapshot[MAX_PROJS];
	memcpy(snapshot, h_projs, MAX_PROJS * sizeof(Projectile));

	for (int i = 0; i < MAX_PROJS; i++)
	{
		if (!snapshot[i].active)
		{
			continue;
		}

		Projectile p = snapshot[i];

		float normalized_dt = delta_t * 60.0f;

		float grav_x = 0.0f;
		float grav_y = 0.0f;

		/* gravity computation */
		for (int j = 0; j < MAX_PROJS; j++)
		{
			if (j == i || !snapshot[j].active || snapshot[j].gravity == 0)
			{
				continue;
			}

			float dx = snapshot[j].pos.x - p.pos.x;
			float dy = snapshot[j].pos.y - p.pos.y;
			float dist_sq = dx * dx + dy * dy;

			if (dist_sq < 1.0f)
			{
				continue;
			}


			float dist = sqrtf(dist_sq);
			float force = snapshot[j].gravity / dist_sq;

			grav_x += (dx / dist) * force;
			grav_y += (dy / dist) * force;
		}

		update_standard_projectile_velocity(&p, grav_x, grav_y, delta_t,
		                                    normalized_dt);

		h_projs[i] = p;
	}
}