#pragma once

#include "FractoriumSettings.h"
#include "FractoriumEmberController.h"

/// <summary>
/// FinalRenderEmberControllerBase and FinalRenderEmberController<T> classes.
/// </summary>

/// <summary>
/// FractoriumEmberController and Fractorium need each other, but each can't include the other.
/// So Fractorium includes this file, and Fractorium is declared as a forward declaration here.
/// </summary>
class Fractorium;
class FractoriumFinalRenderDialog;
template <typename T> class FinalRenderPreviewRenderer;

/// <summary>
/// Used to hold the options specified in the current state of the Gui for performing the final render.
/// </summary>
struct FinalRenderGuiState
{
	bool m_EarlyClip;
	bool m_YAxisUp;
	bool m_AlphaChannel;
	bool m_Transparency;
	bool m_OpenCL;
	bool m_Double;
	bool m_SaveXml;
	bool m_DoAll;
	bool m_Png16Bit;
	bool m_DoSequence;
	bool m_KeepAspect;
	eScaleType m_Scale;
	QString m_Path;
	QString m_Ext;
	QString m_Prefix;
	QString m_Suffix;
	QList<QVariant> m_Devices;
	uint m_ThreadCount;
	int m_ThreadPriority;
	double m_SubBatchPct;
	double m_WidthScale;
	double m_HeightScale;
	double m_Quality;
	uint m_TemporalSamples;
	uint m_Supersample;
	size_t m_Strips;
};

/// <summary>
/// FinalRenderEmberControllerBase serves as a non-templated base class with virtual
/// functions which will be overridden in a derived class that takes a template parameter.
/// Although not meant to be used as an interactive renderer, it derives from FractoriumEmberControllerBase
/// to access a few of its members to avoid having to redefine them here.
/// </summary>
class FinalRenderEmberControllerBase : public FractoriumEmberControllerBase
{
	friend FractoriumFinalRenderDialog;

public:
	FinalRenderEmberControllerBase(FractoriumFinalRenderDialog* finalRenderDialog);
	virtual ~FinalRenderEmberControllerBase() { }

	virtual void SyncCurrentToGui() { }
	virtual void SyncGuiToEmbers(size_t widthOverride = 0, size_t heightOverride = 0, bool dowidth = true, bool doheight = true) { }
	virtual void SyncCurrentToSizeSpinners(bool scale, bool size, bool doWidth = true, bool doHeight = true) { }
	virtual void ResetProgress(bool total = true) { }
	virtual tuple<size_t, size_t, size_t> SyncAndComputeMemory()  { return tuple<size_t, size_t, size_t>(0, 0, 0); }
	virtual double OriginalAspect()  { return 1; }
	virtual QString ComposePath(const QString& name, bool unique = true) { return ""; }
	virtual bool BumpQualityRender(double d)  { return false; }
	virtual QString SaveCurrentAgain() { return ""; }
	virtual void CancelRender() { }
	virtual QString CheckMemory(const tuple<size_t, size_t, size_t>& p) { return ""; }

	bool Running() { return m_Result.isRunning(); }
	bool CreateRendererFromGUI();
	void Output(const QString& s);

protected:
	bool m_Run = false;
	bool m_IsQualityBump = false;
	size_t m_ImageCount = 0;
	std::atomic<size_t> m_FinishedImageCount;

	QFuture<void> m_Result;
	std::function<void (void)> m_FinalRenderFunc;

	shared_ptr<FractoriumSettings> m_Settings;
	FractoriumFinalRenderDialog* m_FinalRenderDialog;
	FinalRenderGuiState m_GuiState;
	std::recursive_mutex m_ProgressCs;
	Timing m_RenderTimer;
	Timing m_TotalTimer;
};

/// <summary>
/// Templated derived class which implements all interaction functionality between the embers
/// of a specific template type and the final render dialog.
/// </summary>
template<typename T>
class FinalRenderEmberController : public FinalRenderEmberControllerBase
{
	friend FinalRenderPreviewRenderer<T>;

public:
	FinalRenderEmberController(FractoriumFinalRenderDialog* finalRender);
	virtual ~FinalRenderEmberController() { }

	//Virtual functions overridden from FractoriumEmberControllerBase.
	void SetEmberFile(const EmberFile<float>& emberFile, bool move) override;
	void CopyEmberFile(EmberFile<float>& emberFile, bool sequence, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) override;
#ifdef DO_DOUBLE
	void SetEmberFile(const EmberFile<double>& emberFile, bool move) override;
	void CopyEmberFile(EmberFile<double>& emberFile, bool sequence, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) override;
#endif
	void SetEmber(size_t index, bool verbatim) override;
	void SaveCurrentAsXml(QString filename = "") override;
	bool Render() override;
	bool BumpQualityRender(double d)  override;
	bool CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool updatePreviews, bool shared = true) override;
	int ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs) override;
	size_t Index() const noexcept override { return m_Ember->m_Index; }
	uint SizeOfT() const noexcept override { return sizeof(T); }

	//Virtual functions overridden from FinalRenderEmberControllerBase.
	void SyncCurrentToGui() override;
	void SyncGuiToEmbers(size_t widthOverride = 0, size_t heightOverride = 0, bool dowidth = true, bool doheight = true) override;
	void SyncCurrentToSizeSpinners(bool scale, bool size, bool doWidth = true, bool doHeight = true) override;
	void ResetProgress(bool total = true)  override;
	tuple<size_t, size_t, size_t> SyncAndComputeMemory() override;
	double OriginalAspect() override { return double(m_Ember->m_OrigFinalRasW) / m_Ember->m_OrigFinalRasH; }
	QString Name() const override { return QString::fromStdString(m_Ember->m_Name); }
	QString ComposePath(const QString& name, bool unique = true) override;
	QString SaveCurrentAgain() override;
	void CancelRender() override;
	QString CheckMemory(const tuple<size_t, size_t, size_t>& p) override;

	//Non Virtual functions.
	EmberNs::Renderer<T, float>* FirstOrDefaultRenderer();

protected:
	void Pause(bool pause) override;
	bool Paused() override;
	void HandleFinishedProgress();
	QString SaveCurrentRender(Ember<T>& ember);
	QString SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<v4F>& pixels, size_t width, size_t height, bool png16Bit, bool transparency);
	void RenderComplete(Ember<T>& ember);
	void RenderComplete(Ember<T>& ember, const EmberStats& stats, Timing& renderTimer);
	void SyncGuiToEmber(Ember<T>& ember, size_t widthOverride = 0, size_t heightOverride = 0, bool dowidth = true, bool doheight = true);
	bool SyncGuiToRenderer();
	void SetProgressComplete(int val);
	bool RenderSingleEmber(Ember<T>& ember, bool fullRender, size_t& stripForProgress);
	bool RenderSingleEmberFromSeries(std::atomic<size_t>* atomfTime, size_t index);

	Ember<T>* m_Ember;
	EmberFile<T> m_EmberFile;
	EmberToXml<T> m_XmlWriter;
	unique_ptr<FinalRenderPreviewRenderer<T>> m_FinalPreviewRenderer;
	vector<unique_ptr<EmberNs::Renderer<T, float>>> m_Renderers;
};

/// <summary>
/// Thin derivation to handle preview rendering that is specific to the final render dialog.
/// This differs from the preview renderers on the main window because they render multiple embers
/// to a tree, whereas this renders a single preview.
/// </summary>
template <typename T>
class FinalRenderPreviewRenderer : public PreviewRenderer<T>
{
public:
	using PreviewRenderer<T>::m_PreviewRun;
	using PreviewRenderer<T>::m_PreviewVec;
	using PreviewRenderer<T>::m_PreviewEmber;
	using PreviewRenderer<T>::m_PreviewRenderer;
	using PreviewRenderer<T>::m_PreviewFinalImage;

	FinalRenderPreviewRenderer(FinalRenderEmberController<T>* controller) : m_Controller(controller)
	{
	}

	void PreviewRenderFunc(uint start, uint end) override;

private:
	FinalRenderEmberController<T>* m_Controller;
};