/***************************************************************************
                   Insert Data Class ( InsertData.cpp )
                -----------------------------------------

	begin      : 2013/07/03
	copyright  : (C) 2013 Solbox Inc.
	author     : Development 1 Team
					- 2013/07/03 - 1st dadamin
	email      : dev1@solbox.com
	version    : 3.2.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 "cc_statd.h"
#include "InsertData.h"
#include "Insert.h"
#include "DBManager.h"
#include "Configs.h"
#include "Logger.h"

CInsertData::CInsertData()
{
}

CInsertData::CInsertData(string &rc, string &userseq, string &svcseq, string &svcname)
{
	m_svc[XML_SVC_RC] = rc;
	m_svc[XML_SVC_USER_SEQ] = userseq;
	m_svc[XML_SVC_SEQ] = svcseq;
	m_svc[XML_SVC_NAME] = svcname;
}

CInsertData::~CInsertData()
{
	Clear();
}

void CInsertData::Clear()
{
	ClearJobLog();
	ClearKey();

	m_svc.clear();
	m_stat.clear();
	m_failtype.clear();
	m_insertfail.clear();
}

void CInsertData::MakeStatKey(string stattype, string val)
{
	ClearKey();
	m_key.append( stattype );
	m_key.append( "-" );
	m_key.append( val );
}

void CInsertData::MakeInsertFailKey(string  failtype)
{
	ClearKey();
	m_key.append(failtype);
}

void CInsertData::SetServiceInfo(string &rc, string &userseq, string &svcseq, string &svcname)
{
	m_svc[XML_SVC_RC] = rc;
	m_svc[XML_SVC_USER_SEQ] = userseq;
	m_svc[XML_SVC_SEQ] = svcseq;
	m_svc[XML_SVC_NAME] = svcname;
}

bool CInsertData::SetStat(string valuetype, string val)
{
	if(m_key.empty())
	{
		LOG(LERR, "InsertData Key empty.");
		return false;
	}
	
	map<string, string> v;
	v.insert(make_pair(valuetype, val));

	std::pair<map<string, map <string, string > >::iterator, bool> ret;
	ret = m_stat.insert(make_pair(m_key, v));
	if (ret.second==false) 
	{
		std::pair<map <string, string >::iterator, bool> r;
		r = ret.first->second.insert(make_pair(valuetype, val));
		if (r.second==false) 
		{
			LOG(LERR, "InsertData element '%s' already existed.", valuetype.c_str());
			return false;
		}
	}

	return true;
}

bool CInsertData::SetInsertFail(string statkey, string valuetype, string val)
{
	if(m_key.empty())
	{
		LOG(LERR, "InsertData(Fail) Key empty.");
		return false;
	}

	map<string, string> v;
	v.insert(make_pair(valuetype, val));

	map<string, map<string, string > > v2;
	v2.insert(make_pair(statkey, v));

	std::pair<map< string, map<string, map <string, string > > >::iterator, bool> ret;
	ret = m_insertfail.insert(make_pair(m_key, v2));
	if (ret.second==false) 
	{
		std::pair<map<string, map <string, string > >::iterator, bool> ret2;
		ret2 = ret.first->second.insert(make_pair(statkey, v));
		if (ret2.second==false) 
		{
			std::pair<map <string, string >::iterator, bool> ret3;
			ret3 = ret2.first->second.insert(make_pair(valuetype,val));
			if(ret3.second == false)
			{
				LOG(LERR, "InsertData(Fail) element '%s' already existed.", valuetype.c_str());
				return false;
			}
		}
	}
	
	// save fail
	m_joblog[1] += (statkey + "|");
	return true;
}

bool CInsertData::SetInsertFail(string statkey, map <string, string > &val)
{
	if(m_key.empty())
	{
		LOG(LERR, "InsertData(Fail) Key empty.");
		return false;
	}

	 map<string, map <string, string > > v;
	 v.insert(make_pair(statkey, val));

	std::pair<map< string, map<string, map <string, string > > >::iterator, bool> ret;
	ret = m_insertfail.insert(make_pair(m_key, v));
	if (ret.second==false) 
	{
		std::pair<map<string, map <string, string > >::iterator, bool> ret2;
		ret2 = ret.first->second.insert(make_pair(statkey, val));
		if (ret2.second==false) 
		{
			LOG(LERR, "InsertData(Fail) element '%s' already existed.", statkey.c_str());
			return false;
		}
	}

	// save fail
	m_joblog[1] += (statkey + "|");
	return true;
}

bool CInsertData::FindFailType(string stype)
{
	// find 
	vector<string>::iterator i = find(m_failtype.begin(), m_failtype.end(), stype);
	
	if (i!= m_failtype.end()) 
	{
		// found it
		return true;
	} 
	else 
	{
		// doesn't exist
		return false;		
	}

	return true;
}

bool CInsertData::ExecuteInsert(string stype, CDBManager*  dbmanager)
{
	bool r = true;	
	map<string, map <string, string > >::iterator it;

	for (it=m_stat.begin(); it!= m_stat.end(); ++it)
	{		
		CInsert *i = NULL;
		CInsertFactory f;
		i = f.Create(stype, it->first);
		if( i )
		{
			CDBPools * pool = dbmanager->GetDBPool(f.GetDBType(), USED_KIND_INTEGRATE);
			if(!pool)
			{
				pool = dbmanager->GetDBPool(f.GetDBType(), f.GetUsedType());
				if(!pool)
				{
					r = false;
					SetInsertFail(it->first, it->second);
					LOG(LERR, "There isn't type of database that you want to use.");
					continue;
				}
			}

			size_t pos = 0;			

			if(pool->GetDB(pos))
			{
				LOG(LDEV1, "Database POOL POS [%zu].", pos);				
				try
				{
					if(i->Insert(m_svc, it->second, pool->GetSession(pos)) == false)
					{
						r = false;
						SetInsertFail(it->first, it->second);
					}
					else
					{
						// save success
						m_joblog[0] += (it->first + "|");
						LOG(LDEV2, "Insert Success :%s => %s, %s "
							, f.GetDBType().c_str(), m_svc[XML_SVC_SEQ].c_str()
							, it->first.c_str());
					}
					pool->ReleaseDB(pos);
				}
				catch (soci_error const &e)
				{
					r = false;
					LOG(LERR, "Database POOL Error message :%s", e.what());
				}	
			}
			else 
			{
				// fail 				
				r = false;
				SetInsertFail(it->first, it->second);
				LOG(LERR, "Database POOL acquisition failure.");
			}

			delete i;
		}
		else
		{
			r = false;
			SetInsertFail(it->first, it->second);
		}
	}

	return r;
}

bool CInsertData::MakeXMLNode(Element* root)
{
	ostringstream msg;
	try
	{
		if ( m_insertfail.empty() )
			return false;
		
		map<string, map<string, map <string, string > > >::iterator it;
		

		for (it=m_insertfail.begin(); it!= m_insertfail.end(); ++it)
		{
			// Set Service
			Element* service = root->add_child(XML_SVC);
			
			/// Set attributes : RC, USERSEQ, SVCSEQ, SVCNAME
			service->set_attribute(XML_SVC_RC,			m_svc[XML_SVC_RC]);
			service->set_attribute(XML_SVC_USER_SEQ,	m_svc[XML_SVC_USER_SEQ]);
			service->set_attribute(XML_SVC_SEQ,			m_svc[XML_SVC_SEQ]);
			service->set_attribute(XML_SVC_NAME,		m_svc[XML_SVC_NAME]);

			/// Set Stat
			Element* stats = service->add_child(XML_STAT);
			////  ACCESS, STORAGE, NETWORK, TRANSFER
			map<string, map <string, string > >::iterator it2;
			for(it2=it->second.begin();it2!= it->second.end(); ++it2 )
			{
				vector<string> stattype;
				StringSplit(it2->first, "-", stattype, true);
				if( stattype.size() != 2 )
				{
					LOG(LWAR, "Stat Type is invalid.[%s]", it2->first.c_str())
					continue;
				}
				
				Element* statdata = stats->add_child(stattype[0]);

				map <string, string >::iterator it3;
				for(it3=it2->second.begin();it3!= it2->second.end(); ++it3 )
				{
					std::size_t found = it3->first.find(XML_STAT_TIME);
					if( found != string::npos && found == 0)
					{
						statdata->set_attribute(XML_STAT_TIME, stattype[1]);
					}
					else
					{
						Element* val = statdata->add_child(it3->first);
						val->set_child_text(it3->second);
					}
				}
			}

			/// Set Fail 
			Element* faildb = service->add_child(XML_FAIL);
			//// Set Fail Database
			Element*  dbtype = faildb->add_child(XML_FAIL_DATABASE);
			dbtype->set_attribute(XML_FAIL_TYPE, it->first);
		}
	}
	catch(const std::exception& ex)
	{
		msg << "Exception caught: " << ex.what();
		LOGACONSOLE(LERR, msg);
		return false;
	}

	return true;
}

void CInsertData::PrintServiceInfo()
{
	if( CLogger::GetInstance()->GetLogLevel() <= LDBG )
		return;

	ostringstream msg;
	msg << "Service Info: " << GetRC() << "," << GetUserSEQ() 
		<< "," << GetSvcSEQ() << "," << GetSvcName();
	LOGACONSOLE(LDEV1, msg);
}

void CInsertData::PrintStat()
{
	if( CLogger::GetInstance()->GetLogLevel() <= LDBG )
		return;

	ostringstream msg;
	map<string, map <string, string > >::iterator it;
	map <string, string >::iterator it2;

	msg << "Stat Data [" << GetSvcSEQ() << "] => " << m_stat.size();
	LOGACONSOLE(LDEV1, msg);
	
	for (it=m_stat.begin(); it!= m_stat.end(); ++it)
	{
		msg << it->first << ":";
		for(it2=it->second.begin();it2!= it->second.end(); ++it2 )
		{
			msg << it2->first  << " => " << it2->second << ",";
		}

		LOGACONSOLE(LDEV1, msg);
	}

	msg << "Stat Data <= [" << GetSvcSEQ() << "]";
	LOGACONSOLE(LDEV1, msg);
}

void CInsertData::PrintFailType()
{
	if( CLogger::GetInstance()->GetLogLevel() <= LDBG )
		return;

	ostringstream msg;

	msg << "Fail Data [" << GetSvcSEQ() << "] => " << m_failtype.size();
	LOGACONSOLE(LDEV1, msg);

	msg << "Fail Database Type :";
	for (unsigned int i = 0; i < m_failtype.size(); ++i)
	{
		 msg << " " << m_failtype[i];
	}
	LOGACONSOLE(LDEV1, msg);

	msg << "Fail Data <= [" <<  GetSvcSEQ() << "]";
	LOGACONSOLE(LDEV1, msg);
}

void CInsertData::PrintInsertFail()
{
	if( CLogger::GetInstance()->GetLogLevel() <= LDBG )
		return;

	ostringstream msg;
	map<string, map<string, map <string, string > > >::iterator it;
	map<string, map <string, string > >::iterator it2;
	map <string, string >::iterator it3;

	msg << "Insert Fail Data [" << GetSvcSEQ() << "] => " << m_insertfail.size();
	LOGACONSOLE(LDEV1, msg);

	for (it=m_insertfail.begin(); it!= m_insertfail.end(); ++it)
	{
		msg << it->first << ":";
		for(it2=it->second.begin();it2!= it->second.end(); ++it2 )
		{
			msg << it2->first  << " : ";
			for(it3=it2->second.begin();it3!= it2->second.end(); ++it3 )
			{
				msg << it3->first  << " => " << it3->second << ",";
			}
		}
		LOGACONSOLE(LDEV1, msg);
	}

	msg << "Insert Fail Data <= [" << GetSvcSEQ() << "]";
	LOGACONSOLE(LDEV1, msg);
}

void CInsertData::PrintStatAll()
{
	if( CLogger::GetInstance()->GetLogLevel() <= LDBG )
		return;

	PrintServiceInfo();
	PrintStat();
	PrintFailType();
}

void CInsertData::PrintInsertFailAll()
{
	if( CLogger::GetInstance()->GetLogLevel() <= LDBG )
		return;

	PrintServiceInfo();
	PrintInsertFail();
}

