////////////////////////////////////////////////////////////////////////////////
// Class VehicleAI and subclasses
// Classes that control vehicle movement, including physics
// Copyright Bjoern Ganster 
// Developed since 2010
////////////////////////////////////////////////////////////////////////////////

#include "FileList.h"
#include "GroundVehicleAI.h"
#include "intersections.h"
#include "MechSim.h"
#include "VehicleAI.h"
#include "Vehicles.h"

#include <algorithm>
#include <iostream>

#define FILE_NUMBER FILE_NO_VEHICLEAI_CPP

// Get list of polygons colliding with this bounding box
void findColliders(const BoundingBox& bb, RenderableObject* scene, const Vehicle* vehicle, std::vector<RenderableObject*>& colliders)
{
	SET_SCOPE;
	Mesh* mesh = dynamic_cast<Mesh*>(scene);

	if (mesh != nullptr) {
		Point3D meshMin, meshMax;
		BoundingBox meshBB = mesh->getTransformedBB();

		if (bb.intersects(meshBB)) {
			colliders.push_back(mesh);
		}
	}

	// Check childs
	for (size_t i = 0; i < scene->getObjectCount(); ++i) {
		RenderableObject* child = scene->getObject(i);
		if (child != nullptr && child != vehicle) {
			findColliders(bb, child, vehicle, colliders);
		}
	}

	RETURN;
}

// Resolve collisions between two moveable objects
// todo: 
// - take into account different weights
// - detect what sections on the mech receive damage and how much
// - DFA ...
void resolveObjectCollision(Vehicle* oneVehicle, Vehicle* otherVehicle)
{
	#ifdef _DEBUG
	BGCPString msg = BGCPString("Collision between ") + oneVehicle->MVMKFN;
	BGCPString id = oneVehicle->getID();
	if (!id.empty()) {
		msg += BGCPString(" (") + id + BGCPString(")");
	}
	msg += BGCPString(" and ") + otherVehicle->MVMKFN;
	id = otherVehicle->getID();
	if (!id.empty()) {
		msg += BGCPString(" (") + id + BGCPString(")");
	}
	msg.printLn();
	#endif

	MechSim* mechSim = getMechSim();
	#ifdef MECHVM
	mechSim->playSound("MECSEXX1.SFL");
	#endif
	AbstractVehicleAI* otherVehicleAI = dynamic_cast <AbstractVehicleAI*>(otherVehicle->getAI());
	AbstractVehicleAI* oneVehicleAI = dynamic_cast <AbstractVehicleAI*>(oneVehicle->getAI());
	if (otherVehicleAI != nullptr && oneVehicleAI != nullptr) {
		Point3D mid = 0.5 * (oneVehicle->getPosition() + otherVehicle->getPosition());

		Point3D newFront = oneVehicle->getPosition() - mid;
		newFront.y = 0.0;
		newFront.unitLength();
		oneVehicle->addMovement(newFront);

		newFront = otherVehicle->getPosition() - mid;
		newFront.y = 0.0;
		newFront.unitLength();
		otherVehicle->addMovement(newFront);
	}
}

// Resolve collisions with unmoveable object
void resolveGroundCollision(Vehicle* vehicle, RenderableObject* collider, const BoundingBox& vehicleBB, 
	Point3D& movement, bool& movementChanged)
{
	SET_SCOPE;
	Mesh* colliderMesh = dynamic_cast<Mesh*>(collider);
	if (colliderMesh == nullptr || vehicle == nullptr) {
		RETURN;
	}

	for (size_t i = 0; i < colliderMesh->getPolygonCount(); ++i) {
		const MeshPolygon* poly = colliderMesh->getPolygon(i);
		if (poly != nullptr && poly->getVertexCount() > 2) {
			BoundingBox polyBB = poly->getTransformedBB();

			if (vehicleBB.intersects(polyBB)) {
				vehicle->checkCollision(poly, movement);
			}
		}
	}

	RETURN;
}

// Constructor
AbstractVehicleAI::AbstractVehicleAI(const char* className, int classId, Vehicle* vehicle)
: VirtualClass(className, classId),
  groupFire(false),
  mVehicle(vehicle),
  mTarget(nullptr),
  mAimingAccuracy(10.0)
{
}

AbstractVehicleAI::~AbstractVehicleAI()
{
}

Point3D AbstractVehicleAI::getFront() const
{
	if (mVehicle != nullptr) {
		return mVehicle->p_front;
	} else {
		return Point3D();
	}
}

double AbstractVehicleAI::getSpeed() const
{
	if (mVehicle != nullptr) {
		return mVehicle->getSpeed();
	} else {
		return 0.0;
	}
}

// Get vehicle controlled by this AI
Vehicle* AbstractVehicleAI::getVehicle()
{
	return mVehicle;
}

const Vehicle* AbstractVehicleAI::getVehicle() const
{
	return mVehicle;
}

// Find out what the AI is currently targeting
GameObject* AbstractVehicleAI::getTarget() const
{
	return mTarget;
}

int AbstractVehicleAI::getPerspective() const
{
	return 0;
}

int PlayerVehicleAI::getPerspective() const
{
	return mPerspective;
}

void AbstractVehicleAI::setTarget(GameObject* gameObject)
{
	// AI shouldn't attack vehicles of its own faction
	SET_SCOPE;
	if (gameObject != nullptr) {
		if (gameObject->getFaction() == mVehicle->getFaction()) {
			RETURN;
		}

		if (mVehicle->getStatus() == shutDown) {
			mVehicle->setStatus(engineRunning);
		}
	}

	mTarget = gameObject;
	RETURN;
}

void PlayerVehicleAI::setTriggerRange(double newVal)
{
}

void PlayerVehicleAI::addAttackGoal(const BGCPString& target)
{
}

// Set AI goals
void PlayerVehicleAI::addGoals(const std::vector<AIGoal>& goals, bool clearExisting)
{
}

// Set skills (will be reflected by other variables, should be implemented by subclasses)
void AbstractVehicleAI::setPiloting(int newVal)
{
}

void AbstractVehicleAI::setGunnery(int newVal)
{
}
