
#include "MW4Headers.hpp"

#include "AI_Log.hpp"

#include "aiutils.hpp"
#include "MWPlayer.hpp"
#include "VehicleInterface.hpp"

int g_objects_in_existence = 0;

using namespace MW4AI;


LogNode::LogNode(Type type, const std::string& text, const std::string& label)
	: m_Text(text)
	, m_Type(type)
	, m_Label(label)
	, m_Line(0)
{
	Verify(type >= TYPE_FIRST);
	Verify(type <= TYPE_LAST);
	++g_objects_in_existence;
}

LogNode::LogNode(Type type, const std::string& text, const std::string& label, const std::string& filename, int line_number)
	: m_Text(text)
	, m_Type(type)
	, m_Label(label)
	, m_File(filename)
	, m_Line(line_number)
{
	Verify(type >= TYPE_FIRST);
	Verify(type <= TYPE_LAST);
	++g_objects_in_existence;
}

LogNode::~LogNode()
{
	--g_objects_in_existence;
	while (m_Children.size() > 0)
	{
		delete (*(m_Children.begin()));
		m_Children.erase(m_Children.begin());
	}
}

LogNode* LogNode::AddChild(Type type, const std::string& text, const std::string& label)
{
	LogNode* new_node = new LogNode(type,text,label);
	m_Children.push_back(new_node);
	return (new_node);
}

LogNode* LogNode::AddChild(Type type, const std::string& text, const std::string& label, const std::string& filename, int line_number)
{
	LogNode* new_node = new LogNode(type,text,label,filename,line_number);
	m_Children.push_back(new_node);
	return (new_node);
}

LogNode* LogNode::Find(const std::string& text)
{
	{for (std::vector<LogNode*>::iterator i = m_Children.begin();
		  i != m_Children.end();
		  ++i)
	{
		LogNode* found = (*i)->Find(text);
		if (found != 0)
		{
			return (found);
		}
	}}

	if (m_Text == text)
	{
		return (this);
	}

	return (0);
}

std::string LogNode::GetText() const
{
	return (m_Text);
}

LogNode::Type LogNode::GetType() const
{
	return (m_Type);
}

std::string LogNode::GetLabel() const
{
	return (m_Label);
}

std::string LogNode::GetFile() const
{
	return (m_File);
}

int LogNode::GetLine() const
{
	return (m_Line);
}

void LogNode::SpewToString(std::string& output, unsigned int level, bool first_in_row) const
{
	std::string prefix;

	if (m_Text.size() > 0)
	{
		{for (unsigned int i = 0;
			  i < level;
			  ++i)
		{
			prefix += " | ";
		}}

		if (GetType() == LogNode::TYPE_FUNCTION)
		{
			output += prefix;
		}

		output += m_Text;

		if (m_Label.size() != 0)
		{
			output += "  (";
			output += m_Label;
			output += ")";
		}

		if (m_File.size() != 0)
		{
			std::string f = m_File;
			while (f.find('\\') != std::string::npos)
			{
				f.erase(0,1);
			}

			output += " [";
			output += f;
			output += ", line ";
			output += IntToString(m_Line);
			output += "]";
		}

		if (GetType() == LogNode::TYPE_FUNCTION)
		{
			output += "\n";
		}
	}

	bool fDrawn = false;
	{for (std::vector<LogNode*>::const_iterator i = m_Children.begin();
		  i != m_Children.end();
		  ++i)
	{
		if ((*i)->GetType() == LogNode::TYPE_PARAMETER)
		{
			if (fDrawn == false)
			{
				output += prefix;
			}
			else
			{
				output += ", ";
			}

			(*i)->SpewToString(output,level+1);
			fDrawn = true;
		}
	}}

	{for (std::vector<LogNode*>::const_iterator i = m_Children.begin();
		  i != m_Children.end();
		  ++i)
	{
		if ((*i)->GetType() == LogNode::TYPE_RETURN_VALUE)
		{
			if (fDrawn == false)
			{
				output += prefix;
			}
			else
			{
				output += ", ";
			}

			(*i)->SpewToString(output,level+1);
			fDrawn = true;
		}
	}}

	if (fDrawn == true)
	{
		output += "\n";
	}

	{for (std::vector<LogNode*>::const_iterator i = m_Children.begin();
		  i != m_Children.end();
		  ++i)
	{
		if ((*i)->GetType() == LogNode::TYPE_FUNCTION)
		{
			(*i)->SpewToString(output,level+1);
		}
	}}
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

DiagnosticsInterface* DiagnosticsInterface::m_Instance = 0;

DiagnosticsInterface::DiagnosticsInterface()
	: m_Enabled(false)
	, m_ExecutingObject(0)
	, m_FileAndLineEnabled(false)
	, m_ParameterNamesEnabled(false)
	, m_FileSpewing(false)
{
	Verify(m_Instance == 0);

	m_Instance = this;
	m_Root.Assimilate(new LogNode(LogNode::TYPE_FUNCTION,""));
}

DiagnosticsInterface::~DiagnosticsInterface()
{
	Verify(m_Instance == this);

	m_Instance = 0;
}

void DiagnosticsInterface::Reset()
{
	if (m_Root.GetPointer() != 0)
	{
		m_Root.Delete();
		m_Root.Assimilate(new LogNode(LogNode::TYPE_FUNCTION,""));
	}
}

DiagnosticsInterface& DiagnosticsInterface::GetInstance()
{
	Verify(m_Instance != 0);
	return (*m_Instance);
}

void DiagnosticsInterface::Log(LogNode::Type type, const std::string& value, const std::string& label)
{
	if (CanLog() == false)
	{
		return;
	}

	std::string l;
	if ((GetParameterNamesEnabled() == true) ||
		(type != LogNode::TYPE_PARAMETER))
	{
		l = label;
	}

	Verify(m_Stack.size() > 0);

	m_Stack.back()->AddChild(type,value,l);
}

void DiagnosticsInterface::Log(LogNode::Type type, const Stuff::Scalar& value, const std::string& label)
{
	if (CanLog() == false)
	{
		return;
	}

	Log(type,ScalarToString(value),label);
}

void DiagnosticsInterface::Log(LogNode::Type type, const Stuff::Point3D& value, const std::string& label)
{
	if (CanLog() == false)
	{
		return;
	}

	std::string s("(");
	s += ScalarToString(value.x);
	s += ",";
	s += ScalarToString(value.y);
	s += ",";
	s += ScalarToString(value.z);
	s += ")";
	Log(type,s,label);
}

void DiagnosticsInterface::Log(LogNode::Type type, const int& value, const std::string& label)
{
	if (CanLog() == false)
	{
		return;
	}

	Log(type,IntToString(value),label);
}

void DiagnosticsInterface::Log(LogNode::Type type, const bool& value, const std::string& label)
{
	if (CanLog() == false)
	{
		return;
	}

	Log(type,BoolToString(value),label);
}

void DiagnosticsInterface::Log(LogNode::Type type, const TacticInterface::WR_WHO& value, const std::string& label)
{
	if (CanLog() == false)
	{
		return;
	}

	switch (value)
	{
		case TacticInterface::SELF:
		{
			Log(type,(std::string)"Self",label);
			return;
		}
		case TacticInterface::TARGET:
		{
			Log(type,(std::string)"Target",label);
			return;
		}
	}

	Log(type,(std::string)"***INVALID***",label);
}

void DiagnosticsInterface::Log(LogNode::Type type, const TacticInterface::WR_QUALIFIER& value, const std::string& label)
{
	if (CanLog() == false)
	{
		return;
	}

	switch (value)
	{
		case TacticInterface::MIN:
		{
			Log(type,(std::string)"Min",label);
			return;
		}
		case TacticInterface::MAX:
		{
			Log(type,(std::string)"Max",label);
			return;
		}
	}

	Log(type,(std::string)"***INVALID***",label);
}

void DiagnosticsInterface::Log(LogNode::Type type, const Stuff::Time& value, const std::string& label)
{
	if (CanLog() == false)
	{
		return;
	}

	Log(type,ScalarToString((Stuff::Scalar)value),label);
}

void DiagnosticsInterface::Log(LogNode::Type type, const UserConstants::ID& value, const std::string& label)
{
	if (CanLog() == false)
	{
		return;
	}

	Log(type,IntToString((int)value),label);
}

void DiagnosticsInterface::Log(LogNode::Type type, const FireStyles::FireStyleID& value, const std::string& label)
{
	if (CanLog() == false)
	{
		return;
	}

	Log(type,FireStyles::FireStyleToString(value),label);
}

void DiagnosticsInterface::Log(LogNode::Type type, const FireData::HIT_RESULT& value, const std::string& label)
{
	if (CanLog() == false)
	{
		return;
	}

	switch (value)
	{
		case FireData::HIT_TARGET:
		{
			Log(type,(std::string)"HIT_TARGET",label);
			return;
		}

		case FireData::HIT_SOMETHING_ELSE:
		{
			Log(type,(std::string)"HIT_SOMETHING_ELSE",label);
			return;
		}

		case FireData::HIT_NOTHING:
		{
			Log(type,(std::string)"HIT_NOTHING",label);
			return;
		}
	}

	Log(type,(std::string)"***INVALID***",label);
}

void DiagnosticsInterface::PushFunction(const std::string& funcname, const std::string& file, int line)
{
	if (CanLog() == false)
	{
		return;
	}

	LogNode* new_node = 0;

	if (m_Stack.size() == 0)
	{
		if (GetFileAndLineEnabled() == true)
		{
			new_node = m_Root->AddChild(LogNode::TYPE_FUNCTION,funcname,"",file,line);
		}
		else
		{
			new_node = m_Root->AddChild(LogNode::TYPE_FUNCTION,funcname);
		}
	}
	else
	{
		if (GetFileAndLineEnabled() == true)
		{
			new_node = m_Stack.back()->AddChild(LogNode::TYPE_FUNCTION,funcname,"",file,line);
		}
		else
		{
			new_node = m_Stack.back()->AddChild(LogNode::TYPE_FUNCTION,funcname);
		}
	}

	m_Stack.push_back(new_node);
}

void DiagnosticsInterface::PopFunction(const std::string& funcname)
{
	if (CanLog() == false)
	{
		return;
	}

	Verify(m_Stack.size() > 0);
	Verify(m_Stack.back()->GetText() == funcname);

	m_Stack.pop_back();

	if (m_Stack.size() == 0)
	{
		if (m_Root.GetPointer() != 0)
		{
			m_BufferedSpew = "";
			m_Root->SpewToString(m_BufferedSpew);
		}
	}
}

void DiagnosticsInterface::SetEnabled(bool enabled)
{
	#ifdef LAB_ONLY
	m_Enabled = enabled;
	#endif
}

bool DiagnosticsInterface::GetEnabled() const
{
	return (m_Enabled);
}

void DiagnosticsInterface::SetFileAndLineEnabled(bool enabled)
{
	m_FileAndLineEnabled = enabled;
}

bool DiagnosticsInterface::GetFileAndLineEnabled() const
{
	return (m_FileAndLineEnabled);
}

void DiagnosticsInterface::SetParameterNamesEnabled(bool enabled)
{
	m_ParameterNamesEnabled = enabled;
}

bool DiagnosticsInterface::GetParameterNamesEnabled() const
{
	return (m_ParameterNamesEnabled);
}

void DiagnosticsInterface::SetFileSpewing(bool enabled)
{
	m_FileSpewing = enabled;
}

bool DiagnosticsInterface::GetFileSpewing() const
{
	return (m_FileSpewing);
}

void DiagnosticsInterface::GetSpew(std::string& output) const
{
	output = m_BufferedSpew;
}

Adept::Entity* GetPlayerVehicle()
{
	if (Adept::Player::GetInstance() == 0)
	{
		return (0);
	}

	MechWarrior4::MWPlayer* p = Cast_Object(MechWarrior4::MWPlayer*,Adept::Player::GetInstance());
	if (p == 0)
	{
		return (0);
	}

	Check_Object(p);
	if (p->GetInterface() == 0)
	{
		return (0);
	}

	return (p->GetInterface()->vehicle);
}

void DiagnosticsInterface::SetExecutingObject(MechWarrior4::MWObject* object)
{
	m_ExecutingObject = object;

	if ((m_ExecutingObject != 0) &&
		(GetPlayerVehicle() != 0) &&
		(m_ExecutingObject == GetPlayerVehicle()))
	{
		Reset();
	}
}

bool DiagnosticsInterface::CanLog() const
{
	if ((GetEnabled() == false) ||
		(m_ExecutingObject == 0) ||
		(GetPlayerVehicle() == 0))
	{
		return (false);
	}

	return (m_ExecutingObject == GetPlayerVehicle());
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

LogFunction::LogFunction(const std::string& name, const std::string& file, int line)
	: m_Name(name)
{
	DiagnosticsInterface::GetInstance().PushFunction(m_Name,file,line);
}

LogFunction::~LogFunction()
{
	DiagnosticsInterface::GetInstance().PopFunction(m_Name);
}
