/* SPDX-License-Identifier: MIT */
/**
	@file		ntv2rp188.h
	@brief		Declares the CRP188 class. See SMPTE RP188 standard for details.
	@copyright	(C) 2007-2022 AJA Video Systems, Inc.
**/

#ifndef __NTV2_RP188_
#define __NTV2_RP188_

#include <string>
#include <ostream>
#include "ntv2publicinterface.h"


#if defined(AJALinux)
# include <stdlib.h>		/* malloc/free */
# include <string.h>		/* memset, memcpy */
#endif


typedef enum
{
	kTCFormatUnknown,
	kTCFormat24fps,
	kTCFormat25fps,
	kTCFormat30fps,
	kTCFormat30fpsDF,
	kTCFormat48fps,
	kTCFormat50fps,
	kTCFormat60fps,
	kTCFormat60fpsDF
} TimecodeFormat;

typedef enum
{
	kTCBurnTimecode,		// display current timecode
	kTCBurnUserBits,		// display current user bits
	kTCBurnFrameCount,		// display current frame count
	kTCBurnBlank			// display --:--:--:--
} TimecodeBurnMode;


const int64_t kDefaultFrameCount = 0x80000000;


//--------------------------------------------------------------------------------------------------------------------
//	class CRP188
//--------------------------------------------------------------------------------------------------------------------
class AJAExport CRP188
{
public:
	// Constructors
						CRP188 ();
						CRP188 (const RP188_STRUCT & rp188, const TimecodeFormat tcFormat = kTCFormat30fps);
						CRP188 (const NTV2_RP188 & rp188, const TimecodeFormat tcFormat = kTCFormat30fps);
						CRP188 (const std::string & sRP188, const TimecodeFormat tcFormat = kTCFormat30fps);
						CRP188 (ULWord ulFrms, ULWord ulSecs, ULWord ulMins, ULWord ulHrs, const TimecodeFormat tcFormat = kTCFormat30fps);
						CRP188 (ULWord frames, const TimecodeFormat tcFormat = kTCFormat30fps);
	virtual				~CRP188();
	void				Init ();
	bool				operator == (const CRP188& s) const;

	// Setters
	void				SetRP188 (ULWord ulFrms, ULWord ulSecs, ULWord ulMins, ULWord ulHrs,
									NTV2FrameRate frameRate, const bool bDropFrame = false, const bool bSMPTE372 = false);
	void				SetRP188 (const RP188_STRUCT & rp188, const TimecodeFormat tcFormat = kTCFormatUnknown);
	void				SetRP188 (const NTV2_RP188 & rp188, const TimecodeFormat tcFormat = kTCFormatUnknown);
	void				SetRP188 (const std::string &sRP188, const TimecodeFormat tcFormat = kTCFormatUnknown);
	void				SetRP188 (ULWord ulFrms, ULWord ulSecs, ULWord ulMins, ULWord ulHrs, const TimecodeFormat tcFormat = kTCFormatUnknown);
	void				SetRP188 (ULWord frames, const TimecodeFormat tcFormat = kTCFormatUnknown);

	void				SetDropFrame (bool bDropFrameFlag);
	void				SetColorFrame (bool bColorFrameFlag);
	void				SetVaricamFrameActive (bool bVaricamActive, ULWord frame);
	void				SetVaricamRate (NTV2FrameRate frameRate);
	void				SetFieldID (ULWord fieldID);
	bool				GetFieldID (void);
	void				SetBFGBits (bool bBFG0, bool bBFG1, bool bBFG2);
	void				SetSource (UByte src);
	void				SetOutputFilter (UByte src);

	// Getters
	bool				GetRP188Str	 (std::string & sRP188) const;
	const char *		GetRP188CString () const;
	bool				GetRP188Secs (ULWord & ulSecs) const;
	bool				GetRP188Frms (ULWord & ulFrms) const;
	bool				GetRP188Mins (ULWord & ulMins) const;
	bool				GetRP188Hrs	 (ULWord & ulHrs) const;
	bool				GetRP188Reg	 (RP188_STRUCT & outRP188) const;
	bool				GetRP188Reg	 (NTV2_RP188 & outRP188) const;
	bool				GetFrameCount (ULWord & frameCount);
	ULWord				MaxFramesPerDay (TimecodeFormat format = kTCFormatUnknown) const;
	void				ConvertTimecode (ULWord & frameCount, TimecodeFormat format, ULWord hours, ULWord minutes, ULWord seconds, ULWord frames);
	void				ConvertFrameCount (ULWord frameCount, TimecodeFormat format, ULWord & hours, ULWord & minutes, ULWord & seconds, ULWord & frames);
	ULWord				AddFrames (ULWord frameCount);
	ULWord				SubtractFrames (ULWord frameCount);
	bool				GetRP188UserBitsStr (std::string & sRP188UB);
	const char *		GetRP188UserBitsCString ();
	UByte				GetSource () const ;
	UByte				GetOutputFilter () const ;
	TimecodeFormat		GetTimecodeFormat() { return _tcFormat; }

	// these tests are ONLY valid if the CRP188 object is instantiated or set with an RP188_STRUCT
	bool				IsFreshRP188 (void)
							{ return(_bFresh); }					// true if RP188 data is fresh this past frame
	bool				VaricamFrame0 (void)
							{ return(_bVaricamActiveF0); }			// true if Varicam "Frame0 Active" bit is set
	bool				VaricamFrame1 (void)
							{ return(_bVaricamActiveF1); }			// true if Varicam "Frame1 Active" bit is set
	ULWord				VaricamFrameRate (void);
	NTV2FrameRate		defaultFrameRateForTimecodeFormat (TimecodeFormat format = kTCFormatUnknown) const;
	bool				FormatIsDropFrame (TimecodeFormat format = kTCFormatUnknown) const;
	bool				FormatIs60_50fps (TimecodeFormat format = kTCFormatUnknown) const;
	bool				FormatIsPAL (TimecodeFormat format = kTCFormatUnknown) const;

	ULWord				FieldID (void)
							{ return(_fieldID); }					// fieldID bit
	bool				DropFrame (void)
							{ return(_bDropFrameFlag);	}			// drop frame bit
	bool				ColorFrame (void)
							{ return(_bColorFrameFlag); }			// color frame bit
	ULWord				BinaryGroup (ULWord smpteNum);

	// For historical reasons, calling SetRP188 clears the user bits, so a call to either of these two functions
	// should be done after the timecode value is set
	bool				SetBinaryGroup (ULWord smpteNum, ULWord bits);
	bool				SetUserBits (ULWord bits);					// eight groups of four bits each

	ULWord				UDW (ULWord smpteUDW);
	ULWord				FramesPerSecond (TimecodeFormat format = kTCFormatUnknown) const;
	NTV2FrameRate		DefaultFrameRateForTimecodeFormat (TimecodeFormat format = kTCFormatUnknown) const;

	// Modifiers
	bool				InitBurnIn (NTV2FrameBufferFormat frameBufferFormat, NTV2FrameSize frameDimensions, LWord percentY = 0);
	void				writeV210Pixel (char **pBytePtr, int x, int c, int y);
	bool				BurnTC (char *pBaseVideoAddress, int rowBytes, TimecodeBurnMode burnMode, int64_t frameCount = kDefaultFrameCount, bool bDisplay60_50fpsAs30_25 = false);
	void				CopyDigit (char *pDigit, int digitWidth, int digitHeight, char *pFrameBuff, int fbRowBytes);
	std::string			GetTimeCodeString(bool bDisplay60_50fpsAs30_25 = false);

private:
	void				ConvertTcStrToVal (void);	// converts _sHMSF to _ulVal
	void				ConvertTcStrToReg (void);	// converts _sHMSF to _rp188
	void				RP188ToUserBits (void);		// derives _ulUserBits and _sUserBits from RP188 struct


private:
	TimecodeFormat		_tcFormat;			// fps, drop- or non-drop frame
	bool				_bInitialized;		// if constructed with no args, set to false
	bool				_bFresh;			// true if hardware told us this was new ANC data (not just what was lying around in the registers)
	//bool				_bDropFrame;		// we have to be told whether we are df or ndf
	//bool				_b50Hz;				// if true, interpret FieldID and Binary Group Flags per 50 Hz spec

	// RP188 user bits
	bool				_bVaricamActiveF0;	// Varicam "Active Frame 0" user bit flag
	bool				_bVaricamActiveF1;	// Varicam "Active Frame 1" user bit flag
	ULWord				_fieldID;			// FieldID bit: '0' or '1'
	bool				_bDropFrameFlag;	// Drop Frame bit: '0' or '1'
	bool				_bColorFrameFlag;	// Color Frame bit: '0' or '1'
	ULWord				_varicamRate;		// Varicam rate expressed as bits [0..3] 1 units, bits [4..7] tens unit.

	std::string			_sHMSF;				// hour:minute:second:frame in string format
	std::string			_sUserBits;			// Binary Groups 8-1 in string format
	ULWord				_ulVal[4];			// [0]=frame, [1]=seconds, etc.
	ULWord				_ulUserBits[8];		// [0] = Binary Group 1, [1] = Binary Group 2, etc. (note: SMPTE labels them 1 - 8)
	RP188_STRUCT		_rp188;				// AJA native format

	bool				_bRendered;			// set 'true' when Burn-In character map has been rendered
	char *				_pCharRenderMap;	// ptr to rendered Burn-In character set
	NTV2FrameBufferFormat _charRenderFBF;	// frame buffer format of rendered characters
	ULWord				_charRenderHeight;	// frame height for which rendered characters were rendered
	ULWord				_charRenderWidth;	// frame width for which rendered characters were rendered
	int					_charWidthBytes;	// rendered character width in bytes
	int					_charHeightLines;	// rendered character height in frame lines
	int					_charPositionX;		// offset (in bytes) from left side of screen to first burn-in character
	int					_charPositionY;		// offset (in lines) from top of screen to top of burn-in characters
};	//	CRP188

/**
	@brief		Prints the given CRP188's contents into the given output stream.
	@param		outputStream	The stream into which the human-readable timecode will be written.
	@param[in]	inObj			Specifies the CRP188 instance to be streamed.
	@return		The ostream that was specified.
**/
AJAExport std::ostream & operator << (std::ostream & outputStream, const CRP188 & inObj);

#endif	//	__NTV2_RP188_
