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

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef _xbSTRING_
#	include "xbString.h"
#endif
#ifndef _xbEXCEPTION_
#	include "xbException.h"
#endif
#ifndef _xbDATETIME_
#	include "xbDateTime.h"
#endif

struct xbEXPORT comcfgIP
{
	short	m_ip[4];
	comcfgIP()
	{
		m_ip[0] = m_ip[1] = m_ip[2] = m_ip[3] = -2;
	}
	void Init(short val = -2)
	{
		m_ip[0] = m_ip[1] = m_ip[2] = m_ip[3] = val;
	}
	void Init(short ip1,short ip2,short ip3,short ip4)
	{
		m_ip[0] = ip1;
		m_ip[1] = ip2;
		m_ip[2] = ip3;
		m_ip[3] = ip4;
	}
	void Empty()
	{
		Init();
	}

	bool IsEmpty() const
	{
		return m_ip[0] < 0 && m_ip[1] < 0 && m_ip[2] < 0 && m_ip[3] < 0;
	}
	bool IsValid() const
	{
		return m_ip[0] >= 0 || m_ip[1] >= 0 || m_ip[2] >= 0 || m_ip[3] >= 0;
	}
	bool IsValidIP() const
	{
		return m_ip[0] >= 0 && m_ip[1] >= 0 && m_ip[2] >= 0 && m_ip[3] >= 0;
	}
	xbString Get() const
	{
		xbString val;
		if (m_ip[0] >= 0) val += xbString(m_ip[0]);
		else val += '*';
		val+='.';
		if (m_ip[1] >= 0) val += xbString(m_ip[1]);
		else val += '*';
		val+='.';
		if (m_ip[2] >= 0) val += xbString(m_ip[2]);
		else val += '*';
		val+='.';
		if (m_ip[3] >= 0) val += xbString(m_ip[3]);
		else val += '*';
		return val;
	}
	bool Set(const xbString &val,xbOpMode opm =xbOp_Required)
	{
		if (val.IsEmpty())
		{
			m_ip[0] = -1;
			m_ip[1] = -1;
			m_ip[2] = -1;
			m_ip[3] = -1;
			return true;
		}
		xbArray<xbString> arr;
		if (val.Split('.',&arr) != 4)
		{
			if (opm == xbOp_Optional) 
				return false;
			xbThrow xbEappCfg("Invalid IP : '"+val+"' (Syntax: X1.X2.X3.X4)");
		}
		if (arr.GetAt(0) == "*") m_ip[0] = -1;
		else 
		{
			char *ptr = NULL;
			int ip = strtol(arr.GetAt(0),&ptr,10);
			if ((ptr != NULL && *ptr != 0) || ip < 0|| ip > 255)
			{
				if (opm == xbOp_Optional) 
					return false;
				xbThrow xbEappCfg("Invalid IP : '"+val+"' (Syntax: X1.X2.X3.X4, X1 incorrect)");
			}
			m_ip[0] = short(ip);
		}
		if (arr.GetAt(1) == "*") m_ip[1] = -1;
		else 
		{
			char *ptr = NULL;
			int ip = strtol(arr.GetAt(1),&ptr,10);
			if ((ptr != NULL && *ptr != 0) || ip < 0|| ip > 255)
			{
				if (opm == xbOp_Optional) 
					return false;
				xbThrow xbEappCfg("Invalid IP : '"+val+"' (Syntax: X1.X2.X3.X4, X1 incorrect)");
			}
			m_ip[1] = short(ip);
		}

		if (arr.GetAt(2) == "*") m_ip[2] = -1;
		else 
		{
			char *ptr = NULL;
			int ip = strtol(arr.GetAt(2),&ptr,10);
			if ((ptr != NULL && *ptr != 0) || ip < 0|| ip > 255)
			{
				if (opm == xbOp_Optional) 
					return false;
				xbThrow xbEappCfg("Invalid IP : '"+val+"' (Syntax: X1.X2.X3.X4, X3 incorrect)");
			}
			m_ip[2] = short(ip);
		}

		if (arr.GetAt(3) == "*") m_ip[3] = -1;
		else 
		{
			char *ptr = NULL;
			int ip = strtol(arr.GetAt(3),&ptr,10);
			if ((ptr != NULL && *ptr != 0) || ip < 0|| ip > 255)
			{
				if (opm == xbOp_Optional) 
					return false;
				xbThrow xbEappCfg("Invalid IP : '"+val+"' (Syntax: X1.X2.X3.X4, X4 incorrect)");
			}
			m_ip[3] = short(ip);
		}

		return true;
	}

	int Compare(int ip0,int ip1,int ip2,int ip3,bool bIgnoreWildCards = false) const
	{
		if (bIgnoreWildCards || (m_ip[0] >= 0 && ip0 >= 0))
		{
			if (m_ip[0] != ip0) return (int(m_ip[0]) - ip0);
		}
		if (bIgnoreWildCards || (m_ip[1] >= 0 && ip1 >= 0))
		{
			if (m_ip[1] != ip1) return (int(m_ip[1]) - ip1);
		}
		if (bIgnoreWildCards || (m_ip[2] >= 0 && ip2 >= 0))
		{
			if (m_ip[2] != ip2) return (int(m_ip[2]) - ip2);
		}
		if (bIgnoreWildCards || (m_ip[3] >= 0 && ip3 >= 0))
		{
			if (m_ip[3] != ip3) return (int(m_ip[3]) - ip3);
		}
		return 0;
	}

	int Compare(const comcfgIP &ip,bool bIgnoreWildCards = false) const
	{
		return Compare(ip.m_ip[0],ip.m_ip[1],ip.m_ip[2],ip.m_ip[3],bIgnoreWildCards);
	}
};

enum comcfgTimeFormat
{
	comcfgTimeStd =			0x00000000,
		comcfgTimeShort =	0x10000000,
		comcfgTimeLong =	0x20000000,
		comcfgTimeDflt = comcfgTimeStd
};

struct xbEXPORT comcfgTime //[weekday] [YYYY/MM/DD] [HH:MM]
{
	short	m_week,m_year,m_month,m_day,m_hour,m_min,m_sec,m_msec;

	comcfgTime()
	{
		m_week = m_year = m_month = m_day = m_hour = m_min = m_sec = m_msec = -1;
	}
	comcfgTime(const SYSTEMTIME &systi)
	{
		Init(systi);
	}
	comcfgTime(const FILETIME &fti)
	{
		Init(fti);
	}
	comcfgTime(const xbDateTime &dt)
	{
		Init(dt);
	}
	explicit comcfgTime(const xbString &str,xbString *pTZ = NULL,
		LPCSTR *ppDayNames = NULL,LPCSTR *ppMonthNames = NULL,xbOpMode opm =xbOp_Required)
	{
		m_week = m_year = m_month = m_day = m_hour = m_min = m_sec = m_msec = -1;
		Set(str);
	}
	void Init(int val = -1)
	{
		m_week = m_year = m_month = m_day = m_hour = m_min = m_sec = m_msec = val;
	}
	void InitToUtcNow()
	{
		SYSTEMTIME systi;
		::GetSystemTime(&systi); //no error return
		Init(systi);
	}
	void Init(const xbDateTime &dt);
	void Init(const FILETIME &fti)
	{
		SYSTEMTIME systi;
		if (!::FileTimeToSystemTime(&fti,&systi))
			xbThrow xbEsys(xbHR_LAST,"comcfgTime::Init() : Can't convert file time to system time object");
		Init(systi);
	}
	void Init(const SYSTEMTIME &systi)
	{
		m_week = xbDayOfWeekFromMS(systi.wDayOfWeek);
		m_year = systi.wYear;
		m_month = systi.wMonth;
		m_day = systi.wDay;
		m_hour = systi.wHour;
		m_min = systi.wMinute;
		m_sec = systi.wSecond;
		m_msec = systi.wMilliseconds;
	}
	void Init(int wd,int y, int mo, int d, int h, int mi, int s,int ms)
	{
		m_week = wd;
		m_year = y;
		m_month = mo;
		m_day = d;
		m_hour = h;
		m_min = mi;
		m_sec = s;
		m_msec = ms;
	}
	bool IsEmpty() const
	{
		return m_week < 0 && m_year <= 0 && m_month <= 0 && m_day <= 0 && m_hour < 0 && m_min < 0 && m_sec < 0 && m_msec < 0;
	}
	bool IsValid() const
	{
		return m_week >= 0 || m_year > 0 || m_month > 0 || m_day > 0 || m_hour >= 0 || m_min >= 0 || m_sec >= 0 || m_msec >= 0;
	}
	inline bool IsFixedDate() const
	{
		bool bEnd = false;
		if (m_year < 0)
			return false;
		if (m_month < 0)
			bEnd = true;
		if (m_day < 0)
			bEnd = true;
		else
			if (bEnd)
				return false;
		if (m_hour < 0)
			bEnd = true;
		else
			if (bEnd)
				return false;
		if (m_min < 0)
			bEnd = true;
		else
			if (bEnd)
				return false;
		if (m_sec < 0)
			bEnd = true;
		else
			if (bEnd)
				return false;
		if (m_hour < 0)
			bEnd = true;
		else
			if (bEnd)
				return false;
		if (m_hour < 0)
			bEnd = true;
		else
			if (bEnd)
				return false;
		return true;
	}
	xbString Get(comcfgTimeFormat frmt = comcfgTimeDflt,LPCSTR pTZ = NULL,LPCSTR *ppDayNames = NULL,LPCSTR *ppMonthNames = NULL) const;
	xbString GetDate(comcfgTimeFormat frmt = comcfgTimeDflt,LPCSTR *ppDayNames = NULL,LPCSTR *ppMonthNames = NULL) const;
	xbString GetTime(LPCSTR pTZ = NULL) const;
	bool Set(const xbString &val,xbString *pTZ = NULL,
		LPCSTR *ppDayNames = NULL,LPCSTR *ppMonthNames = NULL,xbOpMode opm =xbOp_Required);
	int Compare(int wd,int y, int mo, int d, int h, int mi, int s, bool bIgnoreWildCards = false) const
	{
		if (bIgnoreWildCards || (m_week >= 0 && wd >= 0))
		{
			if (m_week != wd) return (int(m_week) - wd);
		}
		if (bIgnoreWildCards || (m_year > 0 && y > 0))
		{
			if (m_year != y) return (int(m_year) - y);
		}
		if (bIgnoreWildCards || (m_month > 0 && mo > 0))
		{
			if (m_month != mo) return (int(m_month) - mo);
		}
		if (bIgnoreWildCards || (m_day > 0 && d > 0))
		{
			if (m_day != d) return (int(m_day) - d);
		}
		if (bIgnoreWildCards || (m_hour >= 0 && h >= 0))
		{
			if (m_hour != h) return (int(m_hour) - h);
		}
		if (bIgnoreWildCards || (m_min >= 0 && mi >= 0))
		{
			if (m_min != mi) return (int(m_min) - mi);
		}
		if (bIgnoreWildCards || (m_sec >= 0 && s >= 0))
		{
			if (m_sec != s) return (int(m_sec) - s);
		}
/*TODO?		if (bIgnoreWildCards || (m_msec >= 0 && ms >= 0))
		{
			if (m_msec != ms) return (int(m_sec) - ms);
		}*/
		return 0;
	}
	int Compare(const comcfgTime &ti,bool bIgnoreWildCards = false) const
	{
		return Compare(ti.m_week,ti.m_year,ti.m_month,ti.m_day,ti.m_hour,ti.m_min,ti.m_sec,bIgnoreWildCards);
	}
	int Compare(const SYSTEMTIME &systi,bool bIgnoreWildCards = false) const
	{
		comcfgTime ti(systi);
		return Compare(ti.m_week,ti.m_year,ti.m_month,ti.m_day,ti.m_hour,ti.m_min,ti.m_sec,bIgnoreWildCards);
	}

	void ToSystemTime(SYSTEMTIME *pSysTi) const
	{
		pSysTi->wDayOfWeek = xbDayOfWeekToMS(m_week);
		pSysTi->wDay = m_day;
		pSysTi->wMonth = m_month;
		pSysTi->wYear = max(0,m_year);
		pSysTi->wHour = max(0,m_hour);
		pSysTi->wMinute = max(0,m_min);
		pSysTi->wSecond = max(0,m_sec);
		pSysTi->wMilliseconds = max(0,m_msec);
	}
	void ToFileTime(FILETIME *pFileTi) const
	{
		SYSTEMTIME	ti;
		ToSystemTime(&ti);
		if (!::SystemTimeToFileTime(&ti,pFileTi))
			xbThrow xbEsys(xbHR_LAST,"comcfgTime::ToFileTime() : Can't convert date to file time object");
	}
	void ToDateTime(xbDateTime *pDt) const;
	operator xbDateTime() const;
	operator SYSTEMTIME() const
	{
		SYSTEMTIME	systi;
		ToSystemTime(&systi);
		return systi;
	}
	operator FILETIME() const
	{
		FILETIME	fti;
		ToFileTime(&fti);
		return fti;
	}

	comcfgTime &operator=(const xbDateTime &dt)
	{
		Init(dt);
		return *this;
	}
	comcfgTime &operator=(const SYSTEMTIME &systi)
	{
		Init(systi);
		return *this;
	}
	comcfgTime &operator=(const FILETIME &fti)
	{
		Init(fti);
		return *this;
	}

	bool operator==(const comcfgTime &ti) const
	{
		return Compare(ti,true) == 0;
	}
	bool operator!=(const comcfgTime &ti) const
	{
		return Compare(ti,true) != 0;
	}

	comcfgTime GetStartDate(const comcfgTime &ti) const;
	bool GetNextDate(const comcfgTime &ti,xbDateTime *pNext) const;
};

#endif
