#ifndef _xbAPP_
#define _xbAPP_
/*<<<***************************************************************************
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: xbApp
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<12/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/

#ifndef _xbEXCEPTION_
#	include "xbException.h"
#endif
#ifndef _xbFILETOOLS_
#	include "xbFileTools.h"
#endif
#ifndef _xbSTREAM_
#	include "xbStream.h"
#endif
#ifndef _xbTHREAD_
#	include "xbThread.h"
#endif
#ifndef _xbCONFIG_
#	include "xbConfig.h"
#endif

enum xbStdStreamType
{
	xbStdStreamIN,
	xbStdStreamOUT,
	xbStdStreamERR
};

class xbEXPORT xbStdStream
{
public:
	xbStdStream(xbStdStreamType stmType) {m_stmType = stmType;}
	virtual ~xbStdStream() 
	{
		eProtectTrace(Close(););
	}

	xbStdStream	&operator<<(const char *pStr)
	{
		if (!fstm.IsOpened())
			Open();
		fstm << pStr;
		fstm.CacheFlush();
		return *this;
	}

	xbStdStream	&operator<<(const xbString &str)
	{
		if (!fstm.IsOpened())
			Open();
		fstm << str;
		fstm.CacheFlush();
		return *this;
	}
	virtual void Open()
	{
		if (fstm.IsOpened())
			return;
		switch(m_stmType)
		{
		case xbStdStreamIN:
			fstm.Open("STDIN$",xbOpen,xbAccessRW,xbWorkSharedRW);
			break;
		case xbStdStreamOUT:
			fstm.Open("STDOUT$",xbOpen,xbAccessW,xbWorkSharedRW);
			break;
		case xbStdStreamERR:
			try
			{
				fstm.Open("STDERR$",xbOpen,xbAccessW,xbWorkSharedRW);
			}
			catch(xbEstream *pE)
			{
				pE->Delete();
				fstm.Open("STDOUT$",xbOpen,xbAccessW,xbWorkSharedRW);
			}
			break;
		default:
			xbThrowEsys("Invalid IO stream");
		}
		fstm.SetFormat(xbFormatText);
		fstm.SetCRLFasLF(true);
		fstm.SetStreamTextCP(CP_OEMCP);
	}
	virtual void Close()
	{
		fstm.Close();
	}
protected:
	xbStdStreamType m_stmType;
	xbFileStream fstm;
};

#define	xbAPPEXE_LOGFILEBUFCNT	2
class xbEXPORT __declspec(uuid("{3CA3843B-D525-4aa9-8CAD-FD62C48CA2DD}"))
	xbAppExe:public xbAppBase
{
protected:
	xbThreadCoInit			m_coInit;
	xbUseEngineCenterExe	m_uselib;
	xbExceptionCtx			m_eCtx;
	xbCfgChangedCb			m_cfgChangedCb;
	virtual void receiveConfigChanged(xbCfgChangedCb &cbObj,xbCfgRoot root,xbCfgKeyChangesInfo *pInfo,const xbString &cfgKeyPath);

	LPCSTR					m_pszAppLogFileDflt;
	xbSectShare				m_sectCfg;
	LPCSTR					m_pOldLogFile;
	int						m_cLogBuf;
	xbString				m_logBuf[xbAPPEXE_LOGFILEBUFCNT];

	virtual void readLogSettings(bool bChg);
	virtual void setLogFile(const xbString &logfile);
public:
	xbAppExe(LPCSTR pszAppId,LPCSTR pszAppLabel = NULL,LPCSTR pszAppLogFile = NULL);
	~xbAppExe();

	void Start();
	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(xbAppExe);
		return xbAppBase::GetInterface(riid,ppInterf);
	}
};

class xbEXPORT __declspec(uuid("{B49EF8EA-7BF3-4b9d-9A81-B92E04AD5CAE}")) 
	xbAppConsole:public xbAppExe
{
protected:
	int			cArg;
	const char	**pArg;
	xbArray<xbString> m_arrArg;

	xbString	currentOp;
	xbString	appPath;
public:
	bool		bSilent;
	bool		bMbox;
	bool		bOpDone;
	xbStdStream fout,fin,ferr;

	xbAppConsole(LPCSTR pszAppId,int _cArg,const char **_pArg,bool _bSilent = false,bool _bMbox = false,LPCSTR pszAppLabel = NULL,LPCSTR pszAppLogFile = NULL);
	~xbAppConsole();

	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(xbAppConsole);
		return xbAppExe::GetInterface(riid,ppInterf);
	}
	virtual xbString	GetAppName() const
	{
		return ::xbGetFileName(appPath);
	}
	virtual xbString	GetAppPath() const
	{
		return appPath;
	}
	virtual xbString	GetAppDir() const
	{
		return ::xbGetFileDir(appPath);
	}

	bool hasArg() const
	{
		return cArg > 0 || m_arrArg.IsValid();
	}
	bool hasSwitch() const
	{
		if (cArg <= 0 && m_arrArg.IsEmpty())
			return false;
		xbString arg;
		if (m_arrArg.IsValid())
			arg = m_arrArg.First();
		else
			arg = (*pArg);
		if (arg.IsEmpty() || (arg.First() != '-' && arg.First() != '/'))
			return false;
		return true;
	}
	bool hasSwitchArg() const
	{
		if (cArg <= 0 && m_arrArg.IsEmpty())
			return false;
		xbString arg;
		if (m_arrArg.IsValid())
			arg = m_arrArg.First();
		else
			arg = (*pArg);
		if (arg.IsValid() && (arg.First() == '-' || arg.First() == '/'))
			return false;
		return true;
	}
	xbString getArg()
	{
		if (cArg <= 0 && m_arrArg.IsEmpty())
			xbThrow xbEuser("Argument missing in command line"+getForLabel());
		xbString arg;
		if (m_arrArg.IsValid())
		{
			arg = m_arrArg.First();
			m_arrArg.Delete(0);
		}
		else
		{
			arg = *(pArg++);
			cArg--;
		}
		return arg;
	}
	xbArray<char*> getArgArray(xbArray<xbString> &tmpArr)
	{
		xbArray<char*> ret;
		if (cArg <= 0 && m_arrArg.IsEmpty())
			return ret;
		while(cArg>0 || m_arrArg.IsValid())
		{
			if (m_arrArg.IsValid())
			{
				xbString arg = m_arrArg.First();
				m_arrArg.Delete(0);
				tmpArr.Add(arg);
				ret.Add((char*)arg.Ptr());
			}
			else
			{
				ret.Add((char*)*(pArg++));
				cArg--;
			}
		}
		return ret;
	}
	xbString getSwitch()
	{
		if (cArg <= 0 && m_arrArg.IsEmpty())
			xbThrow xbEuser("Switch missing in command line"+getForLabel());
		xbString arg;
		if (m_arrArg.IsValid())
		{
			arg = m_arrArg.First();
			m_arrArg.Delete(0);
		}
		else
		{
			arg = *(pArg++);
			cArg--;
		}
		
		if (arg.IsEmpty() || (arg.First() != '-' && arg.First() != '/'))
			xbThrow xbEuser("Invalid argument in command line"+getForLabel()+", '-switch' expected");

		return arg.Mid(1);
	}
	xbString getSwitchArg()
	{
		if (cArg <= 0 && m_arrArg.IsEmpty())
			xbThrow xbEuser("Switch argument missing in command line"+getForLabel());
		xbString arg;
		if (m_arrArg.IsValid())
		{
			arg = m_arrArg.First();
			m_arrArg.Delete(0);
		}
		else
		{
			arg = *(pArg++);
			cArg--;
		}
		
		if (arg.IsValid() && (arg.First() == '-' || arg.First() == '/'))
			xbThrow xbEuser("Invalid argument in command line"+getForLabel()+", switch argument expected");

		return arg;
	}

	xbString getForLabel()
	{
		if (currentOp.IsEmpty())
			return xbString();
		return  " for "+currentOp;
	}

	void OpEnd()
	{
		currentOp.Empty();
	}
	void OpStart(const xbString &currOpLabel)
	{
		currentOp = currOpLabel;
	}
	void commandLoad(const xbString &file)
	{
		int insertPos = 0;
		xbFileStream stm(file,xbOpen,xbAccessR,xbWorkShared);
		xbString line;
		while(stm.ReadString(&line,(xbString*)NULL))
		{
			line.Trim();

			if (line.IsEmpty()) continue;
			if (line.First() == '#') continue;

			xbArray<xbString> arrArg;
			line.consoleGetArg(CP_ACP,"test commands file",&arrArg);

			for(int i = 0; i < arrArg.Length;i++)
			{
				m_arrArg.Insert(insertPos++, arrArg.GetAt(i));
			}
		}
	}
	virtual bool onBreakHandlerRoutine(ulong dwCtrlType);
};

#endif
