#pragma once
#ifndef REAL_BUILD
# include "externals.hh"
#endif

struct T_SyncData;

namespace cops {

	struct T_Context
	{
		float const* time;
		T_SyncData const* sync;

		std::vector< float > varValues;
		std::map< std::string , uint32_t > varPos;

		std::vector< float > opStack;
	};

	class X_OpFailure : public std::exception
	{
	    public:
		X_OpFailure( ) = delete;
		DEF_COPY( X_OpFailure );
		DEF_MOVE( X_OpFailure );

		X_OpFailure(
			__rd__ std::string const& source ,
			__rd__ uint32_t line ,
			__rd__ std::string const& error ) noexcept;

		std::string const& source( ) const noexcept
			{ return source_; }
		uint32_t line( ) const noexcept
			{ return line_; }
		std::string const& error( ) const noexcept
			{ return error_; }

		char const* what( ) const noexcept override;

	    private:
		std::string source_;
		uint32_t line_;
		std::string error_;
		std::string fullMessage_;
	};

	/*--------------------------------------------------------------------*/

	enum E_Op
	{
		OP_LOAD_CONSTANT ,
		OP_LOAD_VARIABLE ,
		OP_LOAD_INPUT ,
		OP_STORE_VARIABLE ,
		//
		OP_ADD , OP_MUL ,
		OP_NEG , OP_INV ,
		//
		OP_DUP , OP_XCHG ,
		//
		OP_SET_UNIFORM ,
		OP_USE_PIPELINE ,
		OP_USE_TEXTURE ,
		OP_USE_FRAMEBUFFER ,
		OP_SET_VIEWPORT ,
		//
		OP_CLEAR ,
		OP_FULLSCREEN
	};

	struct T_Op
	{
		explicit T_Op(
			__rd__ const E_Op op );
		virtual ~T_Op( ) = 0;

		E_Op op( ) const noexcept
			{ return op_; }

		virtual void execute(
			__rw__ T_Context& ctx ) const = 0;

		std::string source;
		uint32_t line;

	    protected:
		X_OpFailure error(
			__rd__ std::string const& message ) const noexcept;

	    private:
		E_Op op_;
	};
	using P_Op = std::unique_ptr< T_Op >;
	using T_Operations = std::vector< P_Op >;

	/*====================================================================*/

	struct OPLoadConstant : public T_Op
	{
		explicit OPLoadConstant(
			__rd__ const float constant );
		void execute(
			__rw__ T_Context& ctx ) const override;
		float constant;
	};

	struct OPLoadVariable : public T_Op
	{
		explicit OPLoadVariable(
			__rd__ std::string const& variable );
		void execute(
			__rw__ T_Context& ctx ) const override;
		std::string variable;
	};

	struct OPLoadInput : public T_Op
	{
		explicit OPLoadInput(
			__rd__ std::string const& input );
		void execute(
			__rw__ T_Context& ctx ) const override;
		std::string input;
	};

	struct OPStoreVariable : public T_Op
	{
		explicit OPStoreVariable(
			__rd__ std::string const& variable );
		void execute(
			__rw__ T_Context& ctx ) const override;
		std::string variable;
	};

	/*--------------------------------------------------------------------*/

	struct OPAdd : public T_Op
	{
		OPAdd( ) : T_Op( OP_ADD ) {}
		void execute(
			__rw__ T_Context& ctx ) const override;
	};

	struct OPMul : public T_Op
	{
		OPMul( ) : T_Op( OP_MUL ) {}
		void execute(
			__rw__ T_Context& ctx ) const override;
	};

	struct OPNeg : public T_Op
	{
		OPNeg( ) : T_Op( OP_NEG ) {}
		void execute(
			__rw__ T_Context& ctx ) const override;
	};

	struct OPInv : public T_Op
	{
		OPInv( ) : T_Op( OP_INV ) {}
		void execute(
			__rw__ T_Context& ctx ) const override;
	};

	/*--------------------------------------------------------------------*/

	struct OPDup : public T_Op
	{
		explicit OPDup( uint32_t stackIndex = 0 );
		void execute(
			__rw__ T_Context& ctx ) const override;
		uint32_t stackIndex;
	};

	struct OPXchg : public T_Op
	{
		explicit OPXchg( uint32_t stackIndex = 1 );
		void execute(
			__rw__ T_Context& ctx ) const override;
		uint32_t stackIndex;
	};

	/*--------------------------------------------------------------------*/

	struct OPSetUniform : public T_Op
	{
		OPSetUniform(
			__rd__ const uint32_t program ,
			__rd__ const uint32_t uniform ,
			__rd__ const uint32_t count ,
			__rd__ const bool integer );
		void execute(
			__rw__ T_Context& ctx ) const override;

		uint32_t program;
		uint32_t uniform;
		uint32_t count;
		bool integer;
	};

	struct OPUsePipeline : public T_Op
	{
		explicit OPUsePipeline(
			__rd__ const uint32_t index );
		void execute(
			__rw__ T_Context& ctx ) const override;

		uint32_t pipeline;
	};

	struct OPUseTexture : public T_Op
	{
		OPUseTexture(
			__rd__ const uint32_t binding ,
			__rd__ const uint32_t texture ,
			__rd__ const uint32_t sampler = 0 );
		void execute(
			__rw__ T_Context& ctx ) const override;

		uint32_t binding;
		uint32_t texture;
		uint32_t sampler;
	};

	struct OPUseFramebuffer : public T_Op
	{
		explicit OPUseFramebuffer(
			__rd__ const uint32_t framebuffer );
		void execute(
			__rw__ T_Context& ctx ) const override;

		uint32_t framebuffer;
	};

	struct OPSetViewport : public T_Op
	{
		OPSetViewport( ) : T_Op( OP_SET_VIEWPORT ) { }
		void execute(
			__rw__ T_Context& ctx ) const override;
	};

	/*--------------------------------------------------------------------*/

	struct OPClear : public T_Op
	{
		OPClear( ) : T_Op( OP_CLEAR ) { }
		void execute(
			__rw__ T_Context& ctx ) const override;
	};

	struct OPFullscreen : public T_Op
	{
		OPFullscreen( ) : T_Op( OP_FULLSCREEN ) { }
		void execute(
			__rw__ T_Context& ctx ) const override;
	};

	/*====================================================================*/

	void Execute(
		__rd__ T_Operations const& operations ,
		__rw__ T_Context& context );

} // namespace cops