#ifndef PW_VISUAL_SHADER_HPP
#define PW_VISUAL_SHADER_HPP

#include <pw/core/globals.hpp>
#include <pw/core/matrix.hpp>
#include <pw/core/vector.hpp>
#include <pw/core/debug.hpp>

#include <map>
#include <variant>

namespace pw {

class shader {
public:

	shader();
    ~shader();

	enum class code_type {
		vertex,
		fragment,
		geometry,
		compute
	};

    void set_source(const std::string& c,code_type t) { _source[t] = c; }
	std::string source(code_type t) { return _source[t]; }

	bool ready() const;

	shader& set(int location,float v);
	shader& set(int location,matrix4x4f const & v);
	shader& set(int location,vector4f const & v);

	int uniform_location(std::string const & name) const;

	template<typename T>
	shader & set(std::string const & name, T&& value)
	{
		int location = uniform_location(name);
		if (location >= 0)
			return set(location, std::forward<T>(value));
		else
			debug::e() << "missing uniform: '" << name << "'";
		return *this;
	}

	bool build();

    void use();

	using uniform_t = std::variant<bool,int,float,double,vector2f,vector3f,vector4f,matrix4x4f>;
	using uniform_entry_t = std::tuple<std::string,uniform_t,int>;

	using uniform_cache_t = std::vector<uniform_entry_t>;

	void set_uniforms(uniform_cache_t c);

	unsigned int native_handle() const;

protected:

	std::map<code_type,std::string> _source;

	struct impl;
	std::unique_ptr<impl> _impl;

};

}

#endif
