#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>    /* isdegit() */
#include <assert.h>
#include "iofunc.h"

#ifndef min
#define min(a,b) ((a < b) ? a : b)
#endif

int IOFUNC::m_iobuff_count = 0;
IOFUNC::IOFUNC()
{
}

IOBUFF * IOFUNC::AllocIOBuff(int buffsize)
{
	IOBUFF *pIOBuff = new IOBUFF;

	if(!pIOBuff) return NULL;
	m_iobuff_count++;
	pIOBuff->buff = new char[buffsize+1];
	memset(pIOBuff->buff, 0, buffsize+1);
	pIOBuff->iFP = pIOBuff->iRP = 0;
	pIOBuff->buffsize = buffsize;
	pIOBuff->bCQ = false;//TRUE이면 환형큐로 동작하여 RP,FP가 로테이션한다.
	return pIOBuff;
}

void IOFUNC::FreeIOBuff(IOBUFF *pIOBuff)
{
	if(pIOBuff->buff)
		delete pIOBuff->buff;
	pIOBuff->buff = NULL;
	delete pIOBuff;
	m_iobuff_count--;
}

void IOFUNC::Clear(IOBUFF *pIOBuff)
{
	pIOBuff->iFP = pIOBuff->iRP = 0;
}

//Get Data size
int	IOFUNC::GetDataSize(IOBUFF *pIOBuff)
{
	if(pIOBuff->iFP == pIOBuff->iRP)
		return 0;
	return (pIOBuff->iFP < pIOBuff->iRP) ? pIOBuff->iRP - pIOBuff->iFP : 
		(pIOBuff->buffsize - pIOBuff->iFP) + pIOBuff->iRP;
}

//Get Free Queue size
int	IOFUNC::GetAvailSize(IOBUFF *pIOBuff)
{
	int nDataSize = GetDataSize(pIOBuff);
	return pIOBuff->buffsize - nDataSize;
}

//Move Rear Pointer
int	IOFUNC::ShiftRear(IOBUFF *pIOBuff,int nShift)
{
	pIOBuff->iRP = ( pIOBuff->iRP + nShift ) % pIOBuff->buffsize;
	return pIOBuff->iRP;
}

// Move Front Pointer
// RP가 경계(pIOBuff->buffsize)를 넘어 FP보다 작게 되어 데이타가 물리적으로 양분하는 일이 없도록
// 항상 RP만큼 추가된 데이타를 처리후 FP를 조정할때 RP까지 모두 처리하면 0으로 리셋하여
// 데이타가 항상 물리적으로 순차적이도록 보정한다.GetFrontPtr로 데이타를 한번에 읽어올수
// 있도록..

int	IOFUNC::ShiftFront(IOBUFF *pIOBuff,int nShift)
{
	if(pIOBuff->bCQ){
		pIOBuff->iFP = (pIOBuff->iFP + nShift) % pIOBuff->buffsize;
	}
	else{
		int newFP = (pIOBuff->iFP + nShift) % pIOBuff->buffsize;
		int nDataSize = pIOBuff->iRP - newFP;

		memcpy(pIOBuff->buff + pIOBuff->iFP,pIOBuff->buff + newFP,nDataSize);
		pIOBuff->iRP = ( pIOBuff->iRP - nShift ) % pIOBuff->buffsize;
	}
	//below 2lines rewind RP to zero index for squencial data(normally, FP <= RP,
	//do not allow overflow RP like RP <= FP)
	if(pIOBuff->iFP == pIOBuff->iRP)
		pIOBuff->iFP = pIOBuff->iRP = 0;

	return pIOBuff->iFP;
}

//Get Data Buffer From nOffset
//lpRestQueueSize : Free Queue size from nOffset to end of Queue
char * IOFUNC::GetOffsetPtr(IOBUFF *pIOBuff,int nOffset, int *lpRestQueueSize)
{
	nOffset = nOffset % pIOBuff->buffsize;
	if(lpRestQueueSize)
		*lpRestQueueSize = (nOffset < pIOBuff->iFP) ? pIOBuff->iFP - nOffset : pIOBuff->buffsize - nOffset;
	return (pIOBuff->buff + nOffset);
}

//Get Data Buffer From Rear Prointer
//lpAvailQueueSize : Free Queue size
char * IOFUNC::GetRearPtr(IOBUFF *pIOBuff,int *lpAvailQueueSize)
{
	if(lpAvailQueueSize)
		*lpAvailQueueSize = (pIOBuff->iRP < pIOBuff->iFP) ? pIOBuff->iFP - pIOBuff->iRP/* - 1*/ : pIOBuff->buffsize - pIOBuff->iRP;
	return (pIOBuff->buff + pIOBuff->iRP);
}

//Get Data Buffer From Front Prointer
//lpRestDataLen : Get Data size from Front Pointer to end of Queue
// |-->F         R-->| , R - F size
// |-->R         F-->| , F--> size only(except -->R)
char * IOFUNC::GetFrontPtr(IOBUFF *pIOBuff,int *lpRestDataLen)
{
	if(lpRestDataLen)
		*lpRestDataLen = (pIOBuff->iFP < pIOBuff->iRP) ? pIOBuff->iRP - pIOBuff->iFP : pIOBuff->buffsize - pIOBuff->iFP;
	return pIOBuff->buff + pIOBuff->iFP;
}

//Add Data to Rear Pointer
//bShiftRear : TRUE = Increase Rear Pointer
bool IOFUNC::AddRear(IOBUFF *pIOBuff,char *pData, int nLen, bool bShiftRear)
{
	int nDataSize = GetDataSize(pIOBuff);

	if((pIOBuff->buffsize - nDataSize)/*-1*/ < nLen)
		return false;

	int nRestQueueSize = 0;
	char *pDest = GetOffsetPtr(pIOBuff,pIOBuff->iRP, &nRestQueueSize);
	int	nCopyLen = min(nRestQueueSize,nLen);
	memcpy(pDest, pData, nCopyLen);

	if(nCopyLen < nLen){
		int nOffset = (pIOBuff->iRP + nCopyLen) % pIOBuff->buffsize;
		assert(nOffset == 0);
		memcpy(pIOBuff->buff + nOffset,pData + nCopyLen, nLen - nCopyLen);
	}
	
	if(bShiftRear)
		ShiftRear(pIOBuff,nLen);
	return true;
}

//Insert Data to Specified Pointer
bool IOFUNC::Insert(IOBUFF *pIOBuff,int nOffset, char *pData, int nLen)
{
	nOffset = nOffset % pIOBuff->buffsize;

	int	nRestQueueSize = 0;
	char *pDest = GetOffsetPtr(pIOBuff,nOffset,&nRestQueueSize);
	int	nCopyLen = min(nRestQueueSize,nLen);
	memcpy(pDest, pData, nCopyLen);
	
	if(nCopyLen < nLen)
		memcpy(pIOBuff->buff,pData + nCopyLen,nLen - nCopyLen);
	return true;
}

//GetData from Front Pointer
//bShiftFront: TRUE = Increase Front Pointer
//큐버퍼에 원하는 nGetSize보다 충분히 큰 데이타가 들어 있는 경우만 처리
bool IOFUNC::GetData(IOBUFF *pIOBuff,char *pDest, int nGetSize, bool bShiftFront)
{
	int nDataSize = GetDataSize(pIOBuff);
	if(nDataSize < nGetSize)//데이타가 없거나 작은 경우
		return false;

	int	nRestDataLen = 0;//Front Pointer 이후의 큐버퍼 끝까지 데이타 길이
	char *pSrc = GetFrontPtr(pIOBuff,&nRestDataLen);
	int	nCopyLen = min(nRestDataLen,nGetSize);
	memcpy(pDest, pSrc, nCopyLen);

	if(nCopyLen < nGetSize){ // |-->R         F-->| 인 경우 -->R 의 나머지 데이타 복사
		int nOffset = (pIOBuff->iFP + nCopyLen) % pIOBuff->buffsize;
		assert(nOffset == 0);
		char *pPtr = GetOffsetPtr(pIOBuff,nOffset);
		memcpy(pDest+nCopyLen,pPtr,nGetSize - nCopyLen);
	}

	if(bShiftFront)
		ShiftFront(pIOBuff,nGetSize);
	return true;
}

//GetData from nOffset
bool IOFUNC::GetData(IOBUFF *pIOBuff,int nOffset, char *pDest, int nGetSize)
{
	int nDataSize = GetDataSize(pIOBuff);
	if(nDataSize < nGetSize)
		return false;

	nOffset = nOffset % pIOBuff->buffsize;

	int	nRestQueueSize = 0;
	char *pSrc = GetOffsetPtr(pIOBuff,nOffset, &nRestQueueSize);
	int	nCopyLen = min(nRestQueueSize,nGetSize);
	memcpy(pDest, pSrc, nCopyLen);

	if(nCopyLen < nGetSize){
		nOffset = (nOffset + nCopyLen) % pIOBuff->buffsize;
		assert(nOffset == 0);
		char *pPtr = GetOffsetPtr(pIOBuff,nOffset);
		memcpy(pDest + nCopyLen,pPtr, nGetSize-nCopyLen);
	}
	return true;
}
