#include "android_api.h"

#include "main_code.h"



#include <iostream>

#include "boost/asio.hpp"
#include "boost/bind.hpp"
#include "boost/shared_ptr.hpp"
#include "boost/shared_array.hpp"
#include "boost/array.hpp"
#include "boost/enable_shared_from_this.hpp"
#include "boost/thread.hpp"

#include <cstdlib>
#include <cstring>
#include <iostream>

#include "ClientSocket.h"



boost::asio::io_service IoService;

boost::thread IoServiceThread;

boost::shared_ptr<TClientSocket> ClientSocket;

boost::mutex connection_mutex;

std::vector<std::string> MessageHeaderArr;
std::vector<std::string> MessageTextArr;

std::vector<std::string> JavaStringHolder;

boost::shared_ptr<boost::asio::deadline_timer> HeartbeatTimer;
boost::shared_ptr<boost::asio::deadline_timer> MapHeartbeatTimer;

std::vector<TUserInfo> UserInfoMapArr;
std::vector<TUserInfo> UserInfoAlarmMapArr;

std::vector<TUserInfo>* CurrentUserInfoMapArr = &UserInfoMapArr;

float LastPosX = 0.f;
float LastPosY = 0.f;

int MapType = 0;

/*
void WriteToFile(std::string text)
{
std::string LogFilename = "/sdcard/hello.txt";
	
	FILE* f = fopen(LogFilename.c_str(), "a");
	
	if (f != NULL)
	{
		fputs(text.c_str(), f);
		fflush(f);
		fclose(f);
	}
}*/

void IoServiceRun()
{
	IoService.run();
	
	ClientSocket = boost::shared_ptr<TClientSocket>();
	
	IoService.reset();
	
	//WriteToFile("lolwaauat\n");
	
	
}



void OnAutorizationSent()
{
	//WriteToFile("OnAutorizationSent\n");
	
	RestartHeartbeatTimer();
	RestartMapHeartbeatTimer();

	connection_mutex.unlock();
}

void OnRecieveMapUpdate(std::vector<TUserInfo> userInfoArr)
{
	UserInfoMapArr = userInfoArr;
}

void OnRecieveAlarmMapUpdate(std::vector<TUserInfo> userInfoArr)
{
	UserInfoAlarmMapArr = userInfoArr;
}

void OnClosed()
{
//WriteToFile("OnClosed\n");
}

JavaVM* JavaVirtualMachine;

template<typename T>
inline std::string tostr(T i)
{
	std::ostringstream out;
	out << i;
	return std::string(out.str()); 
}


JNIEnv* jenv;

jclass clazzLUSCore;

struct OnReceiveMessage
{
void operator()(TUserInfo userInfo, std::string message)
{

	JNIEnv* j_env;
    JavaVirtualMachine->GetEnv((void**)&j_env, JNI_VERSION_1_4);

	std::string header = tostr<int>(userInfo.Uik) + " " + message;
	
	std::string status;
	
	if (userInfo.Status == 0)
	{
		status = "Наблюдатель";
	}
	else
	{
		status = "Мобильная группа";
	}
	
	std::string text = userInfo.LastName
		+ " " + userInfo.FirstName
		+ " " + userInfo.MiddleName
		+ " " + userInfo.PhoneNumber
		+ " " + status
		+ " УИК №" + tostr<int>(userInfo.Uik) 
		+ ": " + message;
		
		
	{
	boost::this_thread::disable_interruption di;
	
	MessageHeaderArr.push_back(header);
	MessageTextArr.push_back(text);
	}

}
};

void OnHeartbeat(const boost::system::error_code& e)
{

    if (e)
    {
        return;
    }
/*
    float posX = MainWindowPtr->GetUi()->PosXSpinBox->value();
    float posY = MainWindowPtr->GetUi()->PosYSpinBox->value();

    int uik = MainWindowPtr->GetUi()->UikSpinBox->value();
    int status = MainWindowPtr->GetUi()->StatusComboBox->currentIndex();
    int mapState = MainWindowPtr->GetUi()->MapStateComboBox->currentIndex() + 1;
    if (status == 1)
    {
        mapState = 0;
    }

    ClientSocket->UserInfo.Uik = uik;
    ClientSocket->UserInfo.MapState = mapState;

    ClientSocket->UserInfo.PosX = posX;
    ClientSocket->UserInfo.PosY = posY;
*/
    ClientSocket->SendHeartbeatThreaded();

    RestartHeartbeatTimer();
}

void OnMapHeartbeat(const boost::system::error_code& e)
{
    if (e)
    {
        return;
    }

    if (MapType == 0)
    {
        ClientSocket->SendMapQueryThreaded();
    }
    else
    {
        ClientSocket->SendAlarmMapQueryThreaded();
    }
	
    RestartMapHeartbeatTimer();
}

void RestartHeartbeatTimer()
{
    HeartbeatTimer = boost::shared_ptr<boost::asio::deadline_timer>(new boost::asio::deadline_timer(IoService, boost::posix_time::seconds(3)));

    HeartbeatTimer->async_wait(boost::bind(&OnHeartbeat, boost::asio::placeholders::error));

}

void RestartMapHeartbeatTimer()
{
    MapHeartbeatTimer = boost::shared_ptr<boost::asio::deadline_timer>(new boost::asio::deadline_timer(IoService, boost::posix_time::seconds(3)));

    MapHeartbeatTimer->async_wait(boost::bind(&OnMapHeartbeat, boost::asio::placeholders::error));

}


JNIEXPORT void JNICALL Java_fishrungames_kobserver_JniWrapper_Open(JNIEnv * env, jobject obj,  jstring addr, jstring port)
{

	int JVMResult;
	JVMResult = env->GetJavaVM(&JavaVirtualMachine);
	
	jenv = env;

static bool x = false;

if (!x)
{
x = true;

std::string LogFilename = "/sdcard/hello.txt";
	
	FILE* f = fopen(LogFilename.c_str(), "w");
	
	if (f != NULL)
	{
		fclose(f);
	}
} 
 const char *addr_c;
	
    addr_c = env->GetStringUTFChars(addr, NULL);
    if (addr_c == NULL)
	{
        return;
    }
	
	const char *port_c;
	
    port_c = env->GetStringUTFChars(port, NULL);
    if (port_c == NULL)
	{
        return;
    }
	
	//WriteToFile("Thread go  xxx\n");

	
	connection_mutex.lock();
	
	ClientSocket = boost::shared_ptr<TClientSocket>(new TClientSocket(IoService));
	
	ClientSocket->UserInfo.PosX = LastPosX;
	ClientSocket->UserInfo.PosY = LastPosY;
	
	ClientSocket->ReceiveAutorizationSentSignal.connect(boost::bind(&OnAutorizationSent));
	
	ClientSocket->ReceiveForceCloseSignal.connect(boost::bind(&OnClosed));
	
	ClientSocket->ReceiveMessageSignal.connect(OnReceiveMessage());
	
	
	
	ClientSocket->ReceiveMapSignal.connect(boost::bind(&OnRecieveMapUpdate, _1));
	ClientSocket->ReceiveAlarmMapSignal.connect(boost::bind(&OnRecieveAlarmMapUpdate, _1));
	

	
	
	ClientSocket->Open(std::string(addr_c), std::string(port_c));
	
	IoServiceThread = boost::thread(boost::bind(&IoServiceRun));
	
	//WriteToFile("Thread go\n");
	
	
	connection_mutex.lock();
	connection_mutex.unlock();
	
	//WriteToFile("Sleep over\n");
	
	jclass cls = env->FindClass("fishrungames/kobserver/JniWrapper");
    jmethodID mid = env->GetStaticMethodID(cls, "OnConnectSuccessful", "()V");
	
	clazzLUSCore = (_jclass*)env->NewGlobalRef(cls);

	env->DeleteLocalRef(cls);

    
	if (mid == NULL)
	{
        return;
    }
	
    env->CallStaticVoidMethod(cls, mid);

	
	env->ReleaseStringUTFChars(addr, addr_c);
    env->ReleaseStringUTFChars(port, port_c);
	
}	

JNIEXPORT void JNICALL Java_fishrungames_kobserver_JniWrapper_Close(JNIEnv * env, jobject obj)
{
	ClientSocket->CloseThreaded();
}


JNIEXPORT void JNICALL Java_fishrungames_kobserver_JniWrapper_UpdateUserInfo(JNIEnv * env, jobject obj
	, jstring lastName
	, jstring firstName
	, jstring middleName
	, jstring contactNumber
	, int mapState
	, int uik
	, int status
	, float posX
	, float posY)
{
	TUserInfo userInfo;
	
	const char *c_lastName;
	
    c_lastName = env->GetStringUTFChars(lastName, NULL);
    
	if (c_lastName == NULL)
	{
        return;
    }
	
	userInfo.LastName = std::string(c_lastName);
	
	env->ReleaseStringUTFChars(lastName, c_lastName);
	
	
	// ==== First Name
	const char *c_firstName;
	
	c_firstName = env->GetStringUTFChars(firstName, NULL);
    
	if (c_firstName == NULL)
	{
        return;
    }
	
	userInfo.FirstName = std::string(c_firstName);
	
	env->ReleaseStringUTFChars(firstName, c_firstName);
	
	
	// ==== Middle Name
	const char *c_middleName;
	
	c_middleName = env->GetStringUTFChars(middleName, NULL);
    
	if (c_middleName == NULL)
	{
        return;
    }
	
	userInfo.MiddleName = std::string(c_middleName);
	
	env->ReleaseStringUTFChars(middleName, c_middleName);
	
	
	// ==== Contact Number
	const char *c_contactNumber;
	
	c_contactNumber = env->GetStringUTFChars(contactNumber, NULL);
    
	if (c_contactNumber == NULL)
	{
        return;
    }
	
	userInfo.PhoneNumber = std::string(c_contactNumber);
	
	env->ReleaseStringUTFChars(contactNumber, c_contactNumber);
	
	userInfo.MapState = mapState;
	userInfo.Uik = uik;
	userInfo.Status = status;
	
	userInfo.PosX =  posX;
	
	userInfo.PosY = posY;
	
	ClientSocket->UserInfo = userInfo;
	
	ClientSocket->SendUserinfoUpdateThreaded();

}


JNIEXPORT void JNICALL Java_fishrungames_kobserver_JniWrapper_SetAlarmCommon(JNIEnv * env, jobject obj)
{
	ClientSocket->UserInfo.MapState = 2;
	ClientSocket->SendUserinfoUpdateThreaded();
	ClientSocket->SendMessageThreaded(std::string("ТРЕВОГА (АВТОМАТИЧЕСКОЕ СООБЩЕНИЕ)"));
}

JNIEXPORT void JNICALL Java_fishrungames_kobserver_JniWrapper_SetAlarmExclusion(JNIEnv * env, jobject obj)
{
	ClientSocket->UserInfo.MapState = 3;
	ClientSocket->SendUserinfoUpdateThreaded();
	ClientSocket->SendMessageThreaded(std::string("ТРЕВОГА: УДАЛЕНИЕ С УИК (АВТОМАТИЧЕСКОЕ СООБЩЕНИЕ)"));
}

JNIEXPORT void JNICALL Java_fishrungames_kobserver_JniWrapper_SetAlarmInjection(JNIEnv * env, jobject obj)
{
	ClientSocket->UserInfo.MapState = 4;
	ClientSocket->SendUserinfoUpdateThreaded();
	ClientSocket->SendMessageThreaded(std::string("ТРЕВОГА: ВБРОС (АВТОМАТИЧЕСКОЕ СООБЩЕНИЕ)"));
}

JNIEXPORT void JNICALL Java_fishrungames_kobserver_JniWrapper_SetAlarmCarousel(JNIEnv * env, jobject obj)
{
	ClientSocket->UserInfo.MapState = 5;
	ClientSocket->SendUserinfoUpdateThreaded();
	ClientSocket->SendMessageThreaded(std::string("ТРЕВОГА: КАРУСЕЛЬ (АВТОМАТИЧЕСКОЕ СООБЩЕНИЕ)"));
}

JNIEXPORT void JNICALL Java_fishrungames_kobserver_JniWrapper_SetAlarmAssault(JNIEnv * env, jobject obj)
{
	ClientSocket->UserInfo.MapState = 6;
	ClientSocket->SendUserinfoUpdateThreaded();
	ClientSocket->SendMessageThreaded(std::string("ТРЕВОГА: НАПАДЕНИЕ (АВТОМАТИЧЕСКОЕ СООБЩЕНИЕ)"));
}

JNIEXPORT void JNICALL Java_fishrungames_kobserver_JniWrapper_SendMessage(JNIEnv * env, jobject obj, jstring msg)
{
	const char *c_msg;
	
	c_msg = env->GetStringUTFChars(msg, NULL);
    
	if (c_msg == NULL)
	{
        return;
    }
	
	std::string str_msg(c_msg);
	
	env->ReleaseStringUTFChars(msg, c_msg);
	
	ClientSocket->SendMessageThreaded(str_msg);
	
}


JNIEXPORT jstring JNICALL Java_fishrungames_kobserver_JniWrapper_GetLastMessageHeader(JNIEnv * env, jobject obj)
{

	jstring jstr;
	

	if (MessageHeaderArr.size() > 0)
	{
		JavaStringHolder.push_back(MessageHeaderArr[0]);
		jstr = env->NewStringUTF(JavaStringHolder[JavaStringHolder.size()-1].c_str());
		MessageHeaderArr.clear();
	}
	else
	{
		jstr = env->NewStringUTF("");
	}
	
	return jstr;
	
}

JNIEXPORT jstring JNICALL Java_fishrungames_kobserver_JniWrapper_GetLastMessageText(JNIEnv * env, jobject obj)
{
	
	jstring jstr;
	
	if (MessageTextArr.size() > 0)
	{
		JavaStringHolder.push_back(MessageTextArr[0]);
		jstr = env->NewStringUTF(JavaStringHolder[JavaStringHolder.size()-1].c_str());
		MessageTextArr.clear();
	}
	else
	{
		jstr = env->NewStringUTF("");
	}
	
	return jstr;
}


JNIEXPORT int JNICALL Java_fishrungames_kobserver_JniWrapper_GetNumberOfUsers(JNIEnv * env, jobject obj)
{
	return (*CurrentUserInfoMapArr).size();
}

JNIEXPORT int JNICALL Java_fishrungames_kobserver_JniWrapper_GetUserUik(JNIEnv * env, jobject obj, int i)
{
	return (*CurrentUserInfoMapArr)[i].Uik;
}

JNIEXPORT float JNICALL Java_fishrungames_kobserver_JniWrapper_GetUserPosX(JNIEnv * env, jobject obj, int i)
{
	return (*CurrentUserInfoMapArr)[i].PosX;
}

JNIEXPORT float JNICALL Java_fishrungames_kobserver_JniWrapper_GetUserPosY(JNIEnv * env, jobject obj, int i)
{
	return (*CurrentUserInfoMapArr)[i].PosY;
}

JNIEXPORT int JNICALL Java_fishrungames_kobserver_JniWrapper_GetUserMapState(JNIEnv * env, jobject obj, int i)
{
	return (*CurrentUserInfoMapArr)[i].MapState;
}

JNIEXPORT jstring JNICALL Java_fishrungames_kobserver_JniWrapper_GetUserDescription(JNIEnv * env, jobject obj, int i)
{
	
	std::string State;

    if ((*CurrentUserInfoMapArr)[i].MapState == 2)
    {
        State = "Тревога";
    }
    else if ((*CurrentUserInfoMapArr)[i].MapState == 3)
    {
        State = "Тревога - Удаление с УИК";
    }
    else if ((*CurrentUserInfoMapArr)[i].MapState == 4)
    {
        State = "Тревога - Вброс";
    }
    else if ((*CurrentUserInfoMapArr)[i].MapState == 5)
    {
        State = "Тревога - Карусель";
    }
    else if ((*CurrentUserInfoMapArr)[i].MapState == 6)
    {
        State = "Тревога - Нападение";
    }
	
	std::string status = "Наблюдатель";
	
	if ((*CurrentUserInfoMapArr)[i].Status == 1)
	{
		status = "Мобильная группа";
	}
	
	std::string descr = "УИК №" + tostr((*CurrentUserInfoMapArr)[i].Uik) + "\\n"+
	(*CurrentUserInfoMapArr)[i].LastName+ "\\n"+
	(*CurrentUserInfoMapArr)[i].FirstName+ "\\n"+
	(*CurrentUserInfoMapArr)[i].MiddleName+ "\\n"+
	(*CurrentUserInfoMapArr)[i].PhoneNumber+ "\\n"+
	status +  "\\n" +
	"Состояние: "+State;
	
	jstring jstr;
	
	jstr = env->NewStringUTF(descr.c_str());

	return jstr;
}

JNIEXPORT float JNICALL Java_fishrungames_kobserver_JniWrapper_GetMapCenterPosX(JNIEnv * env, jobject obj)
{
	return ClientSocket->BaseLon;
}

JNIEXPORT float JNICALL Java_fishrungames_kobserver_JniWrapper_GetMapCenterPosY(JNIEnv * env, jobject obj)
{
	return ClientSocket->BaseLat;
}

JNIEXPORT int JNICALL Java_fishrungames_kobserver_JniWrapper_GetMapCenterZoom(JNIEnv * env, jobject obj)
{
	return ClientSocket->BaseZoom;
}



JNIEXPORT int JNICALL Java_fishrungames_kobserver_JniWrapper_SetPosition(JNIEnv * env, jobject obj, float x, float y)
{
	ClientSocket->UserInfo.PosX = x;
	ClientSocket->UserInfo.PosY = y;
	
	LastPosX = x;
    LastPosY = y;
}

JNIEXPORT void JNICALL Java_fishrungames_kobserver_JniWrapper_ChangeMapType(JNIEnv * env, jobject obj, int mapType)
{
	MapType = mapType;
	
	if (MapType == 0)
	{
	CurrentUserInfoMapArr = &UserInfoMapArr;
	}
	else
	{
	CurrentUserInfoMapArr = &UserInfoAlarmMapArr;
	}
}

