#include "fireflier.h"
#include "fireflierIO.h"
#include "CkjThreadSystem.h"
#include <iostream>

using namespace std;


CffIO ffIO;
CkjThreadSystem gthreadsystem_init;
CffIO::CNetworkThread CffIO::networkThread;
SSL* CffIO::m_ssl = NULL;
SSL_CTX* CffIO::m_ctx = NULL;
SSL_METHOD* CffIO::m_meth = NULL;
in_addr_t CffIO::m_nHostIP = in_addr_t();
guint16 CffIO::m_nPort = guint16();

//std::map <int, ustring> CffIO::CffIOError::m_mapErr2Str;

//CffIO::CffIOError::m_mapErr2Str[CffIO::COMMERR_CONN_NOTVALID] = "no connection to fireflier-server";
//CffIO::CffIOError::m_mapErr2Str[CffIO::COMMERR_NO_MEMORY] = "could not allocate memory for transfer";

bool CffIO::connect()
{
	if ((m_nHostIP == in_addr_t()) || (m_nPort == guint16()))
		// just prevent programming errors
		throw -1;

	int sd;
	struct sockaddr_in sa;

	SSLeay_add_ssl_algorithms();
	m_meth = SSLv2_client_method();
	SSL_load_error_strings();
	m_ctx = SSL_CTX_new(m_meth);
	sd = socket(AF_INET, SOCK_STREAM, 0);     
	
	memset(&sa, 0, sizeof(sa));
	sa.sin_family = AF_INET;
	sa.sin_addr.s_addr = m_nHostIP;
	sa.sin_port = htons(m_nPort);
	
	bool bContinue;
	int err = ::connect(sd, (struct sockaddr*) &sa, sizeof(sa));
	bContinue = (err != -1);
	if (bContinue)
	{
		m_ssl = SSL_new(m_ctx);
		bContinue = (m_ssl != NULL);
	}

	if (bContinue)
	{
		SSL_set_fd(m_ssl, sd);
		err = SSL_connect(m_ssl);
		bContinue = (err > 0);
	}

	if (!bContinue)
	{
		end();
	}
	
	return bContinue;
}

void CffIO::end()
{
	if (m_ssl)
	{
		SSL_shutdown(m_ssl);
		SSL_free(m_ssl);
		m_ssl = NULL;
	}
	m_ctx = NULL;		// according to ssl-docu this should be freed automatically on freeing ssl
	m_meth = NULL;	// in the previous line
}

SSL& operator <<(SSL& ssl, const CCO_SendBase& co)
{
	if (!&ssl)
		throw CffIO::pCffIOError(new CffIO::CffIOError(CffIO::COMMERR_CONN_NOTVALID));

	if (co.getHeaderBuf() && co.getHeaderLen())
	{
		int nResult = SSL_write(&ssl, co.getHeaderBuf(), co.getHeaderLen());
		if (nResult <= 0)
			throw CffIO::pCffIOError(new CffIO::CffIOError(SSL_get_error(&ssl, nResult)));
	}
	
	if (co.getMsgBuf() && co.getMsgLen())
	{
		int nResult = SSL_write(&ssl, co.getMsgBuf(), co.getMsgLen());
		if (nResult <= 0)
			throw CffIO::pCffIOError(new CffIO::CffIOError(SSL_get_error(&ssl, nResult)));
	}

	return ssl;
}

SSL& operator >>(SSL& ssl, CCO_RetrieveBase& co)
{
	if (!&ssl)
		throw CffIO::pCffIOError(new CffIO::CffIOError(CffIO::COMMERR_CONN_NOTVALID));

	if (co.getHeaderBuf() && co.getHeaderLen())
	{
		int nResult = SSL_read(&ssl, co.getHeaderBuf(), co.getHeaderLen());
		if (nResult <= 0)
			throw CffIO::pCffIOError(new CffIO::CffIOError(SSL_get_error(&ssl, nResult)));
	}

	co.parse();
	
	if (co.getMsgBuf() && co.getMsgLen())
	{
		int nResult = SSL_read(&ssl, co.getMsgBuf(), co.getMsgLen());
		if (nResult <= 0)
			throw CffIO::pCffIOError(new CffIO::CffIOError(SSL_get_error(&ssl, nResult)));
	}
	
	return ssl;
}
