/*
 * Copyright (C)2005-2017 Haxe Foundation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
package haxe.macro;

/*
    Warning: Some of these types correspond to compiler-internal data structures
    and might change in minor Haxe releases in order to adapt to internal changes.
*/

/**
	Represents a reference to internal compiler structure. It exists to avoid
	expensive encoding if it is not required and to ensure that physical
	equality remains intact.

	A structure is only encoded when user requests it through `ref.get()`.
*/
typedef Ref<T> = {
	public function get() : T;
	public function toString() : String;
}

/**
	Represents a type.
*/
enum Type {
	/**
		Represents a monomorph.

		@see https://haxe.org/manual/types-monomorph.html
	**/
	TMono( t : Ref<Null<Type>> );

	/**
		Represents an enum instance.

		@see https://haxe.org/manual/types-enum-instance.html
	**/
	TEnum( t : Ref<EnumType>, params : Array<Type> );

	/**
		Represents a class instance.

		@see https://haxe.org/manual/types-class-instance.html
	**/
	TInst( t : Ref<ClassType>, params : Array<Type> );

	/**
		Represents a typedef.

		@see https://haxe.org/manual/type-system-typedef.html
	**/
	TType( t : Ref<DefType>, params : Array<Type> );

	/**
		Represents a function type.

		@see https://haxe.org/manual/types-function.html
	**/
	TFun( args : Array<{ name : String, opt : Bool, t : Type }>, ret : Type );

	/**
		Represents an anonymous structure type.

		@see https://haxe.org/manual/types-anonymous-structure.html
	**/
	TAnonymous( a : Ref<AnonType> );

	/**
		Represents Dynamic.

		@see https://haxe.org/manual/types-dynamic.html
	**/
	TDynamic( t : Null<Type> );

	/**
		Used internally by the compiler to delay some typing.
	**/
	TLazy( f : Void -> Type );

	/**
		Represents an abstract type.

		@see https://haxe.org/manual/types-abstract.html
	**/
	TAbstract( t : Ref<AbstractType>, params : Array<Type> );
}

/**
	Represents information for anonymous structure types.
*/
typedef AnonType = {
	/**
		The class fields of the structure.
	**/
	var fields : Array<ClassField>;
	/**
		The status/kind of the structure.
	**/
	var status : AnonStatus;
}

/**
	Represents the kind of the anonymous structure type.
**/
enum AnonStatus {
	/**
		A closed structure is considered complete. That is, no further fields
		can be added to it.
	**/
	AClosed;

	/**
		An open structure allows having additional fields added to it, which is
		used during type inference. It is closed upon unification.
	**/
	AOpened;

	/**
		A const structure is one that appears directly in syntax. It cannot be
		assigned to a smaller structure type (that is, it does not allow
		structural sub-typing).
	**/
	AConst;

	/**
		Represents a structure which extends one or multiple structures defined
		in `tl`.

		@see https://haxe.org/manual/type-system-extensions.html
	**/
	AExtend( tl:Ref<Array<Type>> );

	/**
		A structure that represents the static fields of a class.
	**/
	AClassStatics( t : Ref<ClassType> );

	/**
		A structure that represents the constructors of an enum.
	**/
	AEnumStatics( t : Ref<EnumType> );

	/**
		A structure that represents the static fields of an abstract.
	**/
	AAbstractStatics( t : Ref<AbstractType> );
}

/**
	Represents the declaration of type parameters.
*/
typedef TypeParameter = {
	/**
		The name of the type parameter.
	**/
	var name: String;

	/**
		The type of the type parameter. It is guaranteed to be a `TInst` with a
		`KTypeParameter` kind.
	**/
	var t: Type;
}

/**
	Represents a class field.
*/
typedef ClassField = {
	/**
		The name of the class field.
	**/
	var name : String;

	/**
		The type of the class field.
	**/
	var type : Type;

	/**
		Whether or not the class field is public.
	**/
	var isPublic : Bool;

	/**
		The type parameters of the class field.
	**/
	var params : Array<TypeParameter>;

	/**
		The metadata of the class field.
	**/
	var meta : MetaAccess;

	/**
		The class field kind.
	**/
	var kind : FieldKind;

	/**
		Returns the typed expression of the class field.
	**/
	function expr() : Null<TypedExpr>;

	/**
		The position of the class field.
	**/
	var pos : Expr.Position;

	/**
		The associated documentation of the class field.
	**/
	var doc : Null<String>;

	/**
		The overload fields of the class field.
	**/
	var overloads : Ref<Array<ClassField>>;
}

/**
	Represents an enum constructor.
*/
typedef EnumField = {
	/**
		The name of the enum constructor.
	**/
	var name : String;

	/**
		The type of the enum constructor.
	**/
	var type : Type;

	/**
		The position of the enum constructor.
	**/
	var pos : Expr.Position;

	/**
		The metadata of the enum constructor.
	**/
	var meta : MetaAccess;

	/**
		The index of the enum constructor, i.e. in which position it appears
		in the syntax.
	**/
	var index : Int;

	/**
		The associated documentation of the enum constructor.
	**/
	var doc : Null<String>;

	/**
		The type parameters of the enum constructor.
	**/
	var params : Array<TypeParameter>;
}


/**
	Represents the kind of a class.
*/
enum ClassKind {
	/**
		A normal class.
	**/
	KNormal;

	/**
		A type parameter class with a set of constraints.
	**/
	KTypeParameter(constraints:Array<Type>);

	/**
		A structurally extended class.

		@deprecated
	**/
	KExtension(cl:Ref<ClassType>, params:Array<Type>);

	/**
		A special kind of class to encode expressions into type parameters.
	**/
	KExpr(expr:Expr);

	/**
		A `@:generic` base class.
	**/
	KGeneric;

	/**
		A concrete `@:generic` instance, referencing the original class and the
		applied type parameters.
	**/
	KGenericInstance(cl:Ref<ClassType>, params:Array<Type>);

	/**
		A special class for `haxe.macro.MacroType`.

		@deprecated
	**/
	KMacroType;

	/**
		An implementation class of an abstract, i.e. where all its run-time code
		is.
	**/
	KAbstractImpl(a:Ref<AbstractType>);

	/**
		A `@:genericBuild` class
	**/
	KGenericBuild;
}

/**
	The information that all types (`ClassType`, `EnumType`, `DefType`,
	`AbstractType`) have in common.
**/
typedef BaseType = {
	/**
		The package of the type.
	**/
	var pack : Array<String>;

	/**
		The name of the type.
	**/
	var name : String;

	/**
		The module name of the type, which might be different.
	**/
	var module : String;

	/**
		The position of the type.
	**/
	var pos : Expr.Position;

	/**
		Whether or not the type is private.
	**/
	var isPrivate : Bool;

	/**
		Whether or not the type is extern.
	**/
	var isExtern : Bool;

	/**
		The type parameters of the type.
	**/
	var params : Array<TypeParameter>;

	/**
		The metadata of the type.
	**/
	var meta : MetaAccess;

	/**
		The associated documentation of the class field.
	**/
	var doc : Null<String>;

	/**
		Allows excluding the type from compilation.
	**/
	function exclude() : Void;
}

/**
	Represents a class type.
*/
typedef ClassType = {> BaseType,
	/**
		The kind of the class.
	**/
	var kind : ClassKind;

	/**
		If true the type is an interface, otherwise it is a class.
	**/
	var isInterface : Bool;

	/**
		The parent class and its type parameters, if available.
	**/
	var superClass : Null<{ t : Ref<ClassType>, params : Array<Type> }>;

	/**
		The implemented interfaces and their type parameters.
	**/
	var interfaces : Array<{ t : Ref<ClassType>, params : Array<Type> }>;

	/**
		The member fields of the class.
	**/
	var fields : Ref<Array<ClassField>>;

	/**
		The static fields of the class.
	**/
	var statics : Ref<Array<ClassField>>;

	//var dynamic : Null<Type>;
	//var arrayAccess : Null<Type>;

	/**
		The constructor of the class, if available.
	**/
	var constructor : Null<Ref<ClassField>>;

	/**
		The `__init__` expression of the class, if available.
	**/
	var init : Null<TypedExpr>;

	/**
		The list of fields that have override status.
	**/
	var overrides : Array<Ref<ClassField>>;
}

/**
	Represents an enum type.
*/
typedef EnumType = {> BaseType,
	/**
		The available enum constructors.
	**/
	var constructs : Map<String, EnumField>;

	/**
		An ordered list of enum constructor names.
	**/
	var names : Array<String>;
}

/**
	Represents a typedef.
*/
typedef DefType = {> BaseType,
	/**
		The target type of the typedef.
	**/
	var type : Type;
}

/**
	Represents an abstract type.
*/
typedef AbstractType = {>BaseType,
	/**
		The underlying type of the abstract.
	**/
	var type : Type;

	/**
		The implementation class of the abstract, if available.
	**/
	var impl : Null<Ref<ClassType>>;

	/**
		The defined binary operators of the abstract.
	**/
	var binops : Array<{op:Expr.Binop, field:ClassField}>;

	/**
		The defined unary operators of the abstract.
	**/
	var unops : Array<{op:Expr.Unop, postFix:Bool, field:ClassField}>;

	/**
		The available implicit from-casts of the abstract.

		@see https://haxe.org/manual/types-abstract-implicit-casts.html
	**/
	var from : Array<{t:Type, field:Null<ClassField>}>;

	/**
		The available implicit to-casts of the abstract.

		@see https://haxe.org/manual/types-abstract-implicit-casts.html
	**/
	var to : Array<{t:Type, field:Null<ClassField>}>;

	/**
		The defined array-access fields of the abstract.
	**/
	var array : Array<ClassField>;

	/**
		The method used for resolving unknown field access, if available.
	**/
	@:require(haxe_ver >= 3.3)
	var resolve : Null<ClassField>;
}

/**
	MetaAccess is a wrapper for the `Metadata` array. It can be used to add
	metadata to and remove metadata from its origin.
**/
typedef MetaAccess = {
	/**
		Return the wrapped `Metadata` array.

		Modifying this array has no effect on the origin of `this` MetaAccess.
		The `add` and `remove` methods can be used for that.
	**/
	function get() : Expr.Metadata;

	/**
		Extract metadata entries by given `name`.

		If there's no metadata with such name, empty array `[]` is returned.

		If `name` is null, compilation fails with an error.
	**/
	function extract( name : String ) : Array<Expr.MetadataEntry>;

	/**
		Adds the metadata specified by `name`, `params` and `pos` to the origin
		of `this` MetaAccess.

		Metadata names are not unique during compilation, so this method never
		overwrites a previous metadata.

		If a `Metadata` array is obtained through a call to `get`, a subsequent
		call to `add` has no effect on that array.

		If any argument is null, compilation fails with an error.
	**/
	function add( name : String, params : Array<Expr>, pos : Expr.Position ) : Void;

	/**
		Removes all `name` metadata entries from the origin of `this`
		MetaAccess.

		This method might clear several metadata entries of the same name.

		If a `Metadata` array is obtained through a call to `get`, a subsequent
		call to `remove` has no effect on that array.

		If `name` is null, compilation fails with an error.
	**/
	function remove( name : String ) : Void;

	/**
		Tells if the origin of `this` MetaAccess has a `name` metadata entry.

		If `name` is null, compilation fails with an error.
	**/
	function has( name : String ) : Bool;
}

/**
	Represents a field kind.
*/
enum FieldKind {
	/**
		A variable of property, depending on the `read` and `write` values.
	**/
	FVar( read : VarAccess, write : VarAccess );

	/**
		A method
	**/
	FMethod( k : MethodKind );
}

/**
	Represents the variable accessor.
*/
enum VarAccess {
	/**
		Normal access (`default`).
	**/
	AccNormal;

	/**
		Private access (`null`).
	**/
	AccNo;

	/**
		No access (`never`).
	**/
	AccNever;

	/**
		Unused.
	**/
	AccResolve;

	/**
		Access through accessor function (`get`, `set`, `dynamic`).
	**/
	AccCall;

	/**
		Inline access (`inline`).
	**/
	AccInline;

	/**
		Failed access due to a `@:require` metadata.
	**/
	AccRequire( r : String, ?msg : String );
}

/**
	Represents the method kind.
*/
enum MethodKind {
	/**
		A normal method.
	**/
	MethNormal;

	/**
		An inline method.

		@see https://haxe.org/manual/class-field-inline.html
	**/
	MethInline;

	/**
		A dynamic, rebindable method.

		@see https://haxe.org/manual/class-field-dynamic.html
	**/
	MethDynamic;

	/**
		A macro method.
	**/
	MethMacro;
}

/**
	Represents typed constant.
*/
enum TConstant {
	/**
		An `Int` literal.
	**/
	TInt(i:Int);

	/**
		A `Float` literal, represented as String to avoid precision loss.
	**/
	TFloat(s:String);

	/**
		A `String` literal.
	**/
	TString(s:String);

	/**
		A `Bool` literal.
	**/
	TBool(b:Bool);

	/**
		The constant `null`.
	**/
	TNull;

	/**
		The constant `this`.
	**/
	TThis;

	/**
		The constant `super`.
	**/
	TSuper;
}

/**
	Represents a variable in the typed AST.
*/
typedef TVar = {
	/**
		The unique ID of the variable.
	**/
	public var id(default, never):Int;

	/**
		The name of the variable.
	**/
	public var name(default, never):String;

	/**
		The type of the variable.
	**/
	public var t(default, never):Type;

	/**
		Whether or not the variable has been captured by a closure.
	**/
	public var capture(default, never):Bool;

	/**
		Special information which is internally used to keep track of closure.
		information
	**/
	public var extra(default,never):Null<{params: Array<TypeParameter>, expr: Null<TypedExpr>}>;

	/**
		The metadata of the variable.
	**/
	public var meta(default,never):Null<MetaAccess>;
}

/**
	Represents a module type. These are the types that can be declared in a Haxe
	module and which are passed to the generators (except `TTypeDecl`).
*/
enum ModuleType {
	/**
		A class.
	**/
	TClassDecl(c:Ref<ClassType>);

	/**
		An enum.
	**/
	TEnumDecl(e:Ref<EnumType>);

	/**
		A typedef.
	**/
	TTypeDecl(t:Ref<DefType>);

	/**
		An abstract.
	**/
	TAbstract(a:Ref<AbstractType>);
}

/**
	Represents a function in the typed AST.
*/
typedef TFunc = {
	/**
		A list of function arguments identified by an argument variable `v` and
		an optional initialization `value`.
	**/
	var args: Array<{v:TVar, value:Null<TConstant>}>;

	/**
		The return type of the function.
	**/
	var t: Type;

	/**
		The expression of the function body.
	**/
	var expr: TypedExpr;
}

/**
	Represents the kind of field access in the typed AST.
*/
enum FieldAccess {
	/**
		Access of field `cf` on a class instance `c` with type parameters
		`params`.
	**/
	FInstance(c:Ref<ClassType>, params:Array<Type>, cf:Ref<ClassField>);

	/**
		Static access of a field `cf` on a class `c`.
	**/
	FStatic(c:Ref<ClassType>, cf:Ref<ClassField>);

	/**
		Access of field `cf` on an anonymous structure.
	**/
	FAnon(cf:Ref<ClassField>);

	/**
		Dynamic field access of a field named `s`.
	**/
	FDynamic(s:String);

	/**
		Closure field access of field `cf` on a class instance `c` with type
		parameters `params`.
	**/
	FClosure(c:Null<{c:Ref<ClassType>, params:Array<Type>}>, cf:Ref<ClassField>);

	/**
		Field access to an enum constructor `ef` of enum `e`.
	**/
	FEnum(e:Ref<EnumType>, ef:EnumField);
}

/**
	Represents kind of a node in the typed AST.
*/
enum TypedExprDef {
	/**
		A constant.
	**/
	TConst(c:TConstant);

	/**
		Reference to a local variable `v`.
	**/
	TLocal(v:TVar);

	/**
		Array access `e1[e2]`.
	**/
	TArray(e1:TypedExpr, e2:TypedExpr);

	/**
		Binary operator `e1 op e2`.
	**/

	TBinop(op:Expr.Binop, e1:TypedExpr, e2:TypedExpr);

	/**
		Field access on `e` according to `fa`.
	**/
	TField(e:TypedExpr, fa:FieldAccess);

	/**
		Reference to a module type `m`.
	**/
	TTypeExpr(m:ModuleType);

	/**
		Parentheses `(e)`.
	**/
	TParenthesis(e:TypedExpr);

	/**
		An object declaration.
	**/
	TObjectDecl(fields:Array<{name:String, expr:TypedExpr}>);

	/**
		An array declaration `[el]`.
	**/
	TArrayDecl(el:Array<TypedExpr>);

	/**
		A call `e(el)`.
	**/
	TCall(e:TypedExpr, el:Array<TypedExpr>);

	/**
		A constructor call `new c<params>(el)`.
	**/
	TNew(c:Ref<ClassType>, params: Array<Type>, el:Array<TypedExpr>);

	/**
		An unary operator `op` on `e`:

		* e++ (op = OpIncrement, postFix = true)
		* e-- (op = OpDecrement, postFix = true)
		* ++e (op = OpIncrement, postFix = false)
		* --e (op = OpDecrement, postFix = false)
		* -e (op = OpNeg, postFix = false)
		* !e (op = OpNot, postFix = false)
		* ~e (op = OpNegBits, postFix = false)
	**/
	TUnop(op:Expr.Unop, postFix:Bool, e:TypedExpr);

	/**
		A function declaration.
	**/
	TFunction(tfunc:TFunc);

	/**
		A variable declaration `var v` or `var v = expr`.
	**/
	TVar(v:TVar, expr:Null<TypedExpr>);

	/**
		A block declaration `{el}`.
	**/
	TBlock(el:Array<TypedExpr>);

	/**
		A `for` expression.
	**/
	TFor(v:TVar, e1:TypedExpr, e2:TypedExpr);

	/**
		An `if(econd) eif` or `if(econd) eif else eelse` expression.
	**/
	TIf(econd:TypedExpr, eif:TypedExpr, eelse:Null<TypedExpr>);

	/**
		Represents a `while` expression.
		When `normalWhile` is `true` it is `while (...)`.
		When `normalWhile` is `false` it is `do {...} while (...)`.
	**/
	TWhile(econd:TypedExpr, e:TypedExpr, normalWhile:Bool);

	/**
		Represents a `switch` expression with related cases and an optional
		`default` case if edef != null.
	**/
	TSwitch(e:TypedExpr, cases:Array<{values:Array<TypedExpr>, expr:TypedExpr}>, edef:Null<TypedExpr>);

	/**
		Represents a `try`-expression with related catches.
	**/
	TTry(e:TypedExpr, catches:Array<{v:TVar, expr:TypedExpr}>);

	/**
		A `return` or `return e` expression.
	**/
	TReturn(e:Null<TypedExpr>);

	/**
		A `break` expression.
	**/
	TBreak;

	/**
		A `continue` expression.
	**/
	TContinue;

	/**
		A `throw e` expression.
	**/
	TThrow(e:TypedExpr);

	/**
		A `cast e` or `cast (e, m)` expression.
	**/
	TCast(e:TypedExpr, m:Null<ModuleType>);

	/**
		A `@m e1` expression.
	**/
	TMeta(m:Expr.MetadataEntry, e1:TypedExpr);

	/**
		Access to an enum parameter (generated by the pattern matcher).
	**/
	TEnumParameter(e1:TypedExpr, ef:EnumField, index:Int);
}

/**
	Represents a typed AST node.
*/
typedef TypedExpr = {
	/**
		The expression kind.
	**/
	var expr: TypedExprDef;

	/**
		The position of the expression.
	**/
	var pos: Expr.Position;

	/**
		The type of the expression.
	**/
	var t: Type;
}
