#include "generale.h"
#include "assets.h"
#include "game.h"

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

	entity->health = 0.75;

	entity->velocity.angularVelocity = (AxisAngle){
		.axis = (Vector3){0.0, 1.0, 0.0},
		.angle = PI/2.0
	};

	// Allocate data.
	entity->data = KF_MALLOC(sizeof(Generale));

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

	Generale * data = (Generale*)entity->data;

	data->flyToPoint = (EntityFlyToPointInfo){
		.controller.bangbang.speed = 80.0,
		.controller.bangbang.stopAt = 0.0,
		.controlType = ENTITY_FLY_TO_POINT_BANG_BANG,
		.rotationSpeed = 100.0,
		.applyRotation = true
	};

	data->zigzag = GENERALE_ZIG;
	data->targetNotSet = true;

	data->laserDirection = Vector3One();
	data->isLaserOn = false;
}

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

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

void updateGeneraleLaser(Game * game, Entity * entity) {
	float t = GetFrameTime();
	Entity * player = getEntityFromWorld(game->world, 0);
	Generale * data = (Generale*)entity->data;

	// Not within distance.
	data->isLaserOn = Vector3Distance(player->position, entity->position) <= GENERALE_LASER_MAX_DISTANCE;

	if (!data->isLaserOn)
		return;

	// Direction to player.
	Vector3 targetDirection = Vector3Normalize(Vector3Subtract(player->position, entity->position));

	// Update laser direction.
	data->laserDirection = Vector3Lerp(data->laserDirection, targetDirection, t * GENERALE_LASER_TRACKING_SPEED);

	// Check if hits player
	Ray ray = (Ray){
		.position = entity->position,
		.direction = data->laserDirection
	};

	RayCollision collision = traceRayToEntity(*player, ray);

	// Do damage.
	if (collision.hit)
		player->health -= t * GENERALE_LASER_DAMAGE;

	//printf("%d\n", collision.hit);
}

void drawGeneraleLaser(Entity * entity) {
	Generale * data = (Generale*)entity->data;

	if (!data->isLaserOn)
		return;

	// Draw the evil laser of doom.
	DrawCylinderEx(
		Vector3Add(entity->position, Vector3Scale(data->laserDirection, entity->radius)),
		Vector3Add(entity->position, Vector3Scale(data->laserDirection, GENERALE_LASER_MAX_DISTANCE)),
		GENERALE_LASER_THICKNESS,
		GENERALE_LASER_THICKNESS,
		GENERALE_LASER_SIDES,
		RED
	);
}

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

	Generale * data = (Generale*)entity->data;

	// Next point.
	if (data->targetNotSet) {
		getTargetGenerale(game, entity);
		data->targetNotSet = false;
	} else if (Vector3Distance(entity->position, data->target) <= GENERALE_NEXT_POINT_THRESHOLD * GetFrameTime())
		getTargetGenerale(game, entity);

	entityFlyToPoint(
		entity,
		data->target,
		&data->flyToPoint
	);

	// Spin this fucker.
	entityUpdateRotation(entity);

	// THE EVIL LASER OF DOOM!!!!!
	updateGeneraleLaser(game, entity);

	entityCheckTransformedCollisionModel(entity);
}

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

	/*
	Generale * data = (Generale*)entity->data;

	DrawLine3D(
		entity->position,
		data->target,
		BLUE
	);

	DrawCubeV(data->target, Vector3One(), BLUE);
	*/
}

void getTargetGenerale(Game * game, Entity * entity) {
	Entity * player = getEntityFromWorld(game->world, 0);
	Generale * data = (Generale*)entity->data;

	Vector3 dis = Vector3Subtract(player->position, entity->position);
	Vector3 dir = Vector3Normalize(dis);

	SetRandomSeed(time(NULL));
	float targetDis = GetRandomValue(GENERALE_ZIGZAG_SIZE_MIN, GENERALE_ZIGZAG_SIZE_MAX);
	float distance = Vector3Distance(entity->position, player->position);

	if (distance < targetDis)
		targetDis = distance;

	if (data->zigzag == GENERALE_ZIG) {
		dir = Vector3RotateByAxisAngle(dir, Vector3One(), PI/4);
		data->zigzag = GENERALE_ZAG;
	} else {
		dir = Vector3RotateByAxisAngle(dir, Vector3One(), -PI/4);
		data->zigzag = GENERALE_ZIG;
	}

	dir = Vector3Scale(dir, targetDis);
	data->target = Vector3Add(entity->position, dir);
}