/*
 * SPDX-License-Identifier: MIT
 * Copyright (C) 2004 - 2022 AJA Video Systems, Inc.
 */
//========================================================================
//
//  ntv2dev_autocirc.h
//
//==========================================================================

#ifndef NTV2AUTOCIRC_H
#define NTV2AUTOCIRC_H

#include "ntv2system.h"
#include "ntv2publicinterface.h"
#include "ntv2rp188.h"

#define NUM_CIRCULATORS 18       // number of 'channels' which have auto-circulate capability
#define NUM_CIRCULATE_FRAMES 64

#define ILLEGAL_CHANNELSPEC(channelSpec) (((unsigned)channelSpec > (unsigned)NTV2CROSSPOINT_INPUT8) || (channelSpec == NTV2CROSSPOINT_MATTE) || (channelSpec == NTV2CROSSPOINT_FGKEY))

#define NTV2_INVALID_FRAME 0xFFFFFFFF

typedef struct {
	NTV2ColorCorrectionMode		mode;
	uint32_t					saturationValue;    /// only used in 3way color correction mode
	uint32_t					ccLookupTables[NTV2_COLORCORRECTOR_TABLESIZE/4]; /// R,G,B lookup tables formated for hardware
} INTERNAL_COLOR_CORRECTION_STRUCT;

typedef struct {
	//! Processor RDTSC at time of play or record.
    int64_t			frameTime;
	//! 48kHz clock in reg 28 extended to 64 bits
    uint64_t		audioClockTimeStamp;    // Register 28 with Wrap Logic
	//! The address that was used to transfer
    uint32_t		audioExpectedAddress;
	//! For record - first position in buffer of audio (includes base offset)
    uint32_t		audioInStartAddress;	// AudioInAddress at the time this Frame was stamped.
	//! For record - end position (exclusive) in buffer of audio (includes base offset)
    uint32_t		audioInStopAddress;		// AudioInAddress at the Frame AFTER this Frame was stamped.
	//! For play - first position in buffer of audio
    uint32_t		audioOutStopAddress;	// AudioOutAddress at the time this Frame was stamped.
	//! For play - end position (exclusive) in buffer of audio
    uint32_t		audioOutStartAddress;	// AudioOutAddress at the Frame AFTER this Frame was stamped.
	uint32_t		audioPreWrapBytes; // For DMA Transfer
	uint32_t		audioPostWrapBytes;	
	//! Total audio and video bytes transfered
	uint32_t		bytesRead;
	/** The actaul start sample when this frame was started in VBI
	* This may be used to check sync against audioInStartAddress (Play) or
	* audioOutStartAddress (Record).  In record it will always be equal, but
	* in playback if the clocks drift or the user supplies non aligned 
	* audio sizes, then this will give the current difference from expected 
	* vs actual position.  To be useful, play audio must be clocked in at
	* the correct rate.
	*/
    uint32_t		startSample;
	//! Associated timecode (RP-188)
    RP188_STRUCT	rp188;                  
	//! Valid counts from n..0 in the isr.  Set to n when valid (n being the number of times (VBIs) to play this frame before advancing to the next.
    // So, 0 indicates a not-ready frame, 1 indicates a normal ready frame, and >1 indicates a preroll condition.
    int32_t			validCount;             // Used to throttle record and playback, See AutoCirculate Method in driver
	//! Repeat is set to n at beginning of DMA.  Moved to repeat on completion.
    int32_t			repeatCount;            // Used to throttle record and playback, See AutoCirculate Method in driver
	//! Opaque user variable
	uint64_t		hUser;					// A user cookie returned by frame stamp
	bool			videoTransferPending;	// p2p transfer in progress
	uint32_t		frameFlags;				// p2p and field flags
    NTV2FrameBufferFormat				frameBufferFormat;
    NTV2VideoFrameBufferOrientation		frameBufferOrientation;
	INTERNAL_COLOR_CORRECTION_STRUCT	colorCorrectionInfo;
	AutoCircVidProcInfo					vidProcInfo;
	CUSTOM_ANC_STRUCT					customAncInfo;
	NTV2RoutingTable					routingTable;
	INTERNAL_TIMECODE_STRUCT			internalTCArray;
	INTERNAL_SDI_STATUS_STRUCT			internalSDIStatusArray;
	// Anc frame info
	uint32_t		ancTransferSize;
	uint32_t		ancField2TransferSize;
	uint32_t		auxData[NTV2_HDMIAuxMaxFrames*NTV2_HDMIAuxDataSize/4];
	uint32_t		auxDataSize;
} INTERNAL_FRAME_STAMP_STRUCT;

typedef struct {
	Ntv2SystemContext*		pSysCon;
	NTV2AutoCirculateState	state;
	NTV2Crosspoint			channelSpec;
	NTV2DMAEngine			DMAEngine;
	bool					recording;  
	int32_t					currentFrame; 
	int32_t					startFrame;
	int32_t					endFrame;
	int32_t					activeFrame;  
	int32_t					activeFrameRegister;
	int32_t					nextFrame;
	bool					circulateWithAudio; 
	bool					circulateWithRP188;
	bool					circulateWithColorCorrection;
	bool					circulateWithVidProc;
	bool					circulateWithCustomAncData;
	bool					enableFbfChange;
	bool					enableFboChange;
    uint64_t				startTimeStamp;
	uint64_t				startAudioClockTimeStamp;
	uint32_t				framesProcessed;
	uint32_t				droppedFrames;
	uint32_t				nextTransferFrame;
	INTERNAL_FRAME_STAMP_STRUCT	frameStamp[MAX_FRAMEBUFFERS];
	uint32_t				nextAudioOutputAddress;
	uint32_t				audioStartSample;
	uint32_t				audioDropsRequired;
	uint32_t				audioDropsCompleted;
    uint64_t				lastInterruptTime;
    uint64_t				prevInterruptTime;
	uint64_t				lastAudioClockTimeStamp;
    uint64_t				startTime;
	bool					circulateWithLTC;
	NTV2AudioSystem			audioSystem;
	int32_t					channelCount;
	bool					videoTransferPending;
	bool					startAudioNextFrame;
	bool					stopAudioNextFrame;
    uint64_t				VBIRDTSC;
    uint64_t				VBILastRDTSC;
	uint32_t				VBIAudioOut;
	uint32_t				transferFrame;
	uint32_t				audioTransferOffset;
	uint32_t				audioTransferSize;
	uint32_t				ancTransferOffset;
	uint32_t				ancField2TransferOffset;
	uint32_t				ancTransferSize;
	uint32_t				ancField2TransferSize;
	bool					circulateWithHDMIAux;
	bool					circulateWithFields;
} INTERNAL_AUTOCIRCULATE_STRUCT;

typedef struct ntv2autocirc
{
	Ntv2SystemContext*	pSysCon;
	NTV2DeviceID		deviceID;
	INTERNAL_AUTOCIRCULATE_STRUCT	autoCirculate[NUM_CIRCULATORS];
	NTV2Channel						ancInputChannel[NTV2_MAX_NUM_CHANNELS];
	NTV2_GlobalAudioPlaybackMode	globalAudioPlaybackMode;
	NTV2Crosspoint		syncChannel1;
	NTV2Crosspoint		syncChannel2;
	bool				startAudioNextFrame;
	bool				stopAudioNextFrame;
	bool				bMultiChannel;
} NTV2AutoCirc;

Ntv2Status AutoCircControl(NTV2AutoCirc* pAutoCirc, AUTOCIRCULATE_DATA_64* psControl);
Ntv2Status AutoCircInit(NTV2AutoCirc* pAutoCirc,
						 NTV2Crosspoint lChannelSpec, int32_t lStartFrameNum,
						 int32_t lEndFrameNum, NTV2AudioSystem lAudioSystem,
						 int32_t lChannelCount, bool bWithAudio,
						 bool bWithRP188, bool bFbfChange,
						 bool bFboChange , bool bWithColorCorrection,
						 bool bWithVidProc, bool bWithCustomAncData,
						 bool bWithLTC, bool bWithFields,
						 bool bWithHDMIAux);
Ntv2Status AutoCircStart(NTV2AutoCirc* pAutoCirc, NTV2Crosspoint channelSpec, int64_t startTime);
Ntv2Status AutoCircStop(NTV2AutoCirc* pAutoCirc, NTV2Crosspoint channelSpec);
Ntv2Status AutoCircAbort(NTV2AutoCirc* pAutoCirc, NTV2Crosspoint channelSpec);
Ntv2Status AutoCircPause(NTV2AutoCirc* pAutoCirc, NTV2Crosspoint channelSpec, bool bPlay, bool bClearDF);
Ntv2Status AutoCircFlush(NTV2AutoCirc* pAutoCirc, NTV2Crosspoint channelSpec, bool bClearDF);
Ntv2Status AutoCircPreroll(NTV2AutoCirc* pAutoCirc, NTV2Crosspoint channelSpec, int32_t lPrerollFrames);
Ntv2Status AutoCircSetActiveFrame(NTV2AutoCirc* pAutoCirc, NTV2Crosspoint channelSpec, int32_t lActiveFrame);
void AutoCircReset(NTV2AutoCirc* pAutoCirc, NTV2Crosspoint channelSpec);
Ntv2Status AutoCircGetStatus(NTV2AutoCirc* pAutoCirc, AUTOCIRCULATE_STATUS* pUserOutBuffer);
Ntv2Status AutoCircGetFrameStamp(NTV2AutoCirc* pAutoCirc, NTV2Crosspoint channelSpec, int32_t ulFrameNum,
									  FRAME_STAMP_STRUCT *pFrameStamp);
Ntv2Status AutoCircTransfer(NTV2AutoCirc* pAutoCirc,
								 AUTOCIRCULATE_TRANSFER* pTransferStruct);
Ntv2Status AutoCircAudioPlaybackMode(NTV2AutoCirc* pAutoCirc,
									 NTV2AudioSystem audioSystem,
									 NTV2_GlobalAudioPlaybackMode mode);
uint32_t AutoCircGetBufferLevel(INTERNAL_AUTOCIRCULATE_STRUCT* pAuto);
bool AutoCircFindNextAvailFrame(INTERNAL_AUTOCIRCULATE_STRUCT* pAuto);
void BeginAutoCircTransfer(uint32_t frameNumber,
							AUTOCIRCULATE_TRANSFER *pTransferStruct,
							INTERNAL_AUTOCIRCULATE_STRUCT *pAuto);
void CompleteAutoCircTransfer(uint32_t frameNumber, AUTOCIRCULATE_TRANSFER_STATUS *pUserOutBuffer,
							INTERNAL_AUTOCIRCULATE_STRUCT *pAuto,
							bool updateValid, bool transferPending);
void AutoCircTransferFields(INTERNAL_AUTOCIRCULATE_STRUCT* pAuto, 
							 AUTOCIRCULATE_TRANSFER* pTransfer,
							 uint32_t frameNumber, bool drop);

bool AutoCirculate(NTV2AutoCirc* pAutoCirc, NTV2Crosspoint channelSpec, uint64_t isrTimeStamp);
bool IsAutoCircInterrupt(NTV2AutoCirc* pAutoCirc, NTV2Crosspoint channelSpec);
int32_t KAUTO_NEXTFRAME(int32_t __dwCurFrame_, INTERNAL_AUTOCIRCULATE_STRUCT* __pAuto_);
int32_t KAUTO_PREVFRAME(int32_t __dwCurFrame_, INTERNAL_AUTOCIRCULATE_STRUCT* __pAuto_);
void AutoCircSetupColorCorrector(NTV2AutoCirc* pAutoCirc,
								NTV2Crosspoint channelSpec,
								INTERNAL_COLOR_CORRECTION_STRUCT *ccInfo);
void AutoCircSetupRouting(NTV2AutoCirc* pAutoCirc, NTV2RoutingTable* pRoutingTable);
void AutoCircWriteHDMIAux(NTV2AutoCirc* pAutoCirc, uint32_t* pAuxData, uint32_t auxDataSize);
bool AutoCircDmaAudioSetup(INTERNAL_AUTOCIRCULATE_STRUCT* pAuto);
bool AutoCircCanDoFieldMode(INTERNAL_AUTOCIRCULATE_STRUCT* pAuto);

bool AutoCircDropSyncFrame(NTV2AutoCirc* pAutoCirc, NTV2Crosspoint channelSpec);
void AutoCircSetupVidProc(NTV2AutoCirc* pAutoCirc,
						   NTV2Crosspoint channelSpec,
						   AutoCircVidProcInfo* vidProcInfo);
void AutoCircTransferColorCorrectorInfo(NTV2AutoCirc* pAutoCirc,
										 INTERNAL_COLOR_CORRECTION_STRUCT* ccInternalInfo,
										 NTV2ColorCorrectionInfo* ccTransferInfo);
bool AutoCircP2PCopy(NTV2AutoCirc* pAutoCirc,
					  PAUTOCIRCULATE_P2P_STRUCT pDriverBuffer,
					  PAUTOCIRCULATE_P2P_STRUCT pUserBuffer,
					  bool bToDriver);
bool AutoCircFrameStampImmediate(NTV2AutoCirc* pAutoCirc, FRAME_STAMP * pInOutFrameStamp);

#endif
