#include "antifaShip.h"
#include "game.h"
#include "settings.h"
#include "bullets.h"
#include "assets.h"

void initAntifaShip(Entity * entity, Game * game) {
	entity->model = &game->assets.models[ANTIFA_SHIP_ASSET];
	entity->collisionModel = entityCreateCollisionModel(*entity->model);
	entity->transformedCollisionModel = entityCreateCollisionModel(*entity->model);
	setEntityRadius(entity);

	// Acceleration stuff.
	entity->useAcceleration = true;
	entity->acceleration = (EntityAcceleration){
		.speedUp = 30.0,
		.speedDown = 15,
		.rotation = (Vector3){2000000.0, 2000000.0, 2000000.0}
	};

	// Set Data pointer.
	entity->data = KF_MALLOC(sizeof(AntifaShip));

	if (entity->data == NULL) {
		ALLOCATION_ERROR;
		return;
	}

	AntifaShip * data = (AntifaShip*)entity->data;
	data->lastMouse = GetMousePosition();
	data->forwardSpeed = 0.0;
	data->shouldInitMousePosition = true;
	data->timeSinceLastBullet = GetTime();
	data->doAutoTarget = false;
	data->targetedEntityId = ENTITY_NONE;
	data->isOnTarget = false;
}

// Go back to burger king you elon musking loving mother fucker!

void closeAntifaShip(Entity * entity) {
	if (entity->data != NULL)
		KF_FREE(entity->data);

	entityFreeCollisionModel(entity->collisionModel);
	entityFreeCollisionModel(entity->transformedCollisionModel);
}

float getAntifaShipAimDistance(Entity * ship, Entity * targetEntity) {
	Vector3 directionAiming = Vector3RotateByQuaternion((Vector3){0.0, 0.0, 1.0}, ship->rotation);
	Vector3 directionToTarget = Vector3Normalize(Vector3Subtract(targetEntity->position, ship->position));
	return Vector3Distance(directionAiming, directionToTarget);
}

// Returns closest entity to ship and in range or none.
EntityId getShipInRangeAntifaShip(Game * game, Entity * entity) {
	int i;

	// Needs at least two entities to work correctly.
	if (game->world.entitiesCount < 2)
		return ENTITY_NONE;

	// Start out with entity 1 as closest.
	Entity * currentEntity = &game->world.entities[1];
	EntityId closestId = ENTITY_NONE;
	float closestDistance = (float)INT_MAX;
	float distance;

	// This entity will only ever be at id 0 so skip it.
	for (i = 1; i < game->world.entitiesCount; ++i) {

		// Get entity and distance.
		currentEntity = &game->world.entities[i];
		distance = Vector3Distance(currentEntity->position, entity->position);

		// Is closest and in range.
		if (distance < closestDistance && getAntifaShipAimDistance(entity, currentEntity) <= ANTIFA_START_AUTO_TARGET_MAX) {
			closestDistance = distance;
			closestId = currentEntity->id;
		}
	}

	return closestId;
}

void updateAntifaShipTarget(Game * game, Entity * entity) {
	float t = GetFrameTime();
	AntifaShip * data = (AntifaShip*)entity->data;

	if (data->doAutoTarget) {
		Entity * targetEntity = getEntityFromWorld(game->world, data->targetedEntityId);

		// Lost target.
		if (targetEntity == NULL) {
			data->doAutoTarget = false;
			return;
		} else if (targetEntity->fingerprint != data->targetedEntityFingerprint) {
			data->doAutoTarget = false;
			return;
		}

		// If to far from target.
		float aimDistance = getAntifaShipAimDistance(entity, targetEntity);

		if (aimDistance > ANTIFA_START_AUTO_TARGET_MAX) {
			data->doAutoTarget = false;
			return;
		}

		Vector3 directionToTarget = Vector3Normalize(Vector3Subtract(targetEntity->position, entity->position));

		// Update target.
		float speed = ANTIFA_TARGETING_SPEED / aimDistance;
		data->gunTarget = Vector3Lerp(data->gunTarget, directionToTarget, speed * t);
		//printf("%f\n", speed);

		// Is on target.
		data->isOnTarget = traceRayToEntity(*targetEntity, (Ray){entity->position, data->gunTarget}).hit;
	} else {
		data->isOnTarget = false;
		data->gunTarget = Vector3RotateByQuaternion((Vector3){0.0, 0.0, 1.0}, entity->rotation);
	}
}

void toggleAntifaShipAutoTarget(Game * game, Entity * entity) {
	AntifaShip * data = (AntifaShip*)entity->data;

	// Turn off auto target.
	if (data->doAutoTarget) {
		data->doAutoTarget = false;
		return;
	}

	EntityId closestId = getShipInRangeAntifaShip(game, entity);

	if (closestId == ENTITY_NONE)
		return;

	data->targetedEntityId = closestId;
	Entity * closestEntity = getEntityFromWorld(game->world, closestId);
	data->targetedEntityFingerprint = closestEntity->fingerprint;

	data->doAutoTarget = true;
}

// This fucker will fire a bullet!!!
void fireBulletAntifa(Game * game, Entity * entity) {
	double t = GetTime();
	AntifaShip * data = (AntifaShip*)entity->data;

	// Let it cool down.
	if (t - data->timeSinceLastBullet < ANTIFA_BULLET_COOLDOWN)
		return;

	Bullet bullet = createBulletFromDirection(*entity, data->gunTarget, ANTIFA_BULLET_DAMAGE);
	BulletHitInfo info = shootBullet(&game->world, bullet);
	data->lastBulletShot = bullet;

	if (info.hit) {
		Entity * hitEntity = getEntityFromWorld(game->world, info.hitId);

		// We were the fucker that killed this low iq fascist!
		if (hitEntity->health <= 0.0)
			hitEntity->killedByPlayer = true;
	}

	data->timeSinceLastBullet = t;
}

void controlAntifaShipJoystick(Game * game, Entity * entity) {
	Settings settings = game->settings;
	int gamePadNum = settings.gamePadNum;

	// Get joystick values.
	float pitchStick = GetGamepadAxisMovement(gamePadNum, settings.pitchStick);
	float yawStick = GetGamepadAxisMovement(gamePadNum, settings.yawStick);
	float rollStick = GetGamepadAxisMovement(gamePadNum, settings.rollStick);
	float speedStick = GetGamepadAxisMovement(gamePadNum, settings.speedStick);

	// Shoot button.
	if (IsGamepadButtonPressed(gamePadNum, 8))
		fireBulletAntifa(game, entity);

	Vector3 stick = (Vector3){
		pitchStick,
		-yawStick,
		rollStick
	};

	stick = Vector3Scale(stick, settings.joystickSensitivity);
	float speed = fabs(speedStick * ANTIFA_SHIP_MAX_SPEED);

	entityJoystickControl(entity, stick, speed);
}

void controlAntifaShipKeyboardAndMouse(Game * game, Entity * entity) {
	AntifaShip * data = (AntifaShip*)entity->data;

	// Resets mouse on init.
	if (data->shouldInitMousePosition) {
		data->shouldInitMousePosition = false;
		SetMousePosition(0, 0);
		data->lastMouse = Vector2Zero();
	}

#ifdef DEBUG_KEYS
	// Kill me.
	if (IsKeyPressed(KEY_K))
		entity->health = 0.0;
	// Kill everyone else.
	if (IsKeyPressed(KEY_L))
		for (int i = 1; i < game->world.entitiesCount; ++i)
			game->world.entities[i].health = 0.0;
#endif

	// Shoot bullet.
	if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON))
		toggleAntifaShipAutoTarget(game, entity);
	if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
		fireBulletAntifa(game, entity);

	// Get mouse values.
	Vector2 mouse = GetMousePosition();
	float speed = GetMouseWheelMove();
	
	data->forwardSpeed += (speed * game->settings.scrollBarSpeed);

	if (data->forwardSpeed < 0.)
		data->forwardSpeed = 0.0;
	else if (data->forwardSpeed > ANTIFA_SHIP_MAX_SPEED)
		data->forwardSpeed = ANTIFA_SHIP_MAX_SPEED;

	Vector2 v = Vector2Subtract(mouse, data->lastMouse);
	v = Vector2Scale(v, 1.0 / GetFrameTime());
	data->lastMouse = mouse;

	// Using mouse as a joystick.
	Vector3 mouseStick = (Vector3){v.y, -v.x, 0.0};
	mouseStick = Vector3Scale(mouseStick, game->settings.mouseSensitivity);

	// Swap axis for more movement with mouse.
	if (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE)) {
		mouseStick.z = -mouseStick.y;
		mouseStick.y = 0.0;
	}

	entityJoystickControl(entity, mouseStick, data->forwardSpeed);
}

void updateAntifaShip(Game * game, Entity * entity) {
	entityUpdateLastValues(entity);

	updateAntifaShipTarget(game, entity);

	switch (game->settings.controlMode) {
		case JOYSTICK_CONTROL:
			controlAntifaShipJoystick(game, entity);
			break;
		case KEYBOARD_AND_MOUSE_CONTROL:
			controlAntifaShipKeyboardAndMouse(game, entity);
			break;
		default:
			break;
	}

	//printf("%f\n", entity->health);

	entityCheckTransformedCollisionModel(entity);
}

void drawAntifaShip(Game * game, Entity * entity) {
	entityDraw(entity);

	// Draw bullet.
	AntifaShip * data = (AntifaShip*)entity->data;

	// Auto target line.
	if (data->doAutoTarget) {
		Entity * targetedEntity = getEntityFromWorld(game->world, data->targetedEntityId);

		if (targetedEntity != NULL)
			DrawLine3D(
				entity->position,
				targetedEntity->position,
				data->isOnTarget ? RED : BLUE
			);
	}

	// Draw bullet being shot.
	if (GetTime() - data->timeSinceLastBullet <= ANTIFA_DRAW_BULLET_FOR)
		DrawRay(data->lastBulletShot.ray, RED);
}