/*
bot.cpp 
JNI implementation. 
Please see the java file and the readme for more information.
*/

#include "dll/stdafx.h"
#include "dll/enums.h"      // Contains all important enums and constants
#include "bot/bot.h"


#include <sstream>
#include <iostream>
#include <windows.h>

// allow static jni callback functions to access bot
Bot *myBot;

Bot::Bot()
{	
	m_jni_loaded = false;
	debug = false;

	// If you want to change the name or location of your bot or java files, apply these changes here.
	// These are default values which can be overwritten by commandline options passed to DEFCON.
	m_javaClassPath = "";
	m_javaClassName = "javabot/JBot"; // Format: package/package/class
	m_javaVersion = JNI_VERSION_1_6;
	myBot = this;
}

// Initialise
// Called when the bot is initialized (every time it is selected from the "Select Bot"
// dropdown in the lobby.
bool Bot::Initialise(Funct *_functions, std::vector<std::vector<std::string>> _commandLineOptions)
{
	std::cout << "init" << std::endl;
	// The f object is an object of the Funct class, containing all the functions exposed by DEFCON.
	m_game = _functions;

	// select commandline options that specify location of java bot
	for (unsigned int i = 0; i < _commandLineOptions.size(); i++)
	{
		if (_commandLineOptions[i][0] == "javaclasspath")
		{
			m_javaClassPath = _commandLineOptions[i][1];
		}
		else if (_commandLineOptions[i][0] == "javaclassname")
		{
			m_javaClassName = _commandLineOptions[i][1];
		}
		else if (_commandLineOptions[i][0] == "javaversion")
		{
			if (_commandLineOptions[i][1] == "1.6")
				m_javaVersion = JNI_VERSION_1_6;
			else if (_commandLineOptions[i][1] == "1.4")
				m_javaVersion = JNI_VERSION_1_4;
			else if (_commandLineOptions[i][1] == "1.2")
				m_javaVersion = JNI_VERSION_1_2;
			else if (_commandLineOptions[i][1] == "1.1")
				m_javaVersion = JNI_VERSION_1_1;
		} 
		else if (_commandLineOptions[i][0] == "debug")
		{
			debug = true;
		}
	}

	if (!m_jni_loaded)
	{
		if (!InitJavaDriver())
			return false;
		else
		{
			// call java

			jobjectArray row = (jobjectArray)m_javaEnv->NewObjectArray(2,  
				m_javaEnv->FindClass("java/lang/String"),  
				m_javaEnv->NewStringUTF(""));
			jobjectArray ret = (jobjectArray)m_javaEnv->NewObjectArray(
				_commandLineOptions.size(), m_javaEnv->GetObjectClass(row), 0);

			for(unsigned int i = 0; i < _commandLineOptions.size(); i++) {
				m_javaEnv->SetObjectArrayElement(row, 0, m_javaEnv->NewStringUTF(_commandLineOptions[i][0].c_str()));
				m_javaEnv->SetObjectArrayElement(row, 1, m_javaEnv->NewStringUTF(_commandLineOptions[i][1].c_str()));
				m_javaEnv->SetObjectArrayElement(ret, i, row);
				row = (jobjectArray)m_javaEnv->NewObjectArray(2,  
					m_javaEnv->FindClass("java/lang/String"),  
					m_javaEnv->NewStringUTF(""));
			}

			jmethodID mid = m_javaEnv->GetStaticMethodID(m_javaClass, "initialise", "([[Ljava/lang/String;)Z");
			std::cout << "Calling intialise: " << mid << std::endl;
			
			jboolean b = m_javaEnv->CallBooleanMethod(m_javaClass, mid, ret);
			std::cout << "Native call to initialise finished" << std::endl;
			if (m_javaEnv->ExceptionCheck() == JNI_TRUE) {
				std::cout << "Exception thrown: " << std::endl;
				m_javaEnv->ExceptionDescribe();
			}
			return b == JNI_TRUE;
		}
	}
	return true;
}

// Callback functions

JNIEXPORT jint JNICALL jGetDefcon(JNIEnv *, jclass)
{
	return myBot->m_game->GetDefcon();
}

JNIEXPORT jfloat JNICALL jGetGameTime(JNIEnv *, jclass)
{
	return myBot->m_game->GetGameTime();
}

JNIEXPORT jint JNICALL jGetGameSpeed(JNIEnv *, jclass)
{
	return myBot->m_game->GetGameSpeed();
}

JNIEXPORT jint JNICALL jGetGameTick(JNIEnv *, jclass)
{
	return myBot->m_game->GetGameTick();
}

JNIEXPORT jfloat JNICALL jGetVictoryTimer(JNIEnv *, jclass)
{
	return myBot->m_game->GetVictoryTimer();
}

JNIEXPORT jboolean JNICALL jIsVictoryTimerActive(JNIEnv *, jclass)
{
	return myBot->m_game->IsVictoryTimerActive();
}

JNIEXPORT jint JNICALL jGetOptionValue(JNIEnv *env, jclass, jstring options)
{
	int strlength = env->GetStringLength(options);
	char* str = new char[strlength + 1];
	//memset(str, 0, strlength + 1);
	const char* original = env->GetStringUTFChars(options, false);
	strcpy(str, original);
	env->ReleaseStringUTFChars(options, original);
	int return_value = myBot->m_game->GetOptionValue(str);
	delete[] str;

	return return_value;
}

JNIEXPORT jintArray JNICALL jGetCityIds(JNIEnv *env, jclass)
{
	std::vector<int> result = myBot->m_game->GetCityIds();

	jint* converted = new jint[result.size()];

	for (unsigned int i = 0; i < result.size(); ++i)
		converted[i] = result[i];

	jintArray arr = env->NewIntArray( result.size() );

	if (result.size() > 0)
		env->SetIntArrayRegion( arr, 0, result.size(), converted );

	delete[] converted;
	return arr;
}

JNIEXPORT jint JNICALL jGetCityPopulation(JNIEnv *, jclass, jint _cityId)
{
	return myBot->m_game->GetCityPopulation(_cityId);
}

JNIEXPORT jint JNICALL jGetRemainingPopulation(JNIEnv *, jclass, jint _cityId)
{
	return myBot->m_game->GetRemainingPopulation(_cityId);
}

JNIEXPORT jboolean JNICALL jIsValidTerritory(JNIEnv *, jclass, jint _teamId, jfloat _longitude, jfloat _latitude, jboolean seaArea)
{
	return myBot->m_game->IsValidTerritory(_teamId, _longitude, _latitude, seaArea == JNI_TRUE);
}

JNIEXPORT jboolean JNICALL jIsBorder(JNIEnv *, jclass, jfloat _longitude, jfloat _latitude)
{
	return myBot->m_game->IsBorder(_longitude, _latitude);
}

JNIEXPORT jint JNICALL jGetTerritoryId(JNIEnv *, jclass, jfloat _longitude, jfloat _latitude)
{
	return myBot->m_game->GetTerritoryId(_longitude, _latitude);
}

JNIEXPORT jint JNICALL jGetOwnTeamId(JNIEnv *, jclass)
{
	return myBot->m_game->GetOwnTeamId();
}

JNIEXPORT jintArray JNICALL jGetTeamIds(JNIEnv *env, jclass)
{
	std::vector<int> result = myBot->m_game->GetTeamIds();
	jint* converted = new jint[result.size()];

	for (unsigned int i = 0; i < result.size(); ++i)
		converted[i] = result[i];

	jintArray arr = env->NewIntArray( result.size() );

	if (result.size() > 0)
		env->SetIntArrayRegion( arr, 0, result.size(), converted );

	delete[] converted;

	return arr;
}

JNIEXPORT jint JNICALL jGetTeamTerritoriesCount(JNIEnv *env, jclass, jint _teamId)
{
	return myBot->m_game->GetTeamTerritories(_teamId).size();
}

JNIEXPORT jintArray JNICALL jGetTeamTerritories(JNIEnv *env, jclass, jint _teamId)
{
	std::vector<int> result = myBot->m_game->GetTeamTerritories(_teamId);
	jint* converted = new jint[result.size()];

	for (unsigned int i = 0; i < result.size(); ++i)
		converted[i] = result[i];

	jintArray arr = env->NewIntArray( result.size() );

	if (result.size() > 0)
		env->SetIntArrayRegion( arr, 0, result.size(), converted );

	delete[] converted;

	return arr;
}

JNIEXPORT jint JNICALL jGetAllianceId(JNIEnv *, jclass, jint _teamId)
{
	return myBot->m_game->GetAllianceId(_teamId);
}

JNIEXPORT jint JNICALL jGetDesiredGameSpeed(JNIEnv *, jclass, jint _teamId)
{
	return myBot->m_game->GetDesiredGameSpeed(_teamId);
}

JNIEXPORT jint JNICALL jGetEnemyKills(JNIEnv *, jclass, jint _teamId)
{
	return myBot->m_game->GetEnemyKills(_teamId);
}

JNIEXPORT jint JNICALL jGetFriendlyDeaths(JNIEnv *, jclass, jint _teamId)
{
	return myBot->m_game->GetFriendlyDeaths(_teamId);
}

JNIEXPORT jint JNICALL jGetCollateralDamage(JNIEnv *, jclass, jint _teamId)
{
	return myBot->m_game->GetCollateralDamage(_teamId);
}

JNIEXPORT jstring JNICALL jGetTeamName(JNIEnv *env, jclass, jint _teamId)
{
	return env->NewStringUTF(myBot->m_game->GetTeamName(_teamId));
}

JNIEXPORT jboolean JNICALL jIsSharingRadar(JNIEnv *, jclass, jint _teamId1, jint _teamId2)
{
	return myBot->m_game->IsSharingRadar(_teamId1, _teamId2);
}

JNIEXPORT jboolean JNICALL jIsCeaseFire(JNIEnv *, jclass, jint _teamId1, jint _teamId2)
{
	return myBot->m_game->IsCeaseFire(_teamId1, _teamId2);
}

JNIEXPORT void JNICALL jRequestAlliance(JNIEnv *, jclass, jint _allianceId)
{
	myBot->m_game->RequestAlliance(_allianceId);
}

JNIEXPORT void JNICALL jRequestCeaseFire(JNIEnv *, jclass, jint _teamId)
{
	myBot->m_game->RequestCeaseFire(_teamId);
}

JNIEXPORT void JNICALL jRequestShareRadar(JNIEnv *, jclass, jint _teamId)
{
	myBot->m_game->RequestShareRadar(_teamId);
}

JNIEXPORT void JNICALL jRequestGameSpeed(JNIEnv *, jclass, jint _requestedSpeedIdentifier)
{
	myBot->m_game->RequestGameSpeed(_requestedSpeedIdentifier);
}

JNIEXPORT jintArray JNICALL jGetAllUnits(JNIEnv *env, jclass)
{
	std::vector<int> result = myBot->m_game->GetAllUnits();
	jint* converted = new jint[result.size()];

	for (unsigned int i = 0; i < result.size(); ++i)
		converted[i] = result[i];

	jintArray arr = env->NewIntArray( result.size() );

	if (result.size() > 0)
		env->SetIntArrayRegion( arr, 0, result.size(), converted );

	delete[] converted;
	return arr;
}

JNIEXPORT jintArray JNICALL jGetOwnUnits(JNIEnv *env, jclass)
{
	std::vector<int> result = myBot->m_game->GetOwnUnits();
	jint* converted = new jint[result.size()];

	for (unsigned int i = 0; i < result.size(); ++i)
		converted[i] = result[i];

	jintArray arr = env->NewIntArray( result.size() );

	if (result.size() > 0)
		env->SetIntArrayRegion( arr, 0, result.size(), converted );

	delete[] converted;
	return arr;
}

JNIEXPORT jintArray JNICALL jGetTeamUnits(JNIEnv *env, jclass, jint _teamId)
{
	std::vector<int> result = myBot->m_game->GetTeamUnits(_teamId);
	jint* converted = new jint[result.size()];

	for (unsigned int i = 0; i < result.size(); ++i)
		converted[i] = result[i];

	jintArray arr = env->NewIntArray( result.size() );

	if (result.size() > 0)
		env->SetIntArrayRegion( arr, 0, result.size(), converted );

	delete[] converted;
	return arr;
}

JNIEXPORT jfloatArray JNICALL j_GetAllUnitData(JNIEnv *env, jclass)
{
	std::vector<Funct::UnitData *> result = myBot->m_game->GetAllUnitData();
	jfloatArray arr = env->NewFloatArray( result.size() * 7 );

	for (unsigned int i = 0; i < result.size(); i++)
	{
		jfloat tmp = (float) result[i]->m_objectId;
		env->SetFloatArrayRegion(arr, i * 7, 1, &tmp);
		tmp = (float) result[i]->m_type;
		env->SetFloatArrayRegion(arr, i * 7 + 1, 1, &tmp); 
		tmp = (float) result[i]->m_teamId;
		env->SetFloatArrayRegion(arr, i * 7 + 2, 1, &tmp); 
		tmp = (float) result[i]->m_currentState;
		env->SetFloatArrayRegion(arr, i * 7 + 3, 1, &tmp); 
		tmp = result[i]->m_visible ? 1.0f : 0.0f;
		env->SetFloatArrayRegion(arr, i * 7 + 4, 1, &tmp); 
		tmp = result[i]->m_longitude;
		env->SetFloatArrayRegion(arr, i * 7 + 5, 1, &tmp); 
		tmp = result[i]->m_latitude;
		env->SetFloatArrayRegion(arr, i * 7 + 6, 1, &tmp); 
	}
	return arr;
}

JNIEXPORT jint JNICALL jGetType(JNIEnv *, jclass, jint _id)
{
	return myBot->m_game->GetType(_id);
}

JNIEXPORT jint JNICALL jGetTeamId(JNIEnv *, jclass, jint _id)
{
	return myBot->m_game->GetTeamId(_id);
}

JNIEXPORT jintArray JNICALL jGetOwnFleets(JNIEnv *env, jclass)
{
	std::vector<int> result = myBot->m_game->GetOwnFleets();
	jint* converted = new jint[result.size()];

	for (unsigned int i = 0; i < result.size(); ++i)
		converted[i] = result[i];

	jintArray arr = env->NewIntArray( result.size() );

	if (result.size() > 0)
		env->SetIntArrayRegion( arr, 0, result.size(), converted );

	delete[] converted;
	return arr;
}

JNIEXPORT jintArray JNICALL jGetFleets(JNIEnv *env, jclass, jint _teamId)
{
	std::vector<int> result = myBot->m_game->GetFleets(_teamId);
	jint* converted = new jint[result.size()];

	for (unsigned int i = 0; i < result.size(); ++i)
		converted[i] = result[i];

	jintArray arr = env->NewIntArray( result.size() );

	if (result.size() > 0)
		env->SetIntArrayRegion( arr, 0, result.size(), converted );

	delete[] converted;
	return arr;
}

JNIEXPORT jintArray JNICALL jGetFleetMembers(JNIEnv *env, jclass, jint _teamId)
{
	std::vector<int> result = myBot->m_game->GetFleetMembers(_teamId);
	jint* converted = new jint[result.size()];

	for (unsigned int i = 0; i < result.size(); ++i)
		converted[i] = result[i];

	jintArray arr = env->NewIntArray( result.size() );

	if (result.size() > 0)
		env->SetIntArrayRegion( arr, 0, result.size(), converted );

	delete[] converted;
	return arr;
}

JNIEXPORT jint JNICALL jGetFleetId(JNIEnv *, jclass, jint _unitId)
{
	return myBot->m_game->GetFleetId(_unitId);
}

JNIEXPORT jint JNICALL jGetCurrentState(JNIEnv *, jclass, jint _unitId)
{
	return myBot->m_game->GetCurrentState(_unitId);
}

JNIEXPORT jint JNICALL jGetStateCount(JNIEnv *, jclass, jint _unitId, jint _stateId)
{
	return myBot->m_game->GetStateCount(_unitId, _stateId);
}

JNIEXPORT jintArray JNICALL jGetActionQueue(JNIEnv *env, jclass, jint _unitId)
{
	std::vector<int> result = myBot->m_game->GetActionQueue(_unitId);
	jint* converted = new jint[result.size()];

	for (unsigned int i = 0; i < result.size(); ++i)
		converted[i] = result[i];

	jintArray arr = env->NewIntArray( result.size() );

	if (result.size() > 0)
		env->SetIntArrayRegion( arr, 0, result.size(), converted );

	delete[] converted;
	return arr;
}

JNIEXPORT jfloat JNICALL jGetStateTimer(JNIEnv *, jclass, jint _unitId)
{
	return myBot->m_game->GetStateTimer(_unitId);
}

JNIEXPORT jint JNICALL jGetCurrentTargetId(JNIEnv *, jclass, jint _unitId)
{
	return myBot->m_game->GetCurrentTargetId(_unitId);
}

JNIEXPORT jfloatArray JNICALL jGetMovementTargetLocation(JNIEnv *env, jclass, jint _unitId)
{
	std::vector<float> result = myBot->m_game->GetMovementTargetLocation(_unitId);
	jfloat* converted = new jfloat[result.size()];

	for (unsigned int i = 0; i < result.size(); ++i)
		converted[i] = result[i];

	jfloatArray arr = env->NewFloatArray( result.size() );

	if (result.size() > 0)
		env->SetFloatArrayRegion( arr, 0, result.size(), converted );

	delete[] converted;
	return arr;
}

JNIEXPORT jint JNICALL jGetNukeSupply(JNIEnv *, jclass, jint _unitId)
{
	return myBot->m_game->GetNukeSupply(_unitId);
}

JNIEXPORT jfloatArray JNICALL jGetBomberNukeTarget(JNIEnv *env, jclass, jint _unitId)
{
	std::vector<float> result = myBot->m_game->GetBomberNukeTarget(_unitId);
	jfloat* converted = new jfloat[result.size()];

	for (unsigned int i = 0; i < result.size(); ++i)
		converted[i] = result[i];

	jfloatArray arr = env->NewFloatArray( result.size() );

	if (result.size() > 0)
		env->SetFloatArrayRegion( arr, 0, result.size(), converted );

	delete[] converted;
	return arr;
}

JNIEXPORT jboolean JNICALL jIsRetaliating(JNIEnv *, jclass, jint _unitId)
{
	return myBot->m_game->IsRetaliating(_unitId);
}

JNIEXPORT jboolean JNICALL jIsVisible(JNIEnv *, jclass, jint _unitId, jint _byTeamId )
{
	return myBot->m_game->IsVisible(_unitId, _byTeamId);
}

JNIEXPORT void JNICALL jSetState(JNIEnv *, jclass, jint _unitId, jint _stateId)
{
	return myBot->m_game->SetState(_unitId, _stateId);
}

JNIEXPORT void JNICALL jSetMovementTarget(JNIEnv *, jclass, jint _unitId, jfloat _longitude, jfloat _latitude)
{
	return myBot->m_game->SetMovementTarget(_unitId, _longitude, _latitude);
}

JNIEXPORT void JNICALL jSetActionTarget(JNIEnv *, jclass, jint _unitId, jint _targetUnitId, jfloat _longitude, jfloat _latitude)
{
	return myBot->m_game->SetActionTarget(_unitId, _targetUnitId, _longitude, _latitude);
}

JNIEXPORT void JNICALL jSetLandingTarget(JNIEnv *, jclass, jint _unitId, jint _targetUnitId)
{
	return myBot->m_game->SetLandingTarget(_unitId, _targetUnitId);
}

JNIEXPORT jfloat JNICALL jGetLongitude(JNIEnv *, jclass, jint _id)
{
	return myBot->m_game->GetLongitude(_id);
}

JNIEXPORT jfloat JNICALL jGetLatitude(JNIEnv *, jclass, jint _id)
{
	return myBot->m_game->GetLatitude(_id);
}

JNIEXPORT jfloatArray JNICALL jGetVelocity(JNIEnv *env, jclass, jint _unitId)
{
	std::vector<float> result = myBot->m_game->GetVelocity(_unitId);
	jfloat* converted = new jfloat[result.size()];

	for (unsigned int i = 0; i < result.size(); ++i)
		converted[i] = result[i];

	jfloatArray arr = env->NewFloatArray( result.size() );

	if (result.size() > 0)
		env->SetFloatArrayRegion( arr, 0, result.size(), converted );

	delete[] converted;
	return arr;
}

JNIEXPORT jfloat JNICALL jGetRange(JNIEnv *, jclass, jint _unitId)
{
	return myBot->m_game->GetRange(_unitId);
}

JNIEXPORT jint JNICALL jGetRemainingUnits(JNIEnv *, jclass, jint _typeId)
{
	return myBot->m_game->GetRemainingUnits(_typeId);
}

JNIEXPORT jboolean JNICALL jIsValidPlacementLocation(JNIEnv *, jclass, jfloat _longitude, jfloat _latitude, jint _typeId )
{
	return myBot->m_game->IsValidPlacementLocation(_longitude, _latitude, _typeId);
}

JNIEXPORT jfloatArray JNICALL jGetFleetMemberOffset(JNIEnv *env, jclass, jint _memberCount, jint _memberId)
{
	std::vector<float> result = myBot->m_game->GetFleetMemberOffset(_memberCount, _memberId);
	jfloat* converted = new jfloat[result.size()];

	for (unsigned int i = 0; i < result.size(); ++i)
		converted[i] = result[i];

	jfloatArray arr = env->NewFloatArray( result.size() );

	if (result.size() > 0)
		env->SetFloatArrayRegion( arr, 0, result.size(), converted );

	delete[] converted;
	return arr;
}

JNIEXPORT void JNICALL jPlaceStructure(JNIEnv *, jclass, jint _typeId, jfloat _longitude, jfloat _latitude)
{
	myBot->m_game->PlaceStructure(_typeId, _longitude, _latitude);
}

JNIEXPORT void JNICALL jPlaceFleet(JNIEnv *env, jclass, jfloat _longitude, jfloat _latitude,
						  jintArray _shipTypes )
{	
	jsize length = env->GetArrayLength(_shipTypes);

	if (length == 0)
		return;

	jint *buf = env->GetIntArrayElements(_shipTypes, 0);
	/*
	std::cout << "Placing fleet set-up with these units: ";

	for (int i = 0; i < length; ++i) {
		std::cout << *(buf + i) << ", ";
	}
	std::cout << std::endl;
	*/
	switch (length) {
		case 1:
			myBot->m_game->PlaceFleet(_longitude, _latitude, *buf);
			break;
		case 2:
			myBot->m_game->PlaceFleet(_longitude, _latitude, *buf, *(buf + 1));
			break;
		case 3:
			myBot->m_game->PlaceFleet(_longitude, _latitude, *buf, *(buf + 1), *(buf + 2));
			break;
		case 4:
			myBot->m_game->PlaceFleet(_longitude, _latitude, *buf, *(buf + 1), *(buf + 2), *(buf + 3));
			break;
		case 5:
			myBot->m_game->PlaceFleet(_longitude, _latitude, *buf, *(buf + 1), *(buf + 2), *(buf + 3), *(buf + 4));
			break;
		case 6:
			myBot->m_game->PlaceFleet(_longitude, _latitude, *buf, *(buf + 1), *(buf + 2), *(buf + 3), *(buf + 4), *(buf + 5));
			break;
	}	
	env->ReleaseIntArrayElements(_shipTypes, buf, 0);
}

JNIEXPORT jint JNICALL jGetUnitCredits(JNIEnv *, jclass)
{
	return myBot->m_game->GetUnitCredits();
}

JNIEXPORT jint JNICALL jGetUnitValue(JNIEnv *, jclass, jint _typeId)
{
	return myBot->m_game->GetUnitValue(_typeId);
}

JNIEXPORT void JNICALL jSendVote(JNIEnv *, jclass, jint _voteId, jint _vote )
{
	myBot->m_game->SendVote(_voteId, _vote);
}

JNIEXPORT void JNICALL jSendChatMessage(JNIEnv *env, jclass, jstring _chatMessage, jint _receiverId)
{
	const char* tmp = env->GetStringUTFChars(_chatMessage, 0);
	myBot->m_game->SendChatMessage(tmp, _receiverId);
	env->ReleaseStringUTFChars(_chatMessage, tmp);
}

JNIEXPORT jfloat JNICALL jGetDistance(JNIEnv *, jclass, jfloat _longitude1, jfloat _latitude1, jfloat _longitude2, jfloat _latitude2 )
{
	return myBot->m_game->GetDistance( _longitude1,  _latitude1,  _longitude2,  _latitude2 );
}

JNIEXPORT jfloat JNICALL jGetSailDistance(JNIEnv *, jclass, jfloat _longitude1, jfloat _latitude1, jfloat _longitude2, jfloat _latitude2 )
{
	return myBot->m_game->GetSailDistance( _longitude1,  _latitude1,  _longitude2,  _latitude2 );
}

JNIEXPORT void JNICALL jDebugLog(JNIEnv *env, jclass o, jstring _entry, jint _objectId, jstring _tags, jint _colorR, jint _colorG, jint _colorB, jint _colorAlpha)
{
	const char* tmp = env->GetStringUTFChars(_entry, 0);
	const char* tmp2 = env->GetStringUTFChars(_tags, 0);
	myBot->m_game->DebugLog(tmp, _objectId, tmp2, 
		(unsigned char) _colorR, (unsigned char) _colorG, (unsigned char) _colorB, (unsigned char) _colorAlpha);
	env->ReleaseStringUTFChars(_entry, tmp);
	env->ReleaseStringUTFChars(_tags, tmp2);
}

JNIEXPORT void JNICALL jDebugLog1(JNIEnv *env, jclass o, jstring _entry)
{
	jDebugLog(env, o, _entry, -1, env->NewStringUTF(""), 255, 255, 255, 255);
}

JNIEXPORT void JNICALL jDebugLog2(JNIEnv *env, jclass o, jstring _entry, jint _objectId)
{
	jDebugLog(env, o, _entry, _objectId, env->NewStringUTF(""), 255, 255, 255, 255);
}

JNIEXPORT void JNICALL jDebugLog3(JNIEnv *env, jclass o, jstring _entry, jint _objectId, jstring _tags)
{
	jDebugLog(env, o, _entry, _objectId, _tags, 255, 255, 255, 255);
}

JNIEXPORT void JNICALL jDebugLog4(JNIEnv *env, jclass o, jstring _entry, jint _objectId, jstring _tags, jint _colorR, jint _colorG, jint _colorB)
{
	jDebugLog(env, o, _entry, _objectId, _tags, _colorR, _colorG, _colorB, 255);
}

JNIEXPORT jboolean JNICALL jDebugIsReplayingGame(JNIEnv *, jclass )
{
	return myBot->m_game->DebugIsReplayingGame();
}

JNIEXPORT void JNICALL jWhiteboardDraw(JNIEnv *, jclass, jfloat _longitude1, jfloat _latitude1, jfloat _longitude2, jfloat _latitude2 )
{
	return myBot->m_game->WhiteboardDraw( _longitude1,  _latitude1,  _longitude2,  _latitude2 );
}

JNIEXPORT void JNICALL jWhiteboardClear(JNIEnv *, jclass)
{
	return myBot->m_game->WhiteboardClear( );
}

JNIEXPORT jintArray JNICALL jGetSuccessfulCommands(JNIEnv *env, jclass)
{
	std::vector<int> result = myBot->m_game->GetSuccessfulCommands();
	jint* converted = new jint[result.size()];

	for (unsigned int i = 0; i < result.size(); ++i)
		converted[i] = result[i];

	jintArray arr = env->NewIntArray( result.size() );

	if (result.size() > 0)
		env->SetIntArrayRegion( arr, 0, result.size(), converted );

	delete[] converted;
	return arr;
}

JNIEXPORT void JNICALL jWriteToConsole(JNIEnv *env, jclass, jstring _logLine)
{
	if (_logLine == NULL)
		return;

	const char *str;
	str = env->GetStringUTFChars(_logLine, NULL);
	if (str == NULL) {
		return; // OutOfMemoryError already thrown
	}
	std::cout << str << std::endl;
	env->ReleaseStringUTFChars(_logLine, str);
}


std::wstring getClassPath(const wchar_t * path)
{
	std::wstringstream class_path;

	WIN32_FIND_DATA fdata;
	HANDLE dhandle;

	wchar_t buf[MAX_PATH + 2];
	memset(buf, 0, MAX_PATH + 2);

	_snwprintf(buf, sizeof(buf), L"%s/*.*", path);

	if((dhandle = FindFirstFile(buf, &fdata)) == INVALID_HANDLE_VALUE) {
		wprintf(L"FindFirstFile %s failed\n", buf);
		throw "FindFirstFile";
	}

	while(true) {
		int length = wcslen(fdata.cFileName);
		if ( length != 0 &&
			(length != 1 || wcscmp(fdata.cFileName, L".") != 0) &&
			(length != 2 || wcscmp(fdata.cFileName, L"..") != 0) ) {
				if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
					_snwprintf(buf, sizeof(buf), L"%s/%s", path, fdata.cFileName);

					std::wstring prep;
					prep = getClassPath(buf);

					if (prep.length() > 0)
					{
						class_path << prep;
					}
				} else {
					wchar_t* tmp = wcsstr(fdata.cFileName, L".jar");
					if (tmp != NULL && wcslen(tmp) == 4)
					{
						_snwprintf(buf, sizeof(buf), L"%s/%s;", path, fdata.cFileName);

						if (wcslen(buf) > 0) {
							class_path << buf;
						}
					}
				}
		}
		if (!FindNextFile(dhandle, &fdata)) {
			if(GetLastError() == ERROR_NO_MORE_FILES) {
				break;
			} else {
				FindClose(dhandle);
				std::cout << "getClassPath for failed" << std::endl;
				throw "FindNextFile";
			}
		}
	}
	if(FindClose(dhandle) == 0) {
		std::cout << "FindClose(dhandle) == 0";
		throw "FindClose";
	}
	class_path.flush();
	std::wstring output = class_path.str();
	class_path.clear();

	return output;
}

std::wstring getClassPath()
{
	return getClassPath(L"AI/javabot/source/java");
}

// set up the java virtual machine
bool Bot::InitJavaDriver()
{
	std::cout << "InitJavaDriver" << std::endl;
	// java initialisation arguments
	JavaVMInitArgs vm_args; 	
		
	std::wstring wconstructed_class_path = getClassPath();
	
	int max_len = (wconstructed_class_path.length()) * sizeof(wchar_t) + sizeof(char);
	char* constructed_class_path = new char[max_len];

	wcstombs(constructed_class_path, wconstructed_class_path.c_str(), max_len); 
	int class_path_length = 19 + strlen(constructed_class_path) + 1;
	char* path;
	if (!m_javaClassPath.empty()) {
		class_path_length += m_javaClassPath.length() + 1; // + 1 is for ;
		path = new char[class_path_length];
		memset(path, 0, class_path_length);
		sprintf(path, "-Djava.class.path=%s;%s", constructed_class_path, m_javaClassPath);				
	} else {
		path = new char[class_path_length];
		memset(path, 0, class_path_length);
		sprintf(path, "-Djava.class.path=%s", constructed_class_path);
	}
	std::cout << "ClassPath: ";
	std::cout << path << " Length: " << class_path_length << std::endl;

	JavaVMOption* options = (debug) ? new JavaVMOption[3] : new JavaVMOption[2];

	options[0].optionString = path;
	options[1].optionString = "-verbose:jni";                   /* print JNI-related messages */
	if (debug)
		options[2].optionString = "-agentlib:jdwp=transport=dt_socket,server=y,address=8000";
	// add more options here if desired.

	vm_args.version = JNI_VERSION_1_6;
	vm_args.nOptions = (debug) ? 3 : 2;
	vm_args.options = options;
	vm_args.ignoreUnrecognized = false;
	/* load and initialize a Java VM, return a JNI interface
	* pointer in m_javaEnv */
	jint res;
	res = JNI_CreateJavaVM(&m_jvm, (void **)&m_javaEnv, &vm_args);
	
	delete[] path;
	delete[] options;
	delete[] constructed_class_path;

	std::cout << "Finished CreateJavaVM: " << res << std::endl;
	if (res < 0) {
		fprintf(stderr, "Can't create Java VM\n");
		return false;
	}

	// find class
	m_javaClass = m_javaEnv->FindClass(m_javaClassName.c_str());
	if (m_javaClass == NULL) {
		if (m_javaEnv->ExceptionOccurred()) {
			m_javaEnv->ExceptionDescribe();
		}
		return false;
	}

	// Register callback functions with java
	JNINativeMethod methods[] = {  
		// name, signature, function pointer  
		{ "GetGameTick", "()I", &jGetGameTick },
		{ "GetGameSpeed", "()I", &jGetGameSpeed },
		{ "GetDefcon", "()I", &jGetDefcon },
		{ "SendChatMessage", "(Ljava/lang/String;I)V", &jSendChatMessage },
		{ "GetGameTime", "()F", &jGetGameTime },
		{ "GetVictoryTimer", "()F", &jGetVictoryTimer },
		{ "IsVictoryTimerActive", "()Z", &jIsVictoryTimerActive },
		{ "GetOptionValue", "(Ljava/lang/String;)I", &jGetOptionValue },
		{ "GetCityIds", "()[I", &jGetCityIds },
		{ "GetCityPopulation", "(I)I", &jGetCityPopulation },
		{ "GetRemainingPopulation", "(I)I", &jGetRemainingPopulation },
		{ "IsValidTerritory", "(IFFZ)Z", &jIsValidTerritory },
		{ "IsBorder", "(FF)Z", &jIsBorder },
		{ "WriteToConsole", "(Ljava/lang/String;)V", &jWriteToConsole },
		{ "GetTerritoryId", "(FF)I", &jGetTerritoryId },
		{ "GetOwnTeamId", "()I", &jGetOwnTeamId },
		{ "GetTeamIds", "()[I", &jGetTeamIds },
		{ "GetTeamTerritoriesCount", "(I)I", &jGetTeamTerritoriesCount },
		{ "GetTeamTerritories", "(I)[I", &jGetTeamTerritories },
		{ "GetAllianceId", "(I)I", &jGetAllianceId },
		{ "GetDesiredGameSpeed", "(I)I", &jGetDesiredGameSpeed },
		{ "GetEnemyKills", "(I)I", &jGetEnemyKills },
		{ "GetFriendlyDeaths", "(I)I", &jGetFriendlyDeaths },
		{ "GetCollateralDamage", "(I)I", &jGetCollateralDamage },
		{ "GetTeamName", "(I)Ljava/lang/String;", &jGetTeamName },
		{ "IsSharingRadar", "(II)Z", &jIsSharingRadar },
		{ "IsCeaseFire", "(II)Z", &jIsCeaseFire },
		{ "RequestAlliance", "(I)V", &jRequestAlliance },
		{ "RequestCeaseFire", "(I)V", &jRequestCeaseFire },
		{ "RequestShareRadar", "(I)V", &jRequestShareRadar },
		{ "RequestGameSpeed", "(I)V", &jRequestGameSpeed },
		{ "GetAllUnits", "()[I", &jGetAllUnits },
		{ "GetOwnUnits", "()[I", &jGetOwnUnits },
		{ "GetTeamUnits", "(I)[I", &jGetTeamUnits },
		{ "_GetAllUnitData", "()[F", &j_GetAllUnitData },
		{ "GetType", "(I)I", &jGetType },
		{ "GetTeamId", "(I)I", &jGetTeamId },
		{ "GetOwnFleets", "()[I", &jGetOwnFleets },
		{ "GetFleets", "(I)[I", &jGetFleets },
		{ "GetFleetMembers", "(I)[I", &jGetFleetMembers },
		{ "GetFleetId", "(I)I", &jGetFleetId },
		{ "GetCurrentState", "(I)I", &jGetCurrentState },
		{ "GetStateCount", "(II)I", &jGetStateCount },
		{ "GetActionQueue", "(I)[I", &jGetActionQueue },
		{ "GetStateTimer", "(I)F", &jGetStateTimer },
		{ "GetCurrentTargetId", "(I)I", &jGetCurrentTargetId },
		{ "GetMovementTargetLocation", "(I)[F", &jGetMovementTargetLocation },
		{ "GetNukeSupply", "(I)I", &jGetNukeSupply },
		{ "GetBomberNukeTarget", "(I)[F", &jGetBomberNukeTarget },
		{ "IsRetaliating", "(I)Z", &jIsRetaliating },
		{ "IsVisible", "(II)Z", &jIsVisible },
		{ "SetState", "(II)V", &jSetState },
		{ "SetMovementTarget", "(IFF)V", &jSetMovementTarget },
		{ "SetActionTarget", "(IIFF)V", &jSetActionTarget },
		{ "SetLandingTarget", "(II)V", &jSetLandingTarget },
		{ "GetLongitude", "(I)F", &jGetLongitude },
		{ "GetLatitude", "(I)F", &jGetLatitude },
		{ "GetVelocity", "(I)[F", &jGetVelocity },
		{ "GetRange", "(I)F", &jGetRange },
		{ "GetRemainingUnits", "(I)I", &jGetRemainingUnits },
		{ "IsValidPlacementLocation", "(FFI)Z", &jIsValidPlacementLocation },
		{ "GetFleetMemberOffset", "(II)[F", &jGetFleetMemberOffset },
		{ "PlaceStructure", "(IFF)V", &jPlaceStructure },
		{ "PlaceFleet", "(FF[I)V", &jPlaceFleet },
		{ "GetUnitCredits", "()I", &jGetUnitCredits },
		{ "GetUnitValue", "(I)I", &jGetUnitValue },
		{ "SendVote", "(II)V", &jSendVote },
		{ "GetDistance", "(FFFF)F", &jGetDistance },
		{ "GetSailDistance", "(FFFF)F", &jGetSailDistance },
		{ "DebugLog", "(Ljava/lang/String;)V", &jDebugLog1 },
		{ "DebugLog", "(Ljava/lang/String;I)V", &jDebugLog2 },
		{ "DebugLog", "(Ljava/lang/String;ILjava/lang/String;)V", &jDebugLog3 },
		{ "DebugLog", "(Ljava/lang/String;ILjava/lang/String;III)V", &jDebugLog4 },
		{ "DebugLog", "(Ljava/lang/String;ILjava/lang/String;IIII)V", &jDebugLog },
		{ "DebugIsReplayingGame", "()Z", &jDebugIsReplayingGame },
		{ "WhiteboardDraw", "(FFFF)V", &jWhiteboardDraw },
		{ "WhiteboardClear", "()V", &jWhiteboardClear },
		{ "GetSuccessfulCommands", "()[I", &jGetSuccessfulCommands }
	}; 
	std::cout << "Registering native methods" << std::endl;
	m_javaEnv->RegisterNatives(m_javaClass, methods, 78);
	std::cout << "Native methods registered" << std::endl;

	m_jni_loaded = true;
	return true;
}

// Update
// Called every update cycle of the server, normally every 100ms.
// Most of the AI code should be called from here.
bool Bot::Update()
{
	// call java
	jmethodID mid = m_javaEnv->GetStaticMethodID(m_javaClass, "update", "()Z");
	jboolean b = m_javaEnv->CallBooleanMethod(m_javaClass, mid);

	if (m_javaEnv->ExceptionCheck() == JNI_TRUE) {
		std::cout << "Exception thrown: " << std::endl;
		m_javaEnv->ExceptionDescribe();
	}

	return b == JNI_TRUE;
}


// AddEvent
// Called by DEFCON whenever an event happens
void Bot::AddEvent(int _eventType, int _causeId, int _targetId, 
				   int _unitType, float _longitude, float _latitude)
{
	// call java
	jmethodID mid = m_javaEnv->GetStaticMethodID(m_javaClass, "addEvent", "(IIIIFF)V");
	m_javaEnv->CallVoidMethod(m_javaClass, mid, _eventType, _causeId, _targetId, _unitType, _longitude, _latitude);
}