#pragma once
#include "Core/GcArray.h"

namespace gui {

	/**
	 * Description of the layout of a vertex type.
	 *
	 * Generated by the code in Storm to let the C++ code know how to interpret the contents of
	 * vertex buffers. Since the intention is that Storm creates the representation and C++ reads
	 * it, the class is designed to be easily readable from C++, while perhaps a bit more cumbersome
	 * from Storm.
	 *
	 * Note that a description of an entire vertex type is an array of these values. The description
	 * could provide an "index" member that can be used to combine separate vertex buffers (e.g.
	 * from different parameters). Support for this on OpenGL is currently unclear. If this is
	 * possible, perhaps rename to "InputDesc"?
	 */
	class VertexDesc : public Object {
		STORM_CLASS;
	public:
		// Create a vertex description.
		STORM_CTOR VertexDesc(Nat elementSize);

		// Copy.
		VertexDesc(const VertexDesc &other);

		// Get the total size of this element.
		Nat STORM_FN elementSize() const { return elemSize; }

		/**
		 * A single element in the description.
		 *
		 * Intentionally not visible to Storm -- it is usually not needed there.
		 */
		class Elem {
		public:
			// Input slot number (i.e. which input array is this vertex from?)
			Nat slot;

			// Offset of this component inside the vertex (bytes in memory).
			Nat offset;

			// Identifier ID to bind to. Expected to be unique.
			// For Direct3D, the identifier is used to create a "semantic" value of "ID<id>_" (semantics
			// cannot end with a number, probably because since ID:s are a sub-part of them).
			// For OpenGL, the identifier maps to the input location.
			Nat identifier;

			// Simple format description:

			// Integer/float.
			Bool integer;

			// Number of components. Can be 1, 2, 3, 4, or 16 (4x4 matrix).
			// Note that for anything larger than 4 elements, we need to skip that many elements
			// in the identifier.
			Nat components;

			// Create.
			Elem(Nat slot, Nat offset, Nat identifier, Bool integer, Nat components)
				: slot(slot), offset(offset), identifier(identifier), integer(integer), components(components) {}

			// Helper for output.
			void toS(StrBuf *to) const;
		};

		// Add an element. For parameters, see the contents of 'Element'. Computes 'identifier'
		// automatically. Adds to slot number 0.
		void STORM_FN add(Nat offset, Bool integer, Nat components);

		// Append the contents from another description to this one. Set all slots in 'append' to
		// the specified slot. Also adjust offsets to match the end of this description.
		void STORM_FN append(VertexDesc *append, Nat slot);

		// Output.
		void STORM_FN toS(StrBuf *to) const;

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

		// Get an element from C++.
		Elem &at(Nat id) const {
			return data->v[id];
		}

	private:
		// Array.
		GcArray<Elem> *data;

		// Element size.
		Nat elemSize;

		// Type description for 'data'.
		static const GcType dataType;
	};

}
