#pragma once

#include "RendererBase.h"
#include "Iterator.h"
#include "SpatialFilter.h"
#include "TemporalFilter.h"
#include "Interpolate.h"
#include "CarToRas.h"
#include "EmberToXml.h"

/// <summary>
/// Renderer.
/// </summary>

namespace EmberNs
{
/// <summary>
/// Renderer is the main class where all of the execution takes place.
/// It is intended that the program have one instance of it that it
/// keeps around for its duration. After a user sets up an ember, it's passed
/// in to be rendered.
/// This class derives from EmberReport, so the caller is able
/// to retrieve a text dump of error information if any errors occur.
/// The final image output vector is also passed in because the calling code has more
/// use for it than this class does.
/// Several functions are made virtual and have a default CPU-based implementation
/// that roughly matches what flam3 did. However they can be overridden in derived classes
/// to provide alternative rendering implementations, such as using the GPU.
/// Since this is a templated class, it's supposed to be entirely implemented in this .h file.
/// However, VC++ 2010 has very crippled support for lambdas, which Renderer makes use of.
/// If too many lambdas are used in a .h file, it will crash the compiler when another library
/// tries to link to it. To work around the bug, only declarations are here and all implementations
/// are in the .cpp file. It's unclear at what point it starts/stops working. But it seems that once
/// enough code is placed in the .h file, the compiler crashes. So for the sake of consistency, everything
/// is moved to the .cpp, even simple getters. One drawback however, is that the class must be
/// explicitly exported at the bottom of the file.
/// Also, despite explicitly doing this, the compiler throws a C4661 warning
/// for every single function in this class, saying it can't find the implementation. This warning
/// can be safely ignored.
/// Template argument T expected to be float or double.
/// Template argument bucketT was originally used to experiment with different types for the histogram, however
/// the only types that work are float and double, so it's useless and should always match what T is.
/// Mismatched types between T and bucketT are undefined.
/// </summary>
template <typename T, typename bucketT>
class EMBER_API Renderer : public RendererBase
{
//using EmberReport::m_ErrorReport;
public:
	Renderer();
	virtual ~Renderer();

	//Non-virtual processing functions.
	void AddEmber(Ember<T>& ember);
	bool AssignIterator();

	//Virtual processing functions overriden from RendererBase.
	virtual void ComputeBounds() override;
	virtual void ComputeCamera() override;
	virtual void SetEmber(Ember<T>& ember, eProcessAction action = FULL_RENDER) override;
	virtual void SetEmber(vector<Ember<T>>& embers) override;
	virtual bool CreateDEFilter(bool& newAlloc) override;
	virtual bool CreateSpatialFilter(bool& newAlloc) override;
	virtual bool CreateTemporalFilter(bool& newAlloc) override;
	virtual size_t HistBucketSize() const override { return sizeof(glm::detail::tvec4<bucketT, glm::defaultp>); }
	virtual eRenderStatus Run(vector<byte>& finalImage, double time = 0, size_t subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) override;
	virtual EmberImageComments ImageComments(EmberStats& stats, size_t printEditDepth = 0, bool intPalette = false, bool hexPalette = true) override;

protected:
	//New virtual functions to be overridden in derived renderers that use the GPU, but not accessed outside.
	virtual void MakeDmap(T colorScalar);
	virtual bool Alloc();
	virtual bool ResetBuckets(bool resetHist = true, bool resetAccum = true);
	virtual eRenderStatus LogScaleDensityFilter();
	virtual eRenderStatus GaussianDensityFilter();
	virtual eRenderStatus AccumulatorToFinalImage(vector<byte>& pixels, size_t finalOffset);
	virtual eRenderStatus AccumulatorToFinalImage(byte* pixels, size_t finalOffset);
	virtual EmberStats Iterate(size_t iterCount, size_t temporalSample);

public:
	//Non-virtual render properties, getters and setters.
	inline T PixelAspectRatio() const;
	void PixelAspectRatio(T pixelAspectRatio);

	//Non-virtual renderer properties, getters only.
	inline T                            Scale()                         const;
	inline T                            PixelsPerUnitX()                const;
	inline T                            PixelsPerUnitY()                const;
	inline T                            K1()                            const;
	inline T                            K2()                            const;
	inline const CarToRas<T>*           CoordMap()                      const;
	inline glm::detail::tvec4<bucketT, glm::defaultp>* HistBuckets();
	inline glm::detail::tvec4<bucketT, glm::defaultp>* AccumulatorBuckets();
	inline SpatialFilter<T>*            GetSpatialFilter();
	inline TemporalFilter<T>*           GetTemporalFilter();

	//Virtual renderer properties overridden from RendererBase, getters only.
	virtual double ScaledQuality()				   const override;
	virtual double LowerLeftX(bool  gutter = true) const override;
	virtual double LowerLeftY(bool  gutter = true) const override;
	virtual double UpperRightX(bool gutter = true) const override;
	virtual double UpperRightY(bool gutter = true) const override;
	virtual DensityFilterBase* GetDensityFilter()        override;

	//Non-virtual ember wrappers, getters only.
	inline bool              XaosPresent()		   const;
	inline size_t			 Supersample()         const;
	inline size_t			 PaletteIndex()        const;
	inline T                 Time()                const;
	inline T                 Quality()             const;
	inline T                 SpatialFilterRadius() const;
	inline T                 PixelsPerUnit()       const;
	inline T                 Zoom()                const;
	inline T                 CenterX()             const;
	inline T                 CenterY()             const;
	inline T                 Rotate()              const;
	inline T                 Hue()                 const;
	inline T                 Brightness()          const;
	inline T                 Contrast()            const;
	inline T                 Gamma()               const;
	inline T                 Vibrancy()            const;
	inline T                 GammaThresh()         const;
	inline T                 HighlightPower()      const;
	inline Color<T>			 Background()          const;
	inline const Xform<T>*   Xforms()              const;
	inline Xform<T>*         NonConstXforms();
	inline size_t			 XformCount()          const;
	inline const Xform<T>*   FinalXform()          const;
	inline Xform<T>*         NonConstFinalXform();
	inline bool              UseFinalXform()       const;
	inline const Palette<T>* GetPalette()          const;
	inline ePaletteMode      PaletteMode()         const;

	//Virtual ember wrappers overridden from RendererBase, getters only.
	virtual size_t TemporalSamples() const override;
	virtual size_t FinalRasW()       const override;
	virtual size_t FinalRasH()       const override;
	virtual size_t SubBatchSize()    const override;
	virtual size_t FuseCount()		 const override;

	//Non-virtual iterator wrappers.
	const byte* XformDistributions()		const;
	size_t 		XformDistributionsSize()    const;
	Point<T>*	Samples(size_t threadIndex) const;

protected:
	//Non-virtual functions that might be needed by a derived class.
	void PrepFinalAccumVals(Color<T>& background, T& g, T& linRange, T& vibrancy);

	private:
	//Miscellaneous non-virtual functions used only in this class.
	void Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Point<T>* samples, size_t sampleCount, const Palette<bucketT>* palette);
	/*inline*/ void AddToAccum(const glm::detail::tvec4<bucketT, glm::defaultp>& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj);
	template <typename accumT> void GammaCorrection(glm::detail::tvec4<bucketT, glm::defaultp>& bucket, Color<T>& background, T g, T linRange, T vibrancy, bool doAlpha, bool scale, accumT* correctedChannels);

protected:
	T m_Scale;
	T m_PixelsPerUnitX;
	T m_PixelsPerUnitY;
	T m_PixelAspectRatio;
	T m_LowerLeftX;
	T m_LowerLeftY;
	T m_UpperRightX;
	T m_UpperRightY;
	T m_K1;
	T m_K2;
	T m_Vibrancy;//Accumulate these after each temporal sample.
	T m_Gamma;
	T m_ScaledQuality;
	Color<T> m_Background;
	Affine2D<T> m_RotMat;
	Ember<T> m_Ember;
	Ember<T> m_TempEmber;
	Ember<T> m_LastEmber;
	vector<Ember<T>> m_Embers;
	CarToRas<T> m_CarToRas;
	Iterator<T>* m_Iterator;
	unique_ptr<StandardIterator<T>> m_StandardIterator;
	unique_ptr<XaosIterator<T>> m_XaosIterator;
	Palette<bucketT> m_Dmap;
	vector<glm::detail::tvec4<bucketT, glm::defaultp>> m_HistBuckets;
	vector<glm::detail::tvec4<bucketT, glm::defaultp>> m_AccumulatorBuckets;
	unique_ptr<SpatialFilter<T>> m_SpatialFilter;
	unique_ptr<TemporalFilter<T>> m_TemporalFilter;
	unique_ptr<DensityFilter<T>> m_DensityFilter;
	vector<vector<Point<T>>> m_Samples;
	EmberToXml<T> m_EmberToXml;
};

//This class had to be implemented in a cpp file because the compiler was breaking.
//So the explicit instantiation must be declared here rather than in Ember.cpp where
//all of the other classes are done.
//template EMBER_API class Renderer<float, float>;

//#ifdef DO_DOUBLE
//	template EMBER_API class Renderer<double, double>;
//#endif
}