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

namespace storm {
	STORM_PKG(core.lang);

	class Type;
	class Function;
	class MemberVar;

	/**
	 * Description of a type that needs to be rewritten. Expressed as a series of member copy
	 * operations, possibly in combination with initializations and destructors.
	 */
	class TypeTransform : public ObjectOn<Compiler> {
		STORM_CLASS;
	public:
		// Create, specify `old` and `new` types.
		STORM_CTOR TypeTransform(Type *oldType, Type *newType);

		// Get the old and new types.
		Type *STORM_FN oldType() const { return oldT; }
		Type *STORM_FN newType() const { return newT; }

		// Add a variable that has been added from `old` to `new`.
		void STORM_FN added(MemberVar *var);

		// Add a variable that has been removed from `old` to `new`.
		void STORM_FN removed(MemberVar *var);

		// Add a variable that is the same from `old` to `new`.
		void STORM_FN same(MemberVar *o, MemberVar *n);

		// Apply this transform to an object.
		RootObject *apply(RootObject *old);

		// Info in a plain C++ structure that can be used to transform offsets into the structure,
		// and can be used while the GC is paused.
		class Summary {
		public:
			// Size of the type itself.
			size_t size;

			// A single variable.
			struct Var {
				// Start offset.
				size_t start;

				// Size of the variable.
				size_t size;

				// Transformed offset.
				size_t newStart;

				// Compare them.
				bool operator <(const Var &o) const {
					return start < o.start;
				}
				bool operator <(size_t o) const {
					return start < o;
				}
			};

			// All variables, sorted to make lookup faster.
			vector<Var> variables;

			// Create.
			Summary(size_t size, const vector<Var> &vars);

			// Translate an offset.
			size_t translate(size_t from) const;
		};

		// Create a summary.
		Summary summary() const;

	private:
		// Old and new types.
		Type *oldT;
		Type *newT;

		// Variables to handle.
		struct MapItem {
			// Old variable.
			MemberVar *oldVar;

			// New variable.
			MemberVar *newVar;

			// Cache of the function to call when applying the transform. Initially null.
			Function *cache;
		};

		// Variables to manage.
		GcArray<MapItem> *variables;

		// Make sure 'variables' is large enough to hold at least one additional item.
		void grow();

		// Initialize a new variable.
		void initVar(RootObject *object, MemberVar *var, Function *&cache);

		// Copy a variable.
		void copyVar(RootObject *to, MemberVar *toVar, RootObject *from, MemberVar *fromVar, Function *&cache);
	};

}
