#pragma once
#include "Core/Array.h"
#include "Resource.h"
#include "Enums.h"

namespace gui {

	/**
	 * A buffer of vertex indices.
	 */
	class IndexBufferBase : public Resource3D {
		STORM_CLASS;
	public:
		// Create an empty index buffer.
		STORM_CTOR IndexBufferBase();
		STORM_CTOR IndexBufferBase(DrawPrimitive p);

		// Create an index buffer based on an array.
		STORM_CTOR IndexBufferBase(Array<Nat> *src);
		STORM_CTOR IndexBufferBase(DrawPrimitive p, Array<Nat> *src);

		// Copy an existing index buffer.
		STORM_CTOR IndexBufferBase(IndexBufferBase *src);

		// Buffer usage.
		BufferUsage usage;

		// What primitive type does the buffer contain?
		DrawPrimitive primitive;

		// Special index value to restart the current primitive (relevant for line and triangle strips).
		static Nat STORM_FN restart() {
			return 0xFFFFFFFF;
		}

		// Reserve space.
		void STORM_FN reserve(Nat count);

		// Access an element.
		// Note: Storm has overloads for the [] operator.
		Nat STORM_FN get(Nat i) const {
			if (i >= data->filled)
				throwOutOfBounds(i);
			return data->v[i];
		}
		void STORM_FN set(Nat i, Nat to) {
			if (i >= data->filled)
				throwOutOfBounds(i);
			data->v[i] = to;
			markDirty(i);
		}

		// Number of elements.
		inline Nat STORM_FN count() const {
			return data ? Nat(data->filled) : 0;
		}

		// Empty/any?
		inline Bool STORM_FN empty() const { return count() == 0; }
		inline Bool STORM_FN any() const { return count() > 0; }

		// Add an element.
		void STORM_FN push(Nat elem);
		IndexBufferBase &STORM_FN operator <<(Nat elem) {
			push(elem);
			return *this;
		}

		// Insert an element.
		void STORM_FN insert(Nat pos, Nat elem);

		// Remove an element.
		void STORM_FN remove(Nat pos);

		// To string.
		void STORM_FN toS(StrBuf *out) const;

		// Get the buffer itself.
		const Nat *buffer() const {
			return data ? data->v : null;
		}

	private:
		// Host-allocated data.
		GcArray<Nat> *data;

		// Make room for at least one more element.
		void grow();

		// Helper to get exception code out of the hot path.
		void throwOutOfBounds(Nat index) const;
	};

}
