#include "ClientSocket.h"


TClientSocket::TClientSocket(boost::asio::io_service& io_service)
	: Socket(io_service)
    , IoService(io_service)
	, AutorizationCodeResult("  ")
    , BlockCloseOnError(false)
    , BlockForceCloseMessage(false)
{
}


void TClientSocket::Open(std::string address, std::string port)
{
	boost::asio::ip::tcp::resolver resolver(IoService);
	
	boost::asio::ip::tcp::resolver::query query(address.c_str(), port.c_str());
	
    boost::system::error_code ec;
    
	boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query, ec);
    
    boost::asio::ip::tcp::resolver::iterator end;
    
    if (ec)
    {
        OnAddressNotResolved();
        return;
    }
    
	boost::asio::async_connect(Socket, iterator, boost::bind(&TClientSocket::HandleConnect, shared_from_this(), boost::asio::placeholders::error));
    
    
    ConnectionTimeoutTimer = boost::shared_ptr<boost::asio::deadline_timer>(new boost::asio::deadline_timer(IoService, boost::posix_time::seconds(3)));
    
    
    ConnectionTimeoutTimer->async_wait(boost::bind(&TClientSocket::ConnectTimeout, this, boost::asio::placeholders::error));
}

void TClientSocket::ConnectTimeout(const boost::system::error_code& e)
{
    if (e)
    {
        return;
    }
    
    Socket.cancel();
    BlockCloseOnError = true;

    IoService.stop();
    
    ReceiveForceCloseSignal();
}

void TClientSocket::OnAddressNotResolved()
{
    
    BlockCloseOnError = true;
    IoService.stop();
    
    ReceiveForceCloseSignal();
}


void TClientSocket::HandleConnect(const boost::system::error_code& error)
{
    if (!error)
    {
        ConnectionTimeoutTimer->cancel();
        boost::asio::async_read(Socket, boost::asio::buffer(&AutorizationCode, 4), boost::bind(&TClientSocket::HandleReadAutorizationCode, shared_from_this(), boost::asio::placeholders::error));
    }
    else
    {
        if (!BlockCloseOnError)
        {
            CloseImmediate();
        }
    }
}



void TClientSocket::HandleReadAutorizationCode(const boost::system::error_code& error)
{
	if (!error)
    {
		AutorizationCodeResponce = check_func(AutorizationCode); 
		
		boost::asio::async_write(Socket, boost::asio::buffer(&AutorizationCodeResponce, 4), boost::bind(&TClientSocket::HandleWrite, shared_from_this(), boost::asio::placeholders::error));
        
        ReadData.resize(16);

		boost::asio::async_read(Socket, boost::asio::buffer(&ReadData[0], 16), boost::bind(&TClientSocket::HandleReadAutorizationResult, shared_from_this(), boost::asio::placeholders::error));
        
        ReceiveAutorizationSentSignal();
	}
	else
	{
        if (!BlockCloseOnError)
        {
            CloseImmediate();
        }
	}
}

void TClientSocket::HandleReadAutorizationResult(const boost::system::error_code& error)
{
	if (!error)
    {
        AutorizationCodeResult = std::string(ReadData.begin(), ReadData.begin() + 2);
		if (AutorizationCodeResult == "ok")
		{
            BaseLon = (reinterpret_cast<float*>(&ReadData[0]))[1];
            BaseLat = (reinterpret_cast<float*>(&ReadData[0]))[2];
            BaseZoom = (reinterpret_cast<int*>(&ReadData[0]))[3];
			boost::asio::async_read(Socket, boost::asio::buffer(&LastSignal, 4), boost::bind(&TClientSocket::HandleReadSignal, shared_from_this(), boost::asio::placeholders::error));
		}
	}
	else
	{
        if (!BlockCloseOnError)
        {
            CloseImmediate();
        }
	}
}

void TClientSocket::HandleReadSignal(const boost::system::error_code& error)
{
	if (!error)
    {
		boost::asio::async_read(Socket, boost::asio::buffer(&ReadDataLenLong, 4), boost::bind(&TClientSocket::HandleReadDataLen, shared_from_this(), boost::asio::placeholders::error));			


    }
	else
	{
        if (!BlockCloseOnError)
        {
            CloseImmediate();
        }
	}
}

void TClientSocket::HandleReadDataLen(const boost::system::error_code& error)
{
	if (!error)
	{
		ReadData.resize(ReadDataLenLong);
		if (ReadDataLenLong == 0)
		{
			boost::asio::async_read(Socket, boost::asio::buffer(&LastSignal, 4), boost::bind(&TClientSocket::HandleReadSignal, shared_from_this(), boost::asio::placeholders::error));

            if (LastSignal == 4)
            {

            }
            else if (LastSignal == 1)
            {
                std::vector<TUserInfo> userInfoArr;

                ReceiveMapSignal(userInfoArr);

            }
            else if (LastSignal == 2)
            {
                std::vector<TUserInfo> userInfoArr;

                ReceiveAlarmMapSignal(userInfoArr);
            }

        }
		else
		{
			boost::asio::async_read(Socket, boost::asio::buffer(&ReadData[0], ReadDataLenLong), boost::bind(&TClientSocket::HandleReadData, shared_from_this(), boost::asio::placeholders::error));



        }
	}
	else
	{
        if (!BlockCloseOnError)
        {
            CloseImmediate();
        }
	}
}

void TClientSocket::HandleReadData(const boost::system::error_code& error)
{
	if (!error)
	{
		
		if (LastSignal == 4)
		{
			std::string message;
			TUserInfo userInfo;

			DecodeMessage(ReadData, userInfo, message);

			ReceiveMessageSignal(userInfo, message);
		}
		else if (LastSignal == 1)
		{
			std::vector<TUserInfo> userInfoArr;

			DecodeMapUpdateResponce(ReadData, userInfoArr);

			ReceiveMapSignal(userInfoArr);

		}
		else if (LastSignal == 2)
		{
			std::vector<TUserInfo> userInfoArr;

			DecodeAlarmMapUpdateResponce(ReadData, userInfoArr);

			ReceiveAlarmMapSignal(userInfoArr);
		}


		boost::asio::async_read(Socket, boost::asio::buffer(&LastSignal, 4), boost::bind(&TClientSocket::HandleReadSignal, shared_from_this(), boost::asio::placeholders::error));
	}
	else
	{
        if (!BlockCloseOnError)
        {
            CloseImmediate();
        }
	}
}


void TClientSocket::HandleWrite(const boost::system::error_code& error)
{
	if (error)
    {
        if (!BlockCloseOnError)
        {
            CloseImmediate();
        }
    }
}


 void TClientSocket::CloseImmediate()
{
    // Cancel all outstanding asynchronous operations.
    
    BlockCloseOnError = true;
    
    ConnectionTimeoutTimer->cancel();

    Socket.close();
    
    if (!BlockForceCloseMessage)
    {
        ReceiveForceCloseSignal();
    }
  
}


void TClientSocket::SendMessageImmediate(std::string msg)
{
    boost::shared_ptr<TMessageSender>(new TMessageSender(Socket, CONST_SIGNAL_MESSAGE, CreateMessage(UserInfo, msg)))->Send();
}

void TClientSocket::SendUserinfoUpdateImmediate()
{
	boost::shared_ptr<TMessageSender>(new TMessageSender(Socket, 3, CreateProfileUpdateQuery(UserInfo)))->Send();
}

void TClientSocket::SendHeartbeatImmediate()
{
	boost::shared_ptr<TMessageSender>(new TMessageSender(Socket, 5, CreateHeartBeatQuery(UserInfo)))->Send();
}

void TClientSocket::SendMapQueryImmediate()
{
	boost::shared_ptr<TSignalSender>(new TSignalSender(Socket, 1))->Send();
}

void TClientSocket::SendAlarmMapQueryImmediate()
{
	boost::shared_ptr<TSignalSender>(new TSignalSender(Socket, 2))->Send();
}

void TClientSocket::SendKickPlayerImmediate(std::string lastName, std::string firstName, std::string middleName)
{
    boost::shared_ptr<TMessageSender>(new TMessageSender(Socket, 100, CreateKickPlayerSignal(lastName, firstName, middleName)))->Send();
}


void TClientSocket::CloseThreaded()
{
	IoService.post(boost::bind(&TClientSocket::CloseImmediate, this));
}

void TClientSocket::SendMessageThreaded(std::string msg)
{
    IoService.post(boost::bind(&TClientSocket::SendMessageImmediate, this, msg));
}

void TClientSocket::SendUserinfoUpdateThreaded()
{
	IoService.post(boost::bind(&TClientSocket::SendUserinfoUpdateImmediate, this));
}

void TClientSocket::SendHeartbeatThreaded()
{
	IoService.post(boost::bind(&TClientSocket::SendHeartbeatImmediate, this));
}

void TClientSocket::SendMapQueryThreaded()
{
	IoService.post(boost::bind(&TClientSocket::SendMapQueryImmediate, this));
}

void TClientSocket::SendAlarmMapQueryThreaded()
{
	IoService.post(boost::bind(&TClientSocket::SendAlarmMapQueryImmediate, this));
}

void TClientSocket::SendKickPlayerThreaded(std::string lastName, std::string firstName, std::string middleName)
{
    IoService.post(boost::bind(&TClientSocket::SendKickPlayerImmediate, this, lastName, firstName, middleName));
}


