#pragma once

#include "../frames/InputFrameMailbox.h"
#include "DeckLinkAPI_h.h"
#include "DeckLinkDisplayMode.h"
#include "VideoIOEdges.h"

#include <atlbase.h>

#include <atomic>
#include <chrono>
#include <cstdint>
#include <string>
#include <vector>

namespace RenderCadenceCompositor
{
struct DeckLinkInputConfig
{
	VideoFormat videoFormat;
};

using DeckLinkInputMetrics = VideoInputEdgeMetrics;

class DeckLinkInput;

class DeckLinkInputCallback final : public IDeckLinkInputCallback
{
public:
	explicit DeckLinkInputCallback(DeckLinkInput& owner);

	HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID* ppv) override;
	ULONG STDMETHODCALLTYPE AddRef() override;
	ULONG STDMETHODCALLTYPE Release() override;
	HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame* videoFrame, IDeckLinkAudioInputPacket* audioPacket) override;
	HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(BMDVideoInputFormatChangedEvents notificationEvents, IDeckLinkDisplayMode* newDisplayMode, BMDDetectedVideoInputFormatFlags detectedSignalFlags) override;

private:
	DeckLinkInput& mOwner;
	std::atomic<ULONG> mRefCount{ 1 };
};

class DeckLinkInput : public IVideoInputEdge
{
public:
	DeckLinkInput(InputFrameMailbox& mailbox);
	DeckLinkInput(const DeckLinkInput&) = delete;
	DeckLinkInput& operator=(const DeckLinkInput&) = delete;
	~DeckLinkInput();

	bool Initialize(const DeckLinkInputConfig& config, std::string& error);
	bool Start(std::string& error) override;
	void Stop() override;
	void ReleaseResources() override;

	bool IsInitialized() const override { return mInput != nullptr; }
	bool IsRunning() const override { return mRunning.load(std::memory_order_acquire); }
	VideoIOPixelFormat CapturePixelFormat() const override;
	DeckLinkInputMetrics Metrics() const override;

	void HandleFrameArrived(IDeckLinkVideoInputFrame* inputFrame);
	void HandleFormatChanged();

private:
	bool DiscoverInput(const DeckLinkInputConfig& config, std::string& error);
	bool SupportsInputFormat(IDeckLinkInput* input, BMDDisplayMode displayMode, BMDPixelFormat pixelFormat) const;
	bool SubmitBgra8Frame(IDeckLinkVideoInputFrame* inputFrame, const void* bytes);
	bool SubmitUyvy8Frame(IDeckLinkVideoInputFrame* inputFrame, const void* bytes);
	const char* CaptureFormatName() const;

	InputFrameMailbox& mMailbox;
	DeckLinkInputConfig mConfig;
	BMDPixelFormat mCapturePixelFormat = bmdFormat8BitBGRA;
	CComPtr<IDeckLinkInput> mInput;
	CComPtr<DeckLinkInputCallback> mCallback;
	std::atomic<bool> mRunning{ false };
	std::atomic<uint64_t> mCapturedFrames{ 0 };
	std::atomic<uint64_t> mNoInputSourceFrames{ 0 };
	std::atomic<uint64_t> mUnsupportedFrames{ 0 };
	std::atomic<uint64_t> mSubmitMisses{ 0 };
	std::atomic<double> mConvertMilliseconds{ 0.0 };
	std::atomic<double> mSubmitMilliseconds{ 0.0 };
	std::atomic<bool> mLoggedFirstFrame{ false };
	std::atomic<bool> mLoggedNoInputSource{ false };
	std::atomic<bool> mLoggedUnsupportedFrame{ false };
	std::atomic<bool> mLoggedSubmitMiss{ false };
};
}
