#include <ncbi_pch.hpp>
#include "datacache.hpp"
#if defined(__DB_OFFLINE__)
	#include "basealgo.hpp"
#else
	#include <shlu2/BasicUtils/basealgo.hpp>
#endif
//#include <corelib/ncbitime.hpp>
//#include <objects/general/general__.hpp>
#include <corelib/ncbistr.hpp>
#include <sstream>
#include <iostream>
#include <zlib.h>

USING_NCBI_SCOPE;


TDataKey:: operator string (void) const
{
	return MakeHandle();
}
void TDataKey::MakeHandle(string &handle) const
{
    handle = MakeHandle();
}

//TDataKey::TDataKey(const std::string &handle): signature(k_strEmptyString), service_name(k_strEmptyString), data_name(k_strEmptyString), usr_ip4(0)
//{
//	ParseHandle(handle);
//}
//
//int TDataKey::ParseHandle(const std::string &handle)
//{
//	//vector<string> vecBuf;
//	//SplitString(handle, SIDFIELDDELIMIT, vecBuf);
//	
//	CStringTokenizer tks(handle, SIDFIELDDELIMIT);
//	
//	signature.clear();
//    service_name.clear();
//    data_id = 0;
//    data_name.clear();
//    // -- do not reset userip
//	int idx = 0;
//	try
//	{
//		signature = tks.get();
//		++idx;
//		service_name = tks.get();
//		++idx;
//		data_name = tks.get();
//        ++idx;
//        try //try with qman id
//        {
//            data_id = NStr::StringToNumeric<NUMERIC_ID_TYPE>(data_name, 0, 16);
//            data_name = tks.get();
//            ++idx;
//        }
//        catch (...){;}
//		
//	}
//	catch(...){;}
//	
//	return idx;
//}
//
//void TDataKey::MakeMstHandle(string &handle) const
//{
//	handle = MakeMstHandle();
//}
//
//string TDataKey::MakeMstHandle(void) const
//{
//	return signature + SIDFIELDDELIMIT + service_name + (0 == data_id ? k_strEmptyString : SIDFIELDDELIMIT + NStr::NumericToString<NUMERIC_ID_TYPE>(data_id, 0, 16));
//}
//
//string TDataKey::MakeHandle(void) const
//{
//	return signature + SIDFIELDDELIMIT + service_name + SIDFIELDDELIMIT + (0 == data_id ? k_strEmptyString : NStr::NumericToString<NUMERIC_ID_TYPE>(data_id, 0, 16) + SIDFIELDDELIMIT) + data_name;
//}
//
//void TDataKey::MakeHandle(string &handle) const
//{
//	handle = MakeHandle();
//}
//
//
//TDataKey:: operator string (void) const
//{
//	return MakeHandle();
//}

const char * CDataCache::m_dimStatusLits[] =
{
	"Successful",
	"Invalid key",
	"No such data",	//reqId valid, but no data under this name
    "Data Processing",
	"Incorrect cache mode",
	"Data processing error",	//compress/decompress
    "Fail to obtain data storage on cache server",	// -- fail to get store id
	"Fail to store data onto cache server",	// -- fail to transfer blobs to server
    "Too many requests submitted.<br>\nPlease goto <a href=\"//www.ncbi.nlm.nih.gov/Structure/cdd/cdd.shtml\" target=\"_blank\">CDD Homepage</a> to find out<br>\nhow to install and run blast on your local machine",
	"Unknown"
};

const char * CDataCache::MsgString(int errCode)
{
	if (errCode >= 0 && errCode <= CDataCache::eCacheErrorStop) return CDataCache::m_dimStatusLits[errCode];
	else return NULL;
}

CDataCache::CDataCache(int mode, unsigned int usr_ip4):
    m_usr_ip4(usr_ip4),
    m_iMode(mode),
    m_pBuf(new char[CDataCache::BUFFSIZE + CDataCache::BUFMARGIN]),
	m_ulBytesAvailable(0),
	m_ulCurrPos(0),
	m_iBlockIdx(0),
	m_bDirty(false)
{}

CDataCache::~CDataCache(void)
{
	delete [] m_pBuf;
}

void CDataCache::ResetData(int mode)
{
    // -- does not reset user_ip
    if (mode >= 0)
        m_iMode = mode;
    m_ulBytesAvailable = 0;
	m_ulCurrPos = 0;
	m_iBlockIdx = 0;
	m_bDirty = false;
}

int CDataCache::Flush(void)
{
	if (eWrite != m_iMode) return eModeError;
	
	int retval = eDataOk;
	if (m_bDirty)
	{

		retval = x_SaveBlock(m_iBlockIdx, m_pBuf, m_ulBytesAvailable);
		
		if (eDataOk == retval)
		{
			m_ulBytesAvailable = 0;
			m_ulCurrPos = 0;
			m_bDirty = false;
			++m_iBlockIdx;
		}
	}
	return retval;
}


void CDataCache::PushData(const void* pData, size_t ulSize)
{
	if (eWrite != m_iMode) throw eModeError;
	size_t avail = CDataCache::BUFFSIZE - m_ulBytesAvailable;
	
	const char *pSrc = (const char *)pData;
	char *pDst = m_pBuf + m_ulBytesAvailable;
	
	while (ulSize > avail + CDataCache::BUFMARGIN)
	{
		memcpy(pDst, pSrc, avail);
		pSrc += avail;
		m_ulBytesAvailable += avail;
		ulSize -= avail;
		m_bDirty = true;
		int retval = Flush();
		
		if (eDataOk != retval) throw retval;
		pDst = m_pBuf + m_ulBytesAvailable;
		avail = CDataCache::BUFFSIZE - m_ulBytesAvailable;
	}
	
	// -- assert(ulSize <= avail + CDataCache::BUFMARGIN)
	
	if (ulSize > 0)
	{
		memcpy(pDst, pSrc, ulSize);	//copy all rest
		m_ulBytesAvailable += ulSize;
		m_bDirty = true;
		if (m_ulBytesAvailable >= CDataCache::BUFFSIZE)
		{
			int retval = Flush();
			if (eDataOk != retval) throw retval;
		}
	}
}


void CDataCache::PushString(const string &rStr)
{
	//if (eWrite != m_iMode) throw eModeError;
	
	size_t ulSize = rStr.size();
	PushData(&ulSize, sizeof(size_t));
    
    PushData(rStr.data(), ulSize);
	
	//size_t avail = CDataCache::BUFFSIZE - m_ulBytesAvailable;
	//size_t ulSrc = 0;
	//char *pDst = m_pBuf + m_ulBytesAvailable;
	//while (ulSize > avail + CDataCache::BUFMARGIN)
	//{
	//	rStr.copy(pDst, avail, ulSrc);	//fill out this block
	//	ulSrc += avail;
	//	m_ulBytesAvailable += avail;
	//	ulSize -= avail;
	//	m_bDirty = true;
	//	int retval = Flush();
	//	if (eDataOk != retval) throw retval;
	//	pDst = m_pBuf + m_ulBytesAvailable;
	//	avail = CDataCache::BUFFSIZE - m_ulBytesAvailable;
	//}
	//
	//// -- assert(ulSize <= avail + CDataCache::BUFMARGIN)
	//
	//if (ulSize > 0)
	//{
	//	rStr.copy(pDst, ulSize, ulSrc);	//fill out this block
	//	m_ulBytesAvailable += ulSize;
	//	m_bDirty = true;
	//	if (m_ulBytesAvailable >= CDataCache::BUFFSIZE)
	//	{
	//		int retval = Flush();
	//		if (eDataOk != retval) throw retval;
	//	}
	//}
	
}

void CDataCache::PushCString(const char *pStr)
{
	//if (eWrite != m_iMode) throw eModeError;
	
	size_t ulSize = strlen(pStr);
	PushData(&ulSize, sizeof(size_t));
    
    PushData(pStr, ulSize);
	
	//size_t avail = CDataCache::BUFFSIZE - m_ulBytesAvailable;
	//
	//const char *pSrc = pStr;
	//char *pDst = m_pBuf + m_ulBytesAvailable;
	//while (ulSize > avail + CDataCache::BUFMARGIN)
	//{
	//	strncpy(pDst, pSrc, avail);
	//	pSrc += avail;
	//	m_ulBytesAvailable += avail;
	//	ulSize -= avail;
	//	m_bDirty = true;
	//	int retval = Flush();
	//	if (eDataOk != retval) throw retval;
	//	pDst = m_pBuf + m_ulBytesAvailable;
	//	avail = CDataCache::BUFFSIZE - m_ulBytesAvailable;
	//}
	//
	//// -- assert(ulSize <= avail + CDataCache::BUFMARGIN)
	//
	//if (ulSize > 0)
	//{
	//	strncpy(pDst, pSrc, ulSize);
	//	m_ulBytesAvailable += ulSize;
	//	m_bDirty = true;
	//	if (m_ulBytesAvailable >= CDataCache::BUFFSIZE)
	//	{
	//		int retval = Flush();
	//		if (eDataOk != retval) throw retval;
	//	}
	//}
}

int CDataCache::Load(void)
{

	if (eRead != m_iMode) return eModeError;
	int	retval = x_RetrieveBlock(m_iBlockIdx, m_pBuf, m_ulBytesAvailable);

    
	if (eDataOk == retval)
	{
		m_ulCurrPos = 0;
		++m_iBlockIdx;
	}
	return retval;
}


void CDataCache::ReadData(void* pDst, size_t ulSize)
{
	if (eRead != m_iMode) throw eModeError;
	size_t avail = m_ulBytesAvailable - m_ulCurrPos;
	
	char *pDstPos = (char *)pDst;
	const char *pSrcPos = m_pBuf + m_ulCurrPos;
	
	while (avail < ulSize)
	{
		memcpy(pDstPos, pSrcPos, avail);
		pDstPos += avail;
		ulSize -= avail;
		int retval = Load();
		if (eDataOk != retval)
			throw retval;

		avail = m_ulBytesAvailable - m_ulCurrPos;
		pSrcPos = m_pBuf + m_ulCurrPos;
	}
	
	// -- assert avail >= ulSize)
	if (ulSize > 0)
	{
		memcpy(pDstPos, pSrcPos, ulSize);
		m_ulCurrPos += ulSize;
	}
}

void CDataCache::ReadString(string& rStr)
{
	if (eRead != m_iMode) throw eModeError;
	size_t ulSize = 0;
	ReadData(&ulSize, sizeof(size_t));
	
	rStr.clear();
	size_t avail = m_ulBytesAvailable - m_ulCurrPos;
	
	const char *pSrcPos = m_pBuf + m_ulCurrPos;
	
	
	while (avail < ulSize)
	{
		rStr.append(pSrcPos, avail);
		ulSize -= avail;
		int retval = Load();
		if (eDataOk != retval)
			throw retval;

		avail = m_ulBytesAvailable - m_ulCurrPos;
		pSrcPos = m_pBuf + m_ulCurrPos;
	}
	
	// -- assert avail >= ulSize)
	if (ulSize > 0)
	{
		rStr.append(pSrcPos, ulSize);
		m_ulCurrPos += ulSize;
	}
	
}


char* CDataCache::ReadCString(void)
{
	if (eRead != m_iMode) throw eModeError;
	size_t ulSize = 0;
	
	ReadData(&ulSize, sizeof(size_t));
	
	size_t avail = m_ulBytesAvailable - m_ulCurrPos;
	
	char *pData = new char [ulSize + 1], *pDst = pData;
	memset(pData, 0, ulSize * sizeof(char));
	
	const char *pSrcPos = m_pBuf + m_ulCurrPos;
	
	
	while (avail < ulSize)
	{
		strncpy(pDst, pSrcPos, avail);
		ulSize -= avail;
		pDst += avail;
		int retval = Load();
		if (eDataOk != retval)
		{
			delete []pData;
			throw retval;
		}

		avail = m_ulBytesAvailable - m_ulCurrPos;
		pSrcPos = m_pBuf + m_ulCurrPos;
	}
	
	// -- assert avail >= ulSize)
	if (ulSize > 0)
	{
		strncpy(pDst, pSrcPos, ulSize);
		m_ulCurrPos += ulSize;
	}
	
	return pData;
	
}


