// 時間判定箇所が増えてきたので、まとめてできるように。
// 場所により64bit演算があるが、それほど計算コストは増えないはず。
// クラス実装してるがあまり意味はなし。
//

#pragma once
#include <windows.h>

struct DateTime
{
	DWORD Date;		// 修正ユリウス日(Modified Julius Days)、32bit値にしたので2038年問題もクリアできるはず
	DWORD Time;		// 00:00からの秒数
};

// 64bit 時間値(準ユリウス日基点(1858年11月17日00:00:00)からの秒数(閏秒は含まれないが実用上問題はない))
// 64bitで表現すれば、地球がどっかんするまでは・・・オーバーフローおきないよね？
typedef __int64 DateTimeVal64;

const DateTimeVal64 DateTimeVal64_OneDay = 24*60*60;
const DateTimeVal64 DateTimeVal64_Unknown = (DateTimeVal64)(-1);
const DWORD TimeVal_OneSec = 1;
const DWORD TimeVal_OneMin = 60*TimeVal_OneSec;
const DWORD TimeVal_OneHour= 60*TimeVal_OneMin;
const DWORD TimeVal_OneDay = 24*TimeVal_OneHour;
const DWORD MjdVal_Unknown = (DWORD)(-1);
const DWORD TimeVal_Unknown = (DWORD)(-1);

class CDateTime
{
public:
	CDateTime()
	{
	}
	~CDateTime()
	{
	}
	// 現在時刻でデータを作成
	static inline struct DateTime GetCurrentDateTime()
	{
		SYSTEMTIME stTime;
		GetLocalTime(&stTime);
		return SetDateTime(stTime.wYear,stTime.wMonth,stTime.wDay,stTime.wHour,stTime.wMinute,stTime.wSecond);
	}
	// 指定時刻でデータを作成
	static inline struct DateTime SetDateTime(long Year,long Month,long Day,long Hour,long Min,long Sec)
	{
		struct DateTime stCurrent;
		stCurrent.Date=ConvertTo_MJD(Year,Month,Day);
		stCurrent.Time=ConvertTo_TimeSec(Hour,Min,Sec);
		return stCurrent;
	}
	// データを指定時間オフセットさせる
	static inline void OffsetDateTime(struct DateTime &stDateTime,DateTimeVal64 Offset)
	{
		DateTimeVal64 tmp = GetDateTimeValue(stDateTime,Offset);
		stDateTime.Date=GetMJD_FromDateTimeVal64(tmp);
		stDateTime.Time=GetTime_FromDateTimeVal64(tmp);
	}

	// 連続時間値を作成
	static inline DateTimeVal64 GetDateTimeValue(struct DateTime &stDateTime,DateTimeVal64 Offset=0)
	{
		DateTimeVal64 ret = (DateTimeVal64)stDateTime.Date * DateTimeVal64_OneDay + stDateTime.Time;
		return ret + Offset;
	}
	// 連続時間値から準ユリウス日を取得
	static inline DWORD GetMJD_FromDateTimeVal64(DateTimeVal64 DateTimeValue)
	{
		return static_cast<DWORD>(DateTimeValue / DateTimeVal64_OneDay);
	}
	// 連続時間値から当日の00:00:00からの秒数を取得
	static inline DWORD GetTime_FromDateTimeVal64(DateTimeVal64 DateTimeValue)
	{
		return static_cast<DWORD>(DateTimeValue % DateTimeVal64_OneDay);
	}

	// ２つの時間を比較
	// 正: A>B  負: A<B  0:A=B
	static inline DateTimeVal64 Compare_Time(struct DateTime &stTimeA,int iOffsetA,struct DateTime &stTimeB,int iOffsetB)
	{
		return GetDateTimeValue(stTimeA,iOffsetA) - GetDateTimeValue(stTimeB,iOffsetB);
	}

	// ３つの時間A B C を指定。
	// A-B の間に C が存在するかを検査
	static inline bool Check_Time(struct DateTime &stTimeA,int iOffsetA,struct DateTime &stTimeB,int iOffsetB,struct DateTime &stTimeC,int iOffsetC)
	{
		DateTimeVal64 a = GetDateTimeValue(stTimeA,iOffsetA);
		DateTimeVal64 b = GetDateTimeValue(stTimeB,iOffsetB);
		DateTimeVal64 c = GetDateTimeValue(stTimeC,iOffsetC);
		bool bRet=false;
		if(a>b)
		{
			if( a>=c && c>= b ) bRet = true;
		} else {
			if( b>=c && c>= a ) bRet = true;
		}
		return bRet;
	}

	// ユリウス日を作成
	static inline DWORD ConvertTo_MJD(long Year,long Month,long Day)
	{
		if(Month<=2)
		{
			Year--;
			Month+=12;
		}
		return (WORD)((int)(365.25 * Year) + (int)(Year / 400) - (int)(Year / 100) + (int)(30.59 * (Month - 2)) + Day+0.5 - 678912);
	}
	// ユリウス日を変換
	static inline void ConvertFrom_MJD(DWORD MJD,long &Year,long &Month,long &Day)
	{
		if(MJD>0)
		{
			double ajd = (double)MJD + 2400000.5;
			double t=ajd+0.5;
			long   z=(long)t;
			double f=t-z;
			long a;
			if(z<2299161)
			{
				a=z;
			} else {
				long x=(long)((z-1867216.25)/36524.25);
				a=z+1+x-(x/4); 
			}
			double b=a+1524;
			double c=(long)((b-122.1)/365.25);
			double d=(long)(365.25*c);
			double e=(long)((b-d)/30.6001);
			Day  =(long)(b-d-(long)(30.6001*e)+f);
			Month=(long)(e<14?e-1:e-13);
			Year =(long)(Month>2?c-4716:c-4715);
		} else {
			Year=Month=Day=0;
		}
	}
	// 秒数時間を作成
	static inline DWORD ConvertTo_TimeSec(long Hour,long Min,long Sec)
	{
		return (DWORD)Hour*60*60 + (DWORD)Min*60 + (DWORD)Sec;
	}
	// 秒数時間を変換
	static inline void ConvertFrom_TimeSec(DWORD dwTimeSec,long &Hour,long &Min,long &Sec)
	{
		Hour=dwTimeSec/60/60;
		Min=(dwTimeSec-Hour*60*60)/60;
		Sec=(dwTimeSec-Hour*60*60-Min*60);
	}
	// BCD記述時間を変換
	static inline DWORD ConvertFrom_BcdTime(BYTE BcdHour,BYTE BcdMin,BYTE BcdSec)
	{
		long Hour=( (((BcdHour&0xf0)>>4)*10)+(BcdHour&0x0f));
		long Min=( (((BcdMin&0xf0)>>4)*10)+(BcdMin&0x0f));
		long Sec=( (((BcdSec&0xf0)>>4)*10)+(BcdSec&0x0f));
		return ConvertTo_TimeSec(Hour,Min,Sec);
	}
	static inline BYTE GetWeekDayVal(DWORD MJD)
	{
		static BYTE byWeekDayStr[]={3,4,5,6,0,1,2};
		return byWeekDayStr[MJD%7];
	}
	static inline char* GetWeekDayString(DWORD MJD)
	{
//		char szWeekDayStr[][10]={"Wed","Thu","Fri","Sat","Sun","Mon","Tue"};
		static char szWeekDayStr[][10]={"水","木","金","土","日","月","火"};
		return szWeekDayStr[MJD%7];
	}
};