#pragma once
#include "Core/StrBuf.h"
#include "Core/Str.h"
#include "Compiler/Name.h"
#include "Compiler/Scope.h"

namespace storm {
	namespace bs {
		STORM_PKG(lang.bs);

		/**
		 * One formal parameter to a function, with the type not yet resolved.
		 */
		class NameParam {
			STORM_VALUE;
		public:
			STORM_CTOR NameParam(SrcName *type, Str *name);
			STORM_CTOR NameParam(SrcName *type, syntax::SStr *name);

			SrcName *type;
			Str *name;

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

		// Nameless parameter.
		NameParam STORM_FN nameless(SrcName *type);

		wostream &operator <<(wostream &to, NameParam p);


		/**
		 * One formal parameter to a function, with its type resolved.
		 */
		class ValParam {
			STORM_VALUE;
		public:
			STORM_CTOR ValParam(Value type, Str *name);
			STORM_CTOR ValParam(Value type, syntax::SStr *name);

			Value STORM_FN type() const { return Value(t, ref); }
			Str *name;

			// Is this a 'this' parameter?
			Bool thisParam() const { return thisPar; }

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

		private:
			// Note: We disassemble the Value in order to not make this structure large due to
			// misaligned boolean variables (one in the Value and one here).
			ValParam(Value type, Str *name, Bool thisParam);

			// The type (we make a Value out of this).
			Type *t;

			// Is the type a reference (we make a Value out of this).
			Bool ref;

			// Is this parametera "this" parameter? I.e. is it known to be the this parameter?
			Bool thisPar;

			friend ValParam thisParam(Type *me);
			friend ValParam thisParam(Str *name, Type *me);
		};

		wostream &operator <<(wostream &to, ValParam p);

		// Create a ValParam representing the 'this' parameter. This distinction is made to
		// differentiate between the 'this'-variable generated by the system (that we know things
		// about) and any other variable named 'this' by the user for convenience (makes non-member
		// functions more convenient to write at times, so we want to support it).
		//
		// The Basic Storm front-end will use this information to make assumptions about this
		// parameter. In particular:
		// - the parameter will be made constant.
		// - the front-end assumes that we don't need to switch threads when calling member
		//   functions on the object referred to by this variable.
		ValParam STORM_FN thisParam(Type *me);
		ValParam STORM_FN thisParam(Str *name, Type *me);

		// Get parameters as Values.
		ValParam STORM_FN resolve(NameParam param, Scope scope);
		Array<ValParam> *STORM_FN resolve(Array<NameParam> *params, Scope scope);
		Array<ValParam> *STORM_FN resolve(Array<NameParam> *params, Type *me, Scope scope);
		Array<Value> *STORM_FN values(Array<ValParam> *params);

		// Merge two arrays of values and names into one array of ValParam.
		Array<ValParam> *STORM_FN merge(Array<Value> *val, Array<Str *> *names);

	}
}
