/*************************************************************************/
/*  canvas_item_material.h                                               */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#ifndef CANVAS_ITEM_MATERIAL_H
#define CANVAS_ITEM_MATERIAL_H

#include "scene/resources/material.h"

class CanvasItemMaterial : public Material {
	GDCLASS(CanvasItemMaterial, Material);

public:
	enum BlendMode {
		BLEND_MODE_MIX,
		BLEND_MODE_ADD,
		BLEND_MODE_SUB,
		BLEND_MODE_MUL,
		BLEND_MODE_PREMULT_ALPHA,
		BLEND_MODE_DISABLED
	};

	enum LightMode {
		LIGHT_MODE_NORMAL,
		LIGHT_MODE_UNSHADED,
		LIGHT_MODE_LIGHT_ONLY
	};

private:
	union MaterialKey {
		struct {
			uint32_t blend_mode : 4;
			uint32_t light_mode : 4;
			uint32_t particles_animation : 1;
			uint32_t invalid_key : 1;
		};

		uint32_t key = 0;

		static uint32_t hash(const MaterialKey &p_key) {
			return hash_murmur3_one_32(p_key.key);
		}
		bool operator==(const MaterialKey &p_key) const {
			return key == p_key.key;
		}
	};

	struct ShaderNames {
		StringName particles_anim_h_frames;
		StringName particles_anim_v_frames;
		StringName particles_anim_loop;
	};

	static ShaderNames *shader_names;

	struct ShaderData {
		RID shader;
		int users = 0;
	};

	static HashMap<MaterialKey, ShaderData, MaterialKey> shader_map;

	MaterialKey current_key;

	_FORCE_INLINE_ MaterialKey _compute_key() const {
		MaterialKey mk;
		mk.key = 0;
		mk.blend_mode = blend_mode;
		mk.light_mode = light_mode;
		mk.particles_animation = particles_animation;
		return mk;
	}

	static Mutex material_mutex;
	static SelfList<CanvasItemMaterial>::List *dirty_materials;
	SelfList<CanvasItemMaterial> element;

	void _update_shader();
	_FORCE_INLINE_ void _queue_shader_change();
	_FORCE_INLINE_ bool _is_shader_dirty() const;

	bool is_initialized = false;
	BlendMode blend_mode = BLEND_MODE_MIX;
	LightMode light_mode = LIGHT_MODE_NORMAL;
	bool particles_animation = false;

	// Proper values set in constructor.
	int particles_anim_h_frames = 0;
	int particles_anim_v_frames = 0;
	bool particles_anim_loop = false;

protected:
	static void _bind_methods();
	void _validate_property(PropertyInfo &property) const override;

public:
	void set_blend_mode(BlendMode p_blend_mode);
	BlendMode get_blend_mode() const;

	void set_light_mode(LightMode p_light_mode);
	LightMode get_light_mode() const;

	void set_particles_animation(bool p_particles_anim);
	bool get_particles_animation() const;

	void set_particles_anim_h_frames(int p_frames);
	int get_particles_anim_h_frames() const;
	void set_particles_anim_v_frames(int p_frames);
	int get_particles_anim_v_frames() const;

	void set_particles_anim_loop(bool p_loop);
	bool get_particles_anim_loop() const;

	static void init_shaders();
	static void finish_shaders();
	static void flush_changes();

	virtual RID get_shader_rid() const override;

	virtual Shader::Mode get_shader_mode() const override;

	CanvasItemMaterial();
	virtual ~CanvasItemMaterial();
};

VARIANT_ENUM_CAST(CanvasItemMaterial::BlendMode)
VARIANT_ENUM_CAST(CanvasItemMaterial::LightMode)

#endif // CANVAS_ITEM_MATERIAL_H