#ifndef _xbCTXPROGRESS_
#define _xbCTXPROGRESS_
/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbCtxProgress
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/

#ifndef _xbLOCK_
#	include "xbLock.h"
#endif

#ifndef _xbPTR_
#	include "xbPtr.h"
#endif

class xbEXPORT xbCtxProgressInfo:public xbCtxBase, public sIxbProgressInfo
{
public:
	xbCtxProgressInfo(LPCSTR pName,LPCSTR pLabel,bool bMainStage = false,bool bAddToThreadCtx = true)
		:m_name(pName?pName:""),m_label(pLabel?pLabel:""),m_bMainStage(bMainStage)
	{
		m_pPrgReport = NULL;
		if (bAddToThreadCtx)
			AddToThreadCtx();
	}
	virtual ~xbCtxProgressInfo()
	{
		if (!m_pThCtx)
			return;
		eProtect(RemoveFromThreadCtx());
	}
	void AddToThreadCtx()
	{
		if (m_pThCtx)
			return;
		xbCtxBase::AddToThreadCtx();
		m_pPrgReport = NULL;
		m_pPrgReport = GetProgressReport();
		if (m_pPrgReport)
			m_pPrgReport->stageStarted(this,m_bMainStage);
	}
	void RemoveFromThreadCtx()
	{
		if (!m_pThCtx)
			return;
		
		eProtectBegin();
		sIxbProgressReport *pPrgReport = m_pPrgReport;
		m_pPrgReport = NULL;
		if (pPrgReport)
			pPrgReport->stageEnded(this,m_bMainStage);
		eProtectEndTrace();

		xbCtxBase::RemoveFromThreadCtx();
	}
	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(sIxbProgressInfo);
		return xbCtxBase::GetInterface(riid,ppInterf);
	}

//sIxbThreadContextObject methods
	virtual void	ThreadContextRelease()
	{
		xbCtxBase::ThreadContextRelease();
	}
	virtual sIxbThreadContextObject *ThreadContextOld()
	{
		return xbCtxBase::ThreadContextOld();
	}

//sIxbProgressInfo methods
	sIxbProgressInfo *previousInfo()
	{
		if (!m_pThCtxObjOld)
			return NULL;

		sIxbProgressInfo *pInfo = NULL;
		xbCastIn<sIxbProgressInfo>(m_pThCtxObjOld,&pInfo);
		return pInfo;
	}
	virtual void stageInfoGetThis(xbString *pName,xbString *pLabel, uint64 *pStep, uint64 *pStepCnt, xbString *pUnit,xbProgressStatus *pStatus, xbString *pStatusTxt)
	{
		if (pName)
			*pName = m_name;
		if (pLabel)
			*pLabel = m_label;
		if (pStep)
			*pStep = 0;
		if (pStepCnt)
			*pStepCnt = 0;
		if (pUnit)
			*pUnit = xbString();
		if (pStatus)
			*pStatus = xbProgressIn;
		if (pStatusTxt)
			*pStatusTxt = xbString();
	}
	virtual bool stageInfoGet(int no,xbString *pName,xbString *pLabel, uint64 *pStep, uint64 *pStepCnt, xbString *pUnit,xbProgressStatus *pStatus, xbString *pStatusTxt)
	{
		if (no <= 0)
			return false;
		if (no == 1)
		{
			stageInfoGetThis(pName,pLabel,pStep,pStepCnt,pUnit,pStatus,pStatusTxt);
			return true;
		}
		
		sIxbProgressInfo *pPrevInfo = previousInfo();
		if (!pPrevInfo)
			return false;
		return pPrevInfo->stageInfoGet(no-1,pName,pLabel,pStep,pStepCnt,pUnit,pStatus,pStatusTxt);
	}
	virtual int stageInfoLength() const
	{
		sIxbProgressInfo *pPrevInfo = ((xbCtxProgressInfo*)this)->previousInfo();
		if (!pPrevInfo)
			return 1;
		return 1+pPrevInfo->stageInfoLength();
	}
	virtual bool stageErrorGet(int no,LPCSTR pName,xbString *pMsg,xbString *pCtx,LPCSTR pFmt)
	{
		if (!pName || !*pName)
		{
			if (no <= 0)
				return false;
			if (no == 1)
				return false;
			
			sIxbProgressInfo *pPrevInfo = previousInfo();
			if (!pPrevInfo)
				return false;
			return pPrevInfo->stageErrorGet(no-1,NULL,pMsg,pCtx,pFmt);
		}
		sIxbProgressInfo *pPrevInfo = previousInfo();
		if (!pPrevInfo)
			return false;
		return pPrevInfo->stageErrorGet(no,pName,pMsg,pCtx,pFmt);
	}
	virtual sIxbProgressReport *GetProgressReport()
	{
		if (m_pPrgReport)
			return m_pPrgReport;
		sIxbProgressInfo *pPrevInfo = previousInfo();
		if (!pPrevInfo)
			return false;
		return pPrevInfo->GetProgressReport();
	}
	void SetProgressReport(sIxbProgressReport *pPrgReport)
	{
		m_pPrgReport = pPrgReport;
	}
protected:
	xbString m_name,m_label;
	sIxbProgressReport *m_pPrgReport;
	bool m_bMainStage;
};

class xbEXPORT xbCtxProgress:public xbCtxProgressInfo, public sIxbProgress
{
public:
	xbCtxProgress(LPCSTR pName,LPCSTR pLabel,LPCSTR pUnit,bool bMainStage = false,bool bAddToThreadCtx = true)
		:xbCtxProgressInfo(pName,pLabel,bMainStage,false)
	{
		m_status = xbProgressIn;
		m_step = m_stepCnt = 0;
		if (pUnit && *pUnit)
			m_unit = pUnit;

		if (bAddToThreadCtx)
			AddToThreadCtx();
	}
	virtual ~xbCtxProgress()
	{
		if (!m_pThCtx)
			return;
		eProtect(RemoveFromThreadCtx());
	}

	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(sIxbProgress);
		return xbCtxProgressInfo::GetInterface(riid,ppInterf);
	}

	sIxbProgress *previousPrg()
	{
		if (!m_pThCtxObjOld)
			return NULL;

		sIxbProgress *pInfo = NULL;
		xbCastIn<sIxbProgress>(m_pThCtxObjOld,&pInfo);
		return pInfo;
	}

//sIxbThreadContextObject methods
	virtual void	ThreadContextRelease()
	{
		xbCtxProgressInfo::ThreadContextRelease();
	}
	virtual sIxbThreadContextObject *ThreadContextOld()
	{
		return xbCtxProgressInfo::ThreadContextOld();
	}

//sIxbProgress methods
	virtual sIxbProgressReport *GetProgressReport()
	{
		return xbCtxProgressInfo::GetProgressReport();
	}
	virtual bool stageInfoGet(int no,xbString *pName,xbString *pLabel, uint64 *pStep, uint64 *pStepCnt, xbString *pUnit,xbProgressStatus *pStatus, xbString *pStatusTxt)
	{
		return xbCtxProgressInfo::stageInfoGet(no,pName,pLabel,pStep,pStepCnt,pUnit,pStatus,pStatusTxt);
	}	
	virtual bool stageErrorGet(int no,LPCSTR pName,xbString *pMsg,xbString *pCtx,LPCSTR pFmt)
	{
		return xbCtxProgressInfo::stageErrorGet(no,pName,pMsg,pCtx,pFmt);
	}	
	virtual int stageInfoLength() const
	{
		return xbCtxProgressInfo::stageInfoLength();
	}

	virtual void stageInfoGetThis(xbString *pName,xbString *pLabel, uint64 *pStep, uint64 *pStepCnt, xbString *pUnit,xbProgressStatus *pStatus, xbString *pStatusTxt)
	{
		xbCtxProgressInfo::stageInfoGetThis(pName,pLabel,pStep,pStepCnt,pUnit,pStatus,pStatusTxt);
		if (pStep)
			*pStep = m_step;
		if (pStepCnt)
			*pStepCnt = m_stepCnt;
		if (pUnit)
			*pUnit = m_unit;
	}
//sIxbProgress methods
	virtual void stageStart(LPCSTR pName, LPCSTR pLabel, uint64 stepCnt, LPCSTR pUnit = NULL)
	{
		if (!pName || !*pName || xbStricmp(pName,m_name) == 0)
		{
			m_step = 0;
			if (pLabel && *pLabel)
				m_label = pLabel;
			if (pUnit && *pUnit)
				m_unit = pUnit;
			if (stepCnt!=uint64(-1))
				m_stepCnt = stepCnt;
			if (m_pPrgReport)
				m_pPrgReport->stageStarted((sIxbProgress*)this,m_bMainStage);
			return;
		}
		sIxbProgress *pPrg = previousPrg();
		if (pPrg)
			pPrg->stageStart(pName,pLabel,stepCnt,pUnit);
	}
	virtual void stageStatus(LPCSTR pName, xbProgressStatus status, LPCSTR pStatus)
	{
		if (!pName || !*pName || xbStricmp(pName,m_name) == 0)
		{
			m_status = status;
			if (pStatus)
				m_statusTxt = pStatus;
			if (m_pPrgReport)
				m_pPrgReport->stageChanged((sIxbProgress*)this,m_bMainStage);
			return;
		}
		sIxbProgress *pPrg = previousPrg();
		if (pPrg)
			pPrg->stageStatus(pName,status,pStatus);
	}
	virtual void stageStep(LPCSTR pName, uint64 step, LPCSTR pUnit = NULL, bool bAddStep = false)
	{
		if (!pName || !*pName || xbStricmp(pName,m_name) == 0)
		{
			checkStop(m_label);
			if (pUnit && *pUnit)
				m_unit = pUnit;
			if (bAddStep)
				m_step += step;
			else
				m_step = step;
			if (m_pPrgReport)
				m_pPrgReport->stageChanged((sIxbProgress*)this,m_bMainStage);
			return;
		}
		sIxbProgress *pPrg = previousPrg();
		if (pPrg)
			pPrg->stageStep(pName,step,pUnit,bAddStep);
		else
			checkStop(m_label);
	}
	void stageStepInc(LPCSTR pName,uint64 step)
	{
		stageStep(pName, step, NULL, true);
	}
	virtual void stageStepCount(LPCSTR pName, uint64 stepCnt, LPCSTR pUnit = NULL, bool bAddStepCnt = false)
	{
		if (!pName || !*pName || xbStricmp(pName,m_name) == 0)
		{
			if (pUnit && *pUnit)
				m_unit = pUnit;
			if (bAddStepCnt)
				m_stepCnt += stepCnt;
			else
				m_stepCnt = stepCnt;
			if (m_pPrgReport)
				m_pPrgReport->stageChanged((sIxbProgress*)this,m_bMainStage);
			return;
		}
		sIxbProgress *pPrg = previousPrg();
		if (pPrg)
			pPrg->stageStepCount(pName,stepCnt,pUnit,bAddStepCnt);
	}
	void stageStepCountInc(LPCSTR pName,uint64 stepCnt)
	{
		stageStepCount(pName,stepCnt,NULL,true);
	}
	virtual void stageEnd(LPCSTR pName)
	{
		if (!pName || !*pName || xbStricmp(pName,m_name) == 0)
		{
			if (m_pPrgReport)
				m_pPrgReport->stageEnded((sIxbProgress*)this,m_bMainStage);
			return;
		}
		sIxbProgress *pPrg = previousPrg();
		if (pPrg)
			pPrg->stageEnd(pName);
	}
	virtual void stageError(LPCSTR pName,xbEbase *pE,LPCSTR pMsg,bool bEdel = false)
	{
		if (!pName || !*pName || xbStricmp(pName,m_name) == 0)
		{
			xbString msg;
			if (pMsg && *pMsg)
			{
				msg = pMsg;
				if (pE)
					msg += "\n";
			}
			if (pE)
				msg += ::xbGetMsg("raw",pE);

			m_errorMsg = msg;
			m_errorCtx = ::xbGetCtx("raw",pE);
			if (bEdel)
				pE->Delete();
			if (m_pPrgReport)
				m_pPrgReport->stageInError((sIxbProgress*)this,m_bMainStage);
			return;
		}
		sIxbProgress *pPrg = previousPrg();
		if (pPrg)
		{
			pPrg->stageError(pName,pE,pMsg,bEdel);
			return;
		}
		if (bEdel)
			pE->Delete();
	}
protected:
	xbString			m_unit;
	uint64				m_step,m_stepCnt;
	xbProgressStatus	m_status;
	xbString			m_statusTxt;
	xbString			m_errorMsg,m_errorCtx;
};

class xbCtxProgressNotif:public xbTctxGet<sIxbProgress>
{
public:
	xbCtxProgressNotif(LPCSTR pName)
		:xbTctxGet(false),m_pName(pName)
	{
		m_pPrg = Get(xbOp_Optional);
	}
	xbCtxProgressNotif(xbCtxProgress *pPrg,LPCSTR pName)
		:xbTctxGet(false),m_pName(pName)
	{
		if (!pPrg)
			m_pPrg = Get(xbOp_Optional);
		else
			m_pPrg = pPrg;
	}
	void stageStart(LPCSTR pLabel, uint64 stepCnt, LPCSTR pUnit)
	{
		if (!m_pPrg)
			return;
		m_pPrg->stageStart(m_pName,pLabel,stepCnt,pUnit);
	}
	void stageStatus(xbProgressStatus status, LPCSTR pStatus)
	{
		if (!m_pPrg)
			return;
		m_pPrg->stageStatus(m_pName,status,pStatus);
	}
	void stageStep(uint64 step, LPCSTR pUnit = NULL, bool bAddStep = false)
	{
		if (!m_pPrg)
			return;
		m_pPrg->stageStep(m_pName,step,pUnit,bAddStep);
	}
	void stageStepInc(uint64 step)
	{
		if (!m_pPrg)
			return;
		m_pPrg->stageStep(m_pName,step,NULL,true);
	}
	void stageStepCount(uint64 stepCnt, LPCSTR pUnit = NULL, bool bAddStepCnt = false)
	{
		if (!m_pPrg)
			return;
		m_pPrg->stageStepCount(m_pName,stepCnt,pUnit,bAddStepCnt);
	}
	void stageStepCountInc(uint64 stepCnt)
	{
		if (!m_pPrg)
			return;
		m_pPrg->stageStepCount(m_pName,stepCnt,NULL,true);
	}
	void stageEnd()
	{
		if (!m_pPrg)
			return;
		m_pPrg->stageEnd(m_pName);
	}
	void stageError(xbEbase *pE,LPCSTR pMsg,bool bEdel = false)
	{
		if (!m_pPrg)
		{
			if (bEdel)
				pE->Delete();
			return;
		}
		m_pPrg->stageError(m_pName,pE,pMsg,bEdel);
	}
protected:
	sIxbProgress *m_pPrg;
	LPCSTR m_pName;
};


#endif
