#ifndef INTRADAY_RECORD_H
#define INTRADAY_RECORD_H

/********************************************************************/
// s_IntradayRecord

//Special values used for s_IntradayRecord::Open
const float SINGLE_TRADE_WITH_BID_ASK = 0.0F;
const float FIRST_SUB_TRADE_OF_UNBUNDLED_TRADE_VALUE = -1.99900095e+37F;
const float LAST_SUB_TRADE_OF_UNBUNDLED_TRADE_VALUE = -1.99900197e+37F;

struct s_IntradayRecord
{
	//--- Static Members ---------------------------------------------
	
	//--- Members ----------------------------------------------------

	SCDateTimeMS DateTime;

	float Open;
	float High;
	float Low;
	float Close;

	uint32_t NumTrades;
	uint32_t TotalVolume;
	uint32_t BidVolume;
	uint32_t AskVolume;

	//--- Methods ----------------------------------------------------

	s_IntradayRecord();
	s_IntradayRecord(int Value);

	bool operator == (const s_IntradayRecord& Record) const;

	s_IntradayRecord& operator = (const s_IntradayRecord& Record);

	s_IntradayRecord& operator += (const float Amount);
	s_IntradayRecord& operator *= (const float Multiplier);
	s_IntradayRecord& operator /= (const float Divisor);


	void AddAdjustment
		( const float Amount
		, const bool AdjustZeroValues
		);

	bool IsEmpty() const;

	void Clear();
	void ClearOpenFlags();

	void Fix();

	bool IsSingleTradeWithBidAsk() const;
	bool IsFirstSubTradeOfUnbundledTrade() const;
	bool IsLastSubTradeOfUnbundledTrade() const;
	bool IsFirstOrLastSubTradeOfUnbundledTrade() const;
	bool IsBidAskUpdateOnly() const;

	float GetOpen() const;
	float GetHigh() const;
	float GetLow() const;
	float GetClose() const;
	float GetBidPrice() const;
	float GetAskPrice() const;
};

/********************************************************************/
// s_Header

struct s_IntradayFileHeader
{
	static const uint32_t UNIQUE_HEADER_ID = 0x44494353;  // "SCID"

	uint32_t FileTypeUniqueHeaderID;  // "SCID"
	uint32_t HeaderSize;

	uint32_t RecordSize;
	uint16_t Version;

	uint16_t Unused1;

	uint32_t Unused2;

	char Reserve[36];


	s_IntradayFileHeader()
		: FileTypeUniqueHeaderID(UNIQUE_HEADER_ID)
		, HeaderSize(sizeof(s_IntradayFileHeader))
		, RecordSize(sizeof(s_IntradayRecord))
		, Version(1)
		, Unused1(0)
		, Unused2(0)
	{
		memset(Reserve, 0, sizeof(Reserve));
	}
};



/****************************************************************************/
// s_IntradayRecord

/*==========================================================================*/
inline s_IntradayRecord::s_IntradayRecord()
{
	Clear();
}

/*============================================================================
This constructor is to support using this structure as an c_SCArray
element.
----------------------------------------------------------------------------*/
inline s_IntradayRecord::s_IntradayRecord(int Value)
{
	Clear();
}

/*==========================================================================*/
inline bool s_IntradayRecord::operator ==
(const s_IntradayRecord& Record
	) const
{
	return memcmp(this, &Record, sizeof(s_IntradayRecord)) == 0;
}

/*==========================================================================*/
inline s_IntradayRecord&
s_IntradayRecord::operator =
(const s_IntradayRecord& Record
	)
{
	if (&Record == this)
		return *this;

	memcpy(this, &Record, sizeof(s_IntradayRecord));

	return *this;
}

/*==========================================================================*/
inline s_IntradayRecord&
s_IntradayRecord::operator += (const float Amount)
{
	if (!IsSingleTradeWithBidAsk())
		Open += Amount;

	High += Amount;
	Low += Amount;
	Close += Amount;

	return *this;
}

/*==========================================================================*/
inline s_IntradayRecord&
s_IntradayRecord::operator *= (const float Multiplier)
{
	if (!IsSingleTradeWithBidAsk())
		Open *= Multiplier;

	High *= Multiplier;
	Low *= Multiplier;
	Close *= Multiplier;

	return *this;
}

/*==========================================================================*/
inline s_IntradayRecord&
s_IntradayRecord::operator /= (const float Divisor)
{
	if (!IsSingleTradeWithBidAsk())
		Open /= Divisor;

	High /= Divisor;
	Low /= Divisor;
	Close /= Divisor;

	return *this;
}
/*============================================================================
Adds the given Amount to the prices, so long as the prices are non-zero,
or AdjustZeroValues is true.
----------------------------------------------------------------------------*/
inline void s_IntradayRecord::AddAdjustment
(const float Amount
	, const bool AdjustZeroValues
)
{
	if (!IsSingleTradeWithBidAsk())
	{
		if (Open != 0.0f || AdjustZeroValues)
			Open += Amount;
	}

	if (High != 0.0f || AdjustZeroValues)
		High += Amount;

	if (Low != 0.0f || AdjustZeroValues)
		Low += Amount;

	if (Close != 0.0f || AdjustZeroValues)
		Close += Amount;
}

/*==========================================================================*/
inline bool s_IntradayRecord::IsEmpty() const
{
	return DateTime == 0.0
		&& Open == 0.0f
		&& High == 0.0f
		&& Low == 0.0f
		&& Close == 0.0f
		&& NumTrades == 0
		&& TotalVolume == 0;
}

/*==========================================================================*/
inline void s_IntradayRecord::Clear()
{
	DateTime = 0.0;
	Open = 0.0f;
	High = 0.0f;
	Low = 0.0f;
	Close = 0.0f;
	NumTrades = 0;
	TotalVolume = 0;
	BidVolume = 0;
	AskVolume = 0;
}

/*==========================================================================*/
inline void s_IntradayRecord::ClearOpenFlags()
{
	Open = 0.0f;
}

/*============================================================================
This function fixes any data that looks erroneous.
----------------------------------------------------------------------------*/
inline void s_IntradayRecord::Fix()
{
	if (NumTrades > INT_MAX)
		NumTrades = 0;

	if (TotalVolume > INT_MAX)
		TotalVolume = 0;

	if (BidVolume > INT_MAX)
		BidVolume = 0;

	if (AskVolume > INT_MAX)
		AskVolume = 0;
}

/*==========================================================================*/
inline bool s_IntradayRecord::IsSingleTradeWithBidAsk() const
{
	return NumTrades == 1
		&& (Open == 0.0
			|| Open == FIRST_SUB_TRADE_OF_UNBUNDLED_TRADE_VALUE
			|| Open == LAST_SUB_TRADE_OF_UNBUNDLED_TRADE_VALUE
			);
}

/*==========================================================================*/
inline bool s_IntradayRecord::IsFirstSubTradeOfUnbundledTrade() const
{
	return (Open == FIRST_SUB_TRADE_OF_UNBUNDLED_TRADE_VALUE);
}

/*==========================================================================*/
inline bool s_IntradayRecord::IsLastSubTradeOfUnbundledTrade() const
{
	return (Open == LAST_SUB_TRADE_OF_UNBUNDLED_TRADE_VALUE);
}

/*==========================================================================*/
inline bool s_IntradayRecord::IsFirstOrLastSubTradeOfUnbundledTrade() const
{
	return Open == FIRST_SUB_TRADE_OF_UNBUNDLED_TRADE_VALUE
		|| Open == LAST_SUB_TRADE_OF_UNBUNDLED_TRADE_VALUE;
}

/*==========================================================================*/
inline bool s_IntradayRecord::IsBidAskUpdateOnly() const
{
	return NumTrades == 1
		&& TotalVolume == 0
		&& Open == 0.0
		&& High != 0.0
		&& Low != 0.0;
}

/*==========================================================================*/
inline float s_IntradayRecord::GetOpen() const
{
	if (IsSingleTradeWithBidAsk())
		return Close;

	return Open;
}

/*==========================================================================*/
inline float s_IntradayRecord::GetHigh() const
{
	if (IsSingleTradeWithBidAsk())
		return Close;

	return High;
}

/*==========================================================================*/
inline float s_IntradayRecord::GetLow() const
{
	if (IsSingleTradeWithBidAsk())
		return Close;

	return Low;
}

/*==========================================================================*/
inline float s_IntradayRecord::GetClose() const
{
	return Close;
}

/*==========================================================================*/
inline float s_IntradayRecord::GetBidPrice() const
{
	if (!IsSingleTradeWithBidAsk())
		return 0.0f;

	return Low;
}

/*==========================================================================*/
inline float s_IntradayRecord::GetAskPrice() const
{
	if (!IsSingleTradeWithBidAsk())
		return 0.0f;

	return High;
}
#endif
