/***************************************************************************
                   rc_mngd Control Class (RcMngdControl.cpp)
                -----------------------------------------

	begin      : 2015/02/04
	copyright  : (C) 2013 Solbox Inc.
	author     : Development 1 Team
					- 2015/02/04 - 1st dadamin
	email      : dev1@solbox.com
	version    : 3.4.0

	CopyRight(C) 2005 Solbox Inc. All Rights reserved.
	Redistribution and use in source and binary forms, with or with out
	modification, are not permitted in outside of Solbox Inc.
***************************************************************************/
#include "rc_mngd.h"
#include "RcMngdControl.h"
#include "Logger.h"
#include <fcntl.h>
#include <arpa/inet.h>
#include "openssl/ssl.h"
#include "openssl/bio.h"
#include "openssl/err.h"
#include "BaseSocket.h"
#include "RCDBControl.h"
#include "Convert.h"
#include "ConvertReqServiceMove.h"
#include "ConvertReqServiceLink.h"
#include "Service.h"
#include "Parser.h"


CRcMngdControl::CRcMngdControl()
	: m_ctx(NULL), m_bio(NULL)
{
}

CRcMngdControl::~CRcMngdControl()
{
	// Close the connection and free the context
	if (m_bio)
	{
		BIO_free_all(m_bio);
	}	
	
	if (m_ctx)
	{
		SSL_CTX_free(m_ctx);
	}	
}

bool CRcMngdControl::Connect(const char* ip, int port, bool usessl)
{
	if (!usessl)
	{
		LOG(LERR, "Only SSL support.");
		return false;
	}

	// Set up the library
	SSL_library_init();
	SSL_load_error_strings();

	// Setup the connection

	SSL_METHOD *meth = (SSL_METHOD*)SSLv3_method();
	
	// Set the SSL context
	m_ctx = SSL_CTX_new(meth);
	if (m_ctx == NULL)
	{
		LOG(LERR, "Error ctx.(%s)", ERR_error_string(ERR_get_error(), NULL));
		return false;
	}

	m_bio = BIO_new_ssl_connect(m_ctx);

	//Set the SSL_MODE_AUTO_RETRY flag */
	SSL * ssl;
	BIO_get_ssl(m_bio, &ssl);
	SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

	// Create and setup the connection
	ostringstream connstr;
	connstr << ip << ":" << port;
	BIO_set_conn_hostname(m_bio, connstr.str().c_str());

	if (BIO_do_connect(m_bio) <= 0)
	{
		LOG(LERR, "Error attempting to connect.[host:%s](%s)", 
				ip,ERR_error_string(ERR_get_error(), NULL));
		return false;
	}

	m_targetrcts = ip;
	return true;
}

bool CRcMngdControl::SendHeader(int bodylen)
{
	bool r = false;

	int nH = htonl(bodylen);
	if (BIO_write(m_bio, &nH, 4) <= 0)
	{
		r = false;
		LOG(LERR, "Error Send Header.(%s)", ERR_error_string(ERR_get_error(), NULL));
	}
	else
	{
		r = true;
		LOG(LDBG, "Send Header success.");
	}

	return r;
}

bool CRcMngdControl::SendBody(const char * buf, int snedlen)
{
	bool r = false;

	// send body 
	if (BIO_write(m_bio, buf, snedlen) <= 0)
	{
		r = false;
		LOG(LERR, "Error Send Body.(%s)", ERR_error_string(ERR_get_error(), NULL));
	}
	else
	{
		r = true;
		LOG(LDBG, "Send Body success.");
	}

	return r;
}

bool CRcMngdControl::ReadHeader(int & outlen)
{
	bool r = false;
	
	int h = 0;
	int n = BIO_read(m_bio, &h, 4);
	if (n < 0)
	{
		r = false;
		LOG(LERR, "Error Read Header.(%s)", ERR_error_string(ERR_get_error(), NULL));
	}
	else if (n == 0)
	{
		r = false;
		LOG(LERR, "Server closed.[Header].(err:%d)", errno);
	}
	else
	{
		r = true;
		outlen = ntohl(h);
		LOG(LDBG, "Read Header success.[body size : %d]", outlen);
	}

	return r;
}

bool CRcMngdControl::ReadBody(char * out, int & outlen)
{
	bool r = false;
	string buf;
		
	int n = BIO_read(m_bio, out, outlen);

	if (n < 0)
	{
		r = false;
		LOG(LERR, "Error Read Body.(%s)", ERR_error_string(ERR_get_error(), NULL));
	}
	else if (n == 0)
	{
		r = false;
		LOG(LERR, "Server closed.[Body].(err:%d)", errno);

	}
	else
	{
		r = true;
		LOG(LDBG, "Read Body success.[%s]", out);
	}

	return r;
}

bool CRcMngdControl::MoveRequest(const char* userseq, const char* svcseq, const char * rcts)
{
	bool r = false;

	// get all information (이관 서비스 관련 모든 정보)
	CRCDBControl rcdb;

	if (rcdb.Init() != 0)
	{
		LOG(LERR, "RCDB Initialization failed.");
		return false;
	}
	
	CJobDataMap	jobData;
	jobData[KEY_SP_USER_SEQ] = userseq;
	jobData[KEY_SP_SVC_TRAN_ID] = svcseq;

	if (rcdb.GetServiceInfoAll(jobData) == false)
	{
		rcdb.Final();
		return false;
	}

	rcdb.Final();

	// make xml
	CConvertReqSvcMove c(JOB_SERVICE_MOVE_REQUEST);

	jobData[KEY_LINK_MASTER_YN] = "N";
	jobData[KEY_LINK_SP_SVC_TRAN_ID] = svcseq;
	jobData[KEY_LINK_RCTS] = rcts;

	int o;
	char* pBodyData = (char*)c.ToClientData(CServiceInfo::XML, 0, &jobData, o);

	if (pBodyData)
	{
		// send 
		if (SendHeader(o))
		{
			if (SendBody(pBodyData, o))
			{
				SSL * ssl;
				BIO_get_ssl(m_bio, &ssl);

				CXMLParser parser;
				parser.SetSocket(SOCKET_NOT_VALID, ssl);
				if (parser.ReadHeader() == 0)
				{
					if (parser.ReadBody() == 0)
					{
						if (c.ToJobData(CServiceInfo::XML, parser.GetXMLJobNode(), &jobData) > 0)
						{
							LOG(LINF, "Move(%s) RMNGD <=> RMNGD result: [%s@%s : %s => %s (%s,%s)]",
								JOB_SERVICE_MOVE_REQUEST,
								jobData[KEY_SP_SVC_TRAN_ID].c_str(),
								jobData[KEY_SP_USER_SEQ].c_str(),
								rcts,
								m_targetrcts.c_str(),
								jobData[KEY_RESP_RESULT].c_str(),
								jobData[KEY_RESP_RESULT_CODE].c_str());

							if (jobData[KEY_RESP_RESULT].compare(JOX_RESULT_OK) == 0)
							{
								r = SetSync(userseq, svcseq, true, svcseq, SYNC_LINK_CODE_LINK);

								LOG(LDBG, "Move Request : [%s => %s]",
									jobData[KEY_VOLUME_ID].c_str(),
									(r ? "OK" : "FAIL"));
							}
						}
					}
				}
			}		
		}				

		delete[] pBodyData;
	}		

	return r;
}

bool CRcMngdControl::MoveCompleted(const char* userseq, const char* svcseq, const char* rcts)
{
	bool r = false;

	// make xml
	CConvertReqSvcMove c(JOB_SERVICE_MOVE_COMPLETE);
	
	CJobDataMap	jobData;

	string sid = svcseq;
	sid += "@";
	sid += userseq;

	jobData[KEY_VOLUME_SEQ] = sid;
	jobData[KEY_LINK_MASTER_YN] = "N";
	jobData[KEY_SELF_RCTS] = m_targetrcts;
	jobData[KEY_LINK_USER_SEQ] = userseq;
	jobData[KEY_LINK_SP_SVC_TRAN_ID] = svcseq;
	jobData[KEY_LINK_RCTS] = rcts;
	jobData[KEY_REQ_LINK_CODE] = SYNC_LINK_CODE_UNLINK;
	jobData[KEY_REQ_LINK_ONE_WAY] = SYNC_LINK_ONE_WAY;

	int o;
	char* pBodyData = (char*)c.ToClientData(CServiceInfo::XML, 0, &jobData, o);

	if (pBodyData)
	{
		if (SendHeader(o))
		{
			if (SendBody(pBodyData, o))
			{
				SSL * ssl;
				BIO_get_ssl(m_bio, &ssl);

				CXMLParser parser;
				parser.SetSocket(SOCKET_NOT_VALID, ssl);

				if (parser.ReadHeader() == 0)
				{
					if (parser.ReadBody() == 0)
					{
						if (c.ToJobData(CServiceInfo::XML, parser.GetXMLJobNode(), &jobData) > 0)
						{
							LOG(LINF, "Move(%s) RMNGD <=> RMNGD result: [%s : %s => %s (%s,%s)]",
								JOB_SERVICE_MOVE_COMPLETE,
								jobData[KEY_VOLUME_SEQ].c_str(),
								rcts,
								m_targetrcts.c_str(),
								jobData[KEY_RESP_RESULT].c_str(),
								jobData[KEY_RESP_RESULT_CODE].c_str());

							if (jobData[KEY_RESP_RESULT].compare(JOX_RESULT_OK) == 0)
							{
								r = SetSync(userseq, svcseq, true, svcseq, SYNC_LINK_CODE_UNLINK);

								LOG(LDBG, "Move Completed : [%s => %s]",
									jobData[KEY_VOLUME_SEQ].c_str(),
									(r ? "OK" : "FAIL"));
							}
						}
					}
				}
			}
		}

		delete[] pBodyData;
	}	

	return r;
}

bool CRcMngdControl::LinkRequest(struct LinkInfo& self, struct LinkInfo& target, const char* linkcode)
{
	bool r = false;

	// make xml
	CConvertReqSvcLink c("");

	CJobDataMap	jobData;

	string tsid = self.linksvcseq;
	tsid += "@";
	tsid += self.linkuserseq;

	jobData[KEY_VOLUME_SEQ] = tsid;
	jobData[KEY_LINK_MASTER_YN] = (target.master ? "Y" : "N");
	jobData[KEY_SELF_RCTS] = self.linkrcts;
	jobData[KEY_LINK_USER_SEQ] = target.linkuserseq;
	jobData[KEY_LINK_SP_SVC_TRAN_ID] = target.linksvcseq;
	jobData[KEY_LINK_RCTS] = target.linkrcts;
	jobData[KEY_REQ_LINK_CODE] = linkcode;
	jobData[KEY_REQ_LINK_ONE_WAY] = SYNC_LINK_ONE_WAY;

	int o;
	char* pBodyData = (char*)c.ToClientData(CServiceInfo::XML, 0, &jobData, o);

	if (pBodyData)
	{
		if (SendHeader(o))
		{
			if (SendBody(pBodyData, o))
			{
				SSL * ssl;
				BIO_get_ssl(m_bio, &ssl);

				CXMLParser parser;
				parser.SetSocket(SOCKET_NOT_VALID, ssl);

				if (parser.ReadHeader() == 0)
				{
					if (parser.ReadBody() == 0)
					{
						if (c.ToJobData(CServiceInfo::XML, parser.GetXMLJobNode(), &jobData) > 0)
						{
							
							LOG(LINF, "Link(%s) RMNGD <=> RMNGD result: [%s(%s@%s) => %s(%s) : %s,%s]",
								linkcode,
								jobData[KEY_LINK_RCTS].c_str(),								
								jobData[KEY_LINK_SP_SVC_TRAN_ID].c_str(),
								jobData[KEY_LINK_USER_SEQ].c_str(),								
								m_targetrcts.c_str(),
								jobData[KEY_VOLUME_SEQ].c_str(),
								jobData[KEY_RESP_RESULT].c_str(),
								jobData[KEY_RESP_RESULT_CODE].c_str());

							if (jobData[KEY_RESP_RESULT].compare(JOX_RESULT_OK) == 0)
							{
								r = SetSync(target.linkuserseq.c_str(), target.linksvcseq.c_str(), self.master, self.linksvcseq.c_str(), linkcode);

								LOG(LDBG, "Link result : [%s => %s]",
									jobData[KEY_VOLUME_SEQ].c_str(),
									(r ? "OK" : "FAIL"));
							}
						}

					}
				}
			}
		}

		delete[] pBodyData;
	}
	
	return r;
}

bool CRcMngdControl::ChkSync(const char* userseq, const char* svcseq)
{
	bool r = false;

	// 
	CRCDBControl rcdb;

	if (rcdb.Init() != 0)
	{
		LOG(LERR, "RCDB Initialization failed.");
		return false;
	}

	CJobDataMap	jobData;
	jobData[KEY_SP_USER_SEQ] = userseq;
	jobData[KEY_SP_SVC_TRAN_ID] = svcseq;

	if (rcdb.GetSyncLink(jobData) == false)
	{
		rcdb.Final();
		return false;
	}

	rcdb.Final();

	LOG(LDEV2, "Get Sync Service Info : %s", jobData[KEY_SYNC_LIST].c_str());

	return r;
}

bool CRcMngdControl::SetSync(const char* userseq, const char* svcseq, bool mstaer, const char* linksvcseq, const char* linkcode)
{
	bool r = false;

	// 
	CRCDBControl rcdb;

	if (rcdb.Init() != 0)
	{
		LOG(LERR, "RCDB Initialization failed.");
		return false;
	}

	bool exist = false;
	CJobDataMap	jobData;
	jobData[KEY_REQ_LINK_CODE] = linkcode;
	jobData[KEY_SP_USER_SEQ] = userseq;
	jobData[KEY_SP_SVC_TRAN_ID] = svcseq;

	jobData[KEY_LINK_MASTER_YN] = (mstaer ? "Y" : "N");
	jobData[KEY_LINK_SP_SVC_TRAN_ID] = linksvcseq;
	jobData[KEY_LINK_RCTS] = m_targetrcts;

	if (rcdb.CheckProductSync(jobData, exist) == true)
	{
		r = rcdb.SetProductSync(jobData, exist);
	}

	rcdb.Final();
	return r;
}

bool CRcMngdControl::ChangeRcts(const char* userseq, const char* svcseq, bool masteryn)
{
	bool r = false;

	return r;
}
