////////////////////////////////////////////////////////////////////////////////
// GameObject.cpp
// Implementation of objects that can be destroyed in MechVM
// Copyright Bjoern Ganster
// Developed since 2020
////////////////////////////////////////////////////////////////////////////////

#include "BGCPString.h"
#include "MechSim.h"
#include "GameObject.h"
#include "Mesh.h"
#include "MissionGoals.h"

#define FILE_NUMBER FILE_NO_GAMEOBJECT_CPP

// Constructor
GameObject::GameObject(const char* className, BGClassID classID, RenderableObject* parent)
: Mesh(className, classID, parent),
  mHealth(20000),
  mDamageReceived(0.0),
  mInspected(false),
  mDestroyedGeometry(nullptr)
{
}

// Destructor
GameObject::~GameObject()
{
	safeDelete(mDestroyedGeometry);
}

void GameObject::setHealth(double newVal)
{
	mHealth = newVal;
}

// Function to notify the GameObject that it is taking damage
void GameObject::receiveDamage(RenderableObject* limb, double damage, GameObject* sender, const Point3D& pointOfImpact)
{
	SET_SCOPE;
	mDamageReceived += damage;
	if (isDestroyed()) {
		MechSim* mechSim = getMechSim();
		mechSim->updateObjectStatus(mID, SubObjectiveType::attack, false);

		if (mDestroyedGeometry != nullptr) {
			clear();
			addChild(mDestroyedGeometry);
			mDestroyedGeometry->color = Color(0.5f * color.r, 0.5f * color.g, 0.5f * color.b);
			mDestroyedGeometry = nullptr;
		} else {
			color = Color(0.0, 0.0, 0.0);
		}
	}
	RETURN;
}

bool GameObject::isDestroyed() const
{
	return (mDamageReceived >= mHealth);
}

void GameObject::setDescriptionText(const BGCPString& str)
{
	mDescriptionText = str;
}

const BGCPString& GameObject::getDescriptionText() const
{
	return mDescriptionText;
}

void GameObject::setInspectionText(const BGCPString& str)
{
	mInspectionText = str;
}

const BGCPString& GameObject::getInspectionText() const
{
	return mInspectionText;
}

void GameObject::setID(const BGCPString& str)
{
	mID = str;
}

const BGCPString& GameObject::getID() const
{
	return mID;
}

void GameObject::setPosition (double x, double y, double z)
{
	mPosition.x = x;
	mPosition.y = y;
	mPosition.z = z;
}

void GameObject::setPosition(const Point3D& pos)
{
	mPosition = pos;
}

const Point3D& GameObject::getPosition() const
{
	return mPosition;
}

Point3D GameObject::getCenterOfMass()
{
	Point3D min, max;
	if (getMinCoords(min) && getMaxCoords(max)) {
		return 0.5 * (min+max);
	} else {
		return mPosition;
	}
}

void GameObject::setInspected(bool newVal)
{
	mInspected = newVal;
}

bool GameObject::getInspected() const
{
	return mInspected;
}

// Set geometry to use when this object is destroyed
void GameObject::setDestroyedGeometry(Mesh* mesh)
{
	SET_SCOPE;
	safeDelete(mDestroyedGeometry);
	mDestroyedGeometry = mesh;
	RETURN;
}

// Set colors for wireframe rendering
void GameObject::setWireframeColors()
{
	SET_SCOPE;
	ColorBase v = (float) (mDamageReceived / mHealth);
	Color c = Color(v, 0, 1.0f-v);
	mWireframeColor = c;

	for (size_t i = 0; i < getObjectCount(); ++i) {
		RenderableObject* child = getObject(i);
		if (child != nullptr) {
			child->setRecursiveWireframeColor(c);
		}
	}
	RETURN;
}

////////////////////////////////////////////////////////////////////////////////
// Search the scene recursively for an object with the given id

GameObject* findGameObject(RenderableObject* ro, const BGCPString& id)
{
	if (ro == nullptr || id.empty()) {
		return nullptr;
	}

	SET_SCOPE;
	GameObject* go = dynamic_cast<GameObject*>(ro);
	if (go != nullptr) {
		if (go->getID() == id) {
			RETURN go;
		}
	}

	if (ro != nullptr) {
		for (size_t i = 0; i < ro->getObjectCount(); ++i) {
			GameObject* go = findGameObject(ro->getObject(i), id);
			if (go != nullptr) {
				RETURN go;
			}
		}
	}

	RETURN nullptr;
}

void GameObject::setFaction(const BGCPString& aFaction)
{
	mFaction = aFaction;
}

const BGCPString& GameObject::getFaction() const
{
	return mFaction;
}
