#pragma once
#include "DirtyRange.h"

namespace gui {

	class Graphics3D;

	/**
	 * A resource used in rendering that might need to create backend-specific resources.
	 *
	 * Any backend specific resources are created lazily, and the Resource keeps track of
	 * potentially multiple such resources for multiple backends.
	 */
	class Resource3D : public ObjectOn<Render> {
		STORM_ABSTRACT_CLASS;
	public:
		// Create.
		STORM_CTOR Resource3D();

		// Cleanup function for backend-specific data.
		typedef void (*Cleanup)(void *);

		// Destroy the resource for a particular Graphics backend.
		void STORM_FN destroy(Graphics3D *g);

		// Get the backend-specific object.
		void *forGraphics(Graphics3D *g);

	protected:
		// Call to mark a range of this resource as 'dirty'. This will cause it to be updated the
		// next time it is used.
		void STORM_FN markDirty(Nat element);
		void STORM_FN markDirty(Nat begin, Nat end);

		// Called whenever something changed that makes it necessary to re-create the resource when
		// it is rendered again.
		void STORM_FN recreate();

	private:
		// Storage of elements.
		struct Element {
			// The stored pointer (GC:d).
			void *data;

			// Cleanup function (not GC:d).
			Cleanup clean;

			// What range of elements need to be updated?
			// What 'range' means in this situation is entirely dependent on the resource type.
			DirtyRange dirty;

			// Number of references. If zero, this element is unused.
			Nat refs;
		};

		// GcType for Element.
		static const GcType elemType;

		// First element's data.
		UNKNOWN(PTR_GC) void *firstData;

		// First element's cleanup.
		UNKNOWN(PTR_NOGC) Cleanup firstClean;

		// First element's dirty range.
		DirtyRange firstDirty;

		// First element's references.
		Nat firstRefs;

		// Offset of the first element (i.e. the real index of the first element).
		Nat offset;

		// Any remaining elements.
		GcArray<Element> *more;

		// Get an element. Returns number of references. Optionally increases the refcount if the element exists.
		// Returns the old value of the refcount.
		void get(Nat id, void *&ptr, Cleanup &clean, Nat &refs, DirtyRange &dirty, Bool addRef);

		// Set an element. Possibly creates it.
		Nat set(Nat id, void *ptr, Cleanup clean, Bool addRef);

		// Clear an element (i.e. decrementing the refcount).
		void clear(Nat id);

		// Resize the storage.
		void resize(Nat rangeMin, Nat rangeMax);

		// Shrink the storage to fit.
		void shrink();

		// Called whenever a resource needs to be created.
		void create(Graphics3D *g, void *&data, Cleanup &cleanup);

		// Called whenever a resource needs to be updated.
		void update(Graphics3D *g, void *data, const DirtyRange &dirty);
	};

}
