#pragma once

#include "Ident.h"
#include "Scanner.h"
#include "MachineTypes.h"
#include "Assembler.h"
#include "Array.h"

class LinkerObject;
class LinkerSection;
class LinkerRegion;
class Linker;
class Parser;

enum DecType
{
	DT_TYPE_VOID,
	DT_TYPE_NULL,
	DT_TYPE_BOOL,
	DT_TYPE_INTEGER,
	DT_TYPE_FLOAT,
	DT_TYPE_ENUM,
	DT_TYPE_POINTER,
	DT_TYPE_REFERENCE,
	DT_TYPE_RVALUEREF,
	DT_TYPE_ARRAY,
	DT_TYPE_STRUCT,
	DT_TYPE_UNION,
	DT_TYPE_TEMPLATE,
	DT_TYPE_FUNCTION,
	DT_TYPE_ASSEMBLER,
	DT_TYPE_AUTO,

	DT_CONST_INTEGER,
	DT_CONST_FLOAT,
	DT_CONST_FUNCTION,
	DT_CONST_ADDRESS,
	DT_CONST_DATA,
	DT_CONST_STRUCT,
	DT_CONST_POINTER,
	DT_CONST_REFERENCE,
	DT_CONST_ASSEMBLER,
	DT_CONST_CONSTRUCTOR,
	DT_CONST_TEMPLATE,

	DT_PACK_TEMPLATE,
	DT_PACK_VARIABLE,
	DT_PACK_ARGUMENT,
	DT_PACK_TYPE,
	DT_PACK_ANON,

	DT_VARIABLE,
	DT_ARGUMENT,
	DT_ELEMENT,
	DT_PADDING,
	DT_ANON,
	DT_LABEL,
	DT_VARIABLE_REF,
	DT_FUNCTION_REF,
	DT_LABEL_REF,
	DT_NAMESPACE,
	DT_BASECLASS,
	DT_CLABEL,

	DT_BINDING,

	DT_TEMPLATE,

	DT_VTABLE
};

// TypeFlags

static const uint64	DTF_SIGNED			= (1ULL << 0);
static const uint64	DTF_DEFINED			= (1ULL << 1);
static const uint64	DTF_GLOBAL			= (1ULL << 2);
static const uint64	DTF_VARIADIC		= (1ULL << 3);
static const uint64	DTF_INTRINSIC		= (1ULL << 4);
static const uint64	DTF_STATIC			= (1ULL << 5);
static const uint64	DTF_CONST			= (1ULL << 6);
static const uint64	DTF_VOLATILE		= (1ULL << 7);
static const uint64	DTF_EXTERN			= (1ULL << 8);
static const uint64 DTF_NATIVE			= (1ULL << 9);
static const uint64 DTF_UPPER_BYTE		= (1ULL << 10);
static const uint64 DTF_LOWER_BYTE		= (1ULL << 11);
static const uint64 DTF_SECTION_START	= (1ULL << 12);
static const uint64 DTF_SECTION_END		= (1ULL << 13);
static const uint64 DTF_FASTCALL		= (1ULL << 14);
static const uint64 DTF_INLINE			= (1ULL << 15);
static const uint64	DTF_ANALYZED	    = (1ULL << 16);
static const uint64 DTF_REQUEST_INLINE  = (1ULL << 17);
static const uint64 DTF_INTERRUPT		= (1ULL << 18);
static const uint64 DTF_EXPORT			= (1ULL << 19);
static const uint64 DTF_HWINTERRUPT		= (1ULL << 20);
static const uint64 DTF_STACKCALL		= (1ULL << 21);
static const uint64 DTF_ZEROPAGE		= (1ULL << 22);
static const uint64 DTF_PREVENT_INLINE  = (1ULL << 23);
static const uint64 DTF_STRIPED			= (1ULL << 24);
static const uint64 DTF_DYNSTACK		= (1ULL << 25);
static const uint64 DTF_PRIVATE			= (1ULL << 26);
static const uint64 DTF_PROTECTED		= (1ULL << 27);
static const uint64 DTF_VIRTUAL			= (1ULL << 28);
static const uint64 DTF_TEMPORARY		= (1ULL << 29);
static const uint64	DTF_COMPLETED		= (1ULL << 30);
static const uint64	DTF_CONSTEXPR		= (1ULL << 31);

static const uint64 DTF_AUTO_TEMPLATE	= (1ULL << 32);
static const uint64 DTF_BANK_INLAY		= (1ULL << 33);
static const uint64 DTF_PURE_VIRTUAL	= (1ULL << 34);
static const uint64 DTF_FORCE_INLINE	= (1ULL << 35);

static const uint64 DTF_FUNC_VARIABLE	= (1ULL << 36);
static const uint64 DTF_FUNC_ASSEMBLER	= (1ULL << 37);
static const uint64 DTF_FUNC_RECURSIVE  = (1ULL << 38);
static const uint64 DTF_FUNC_ANALYZING  = (1ULL << 39);

static const uint64 DTF_FUNC_CONSTEXPR	= (1ULL << 40);
static const uint64 DTF_FUNC_INTRSAVE   = (1ULL << 41);
static const uint64 DTF_FUNC_INTRCALLED = (1ULL << 42);
static const uint64 DTF_FUNC_PURE		= (1ULL << 43);

static const uint64 DTF_FPARAM_CONST	= (1ULL << 44);
static const uint64 DTF_FPARAM_NOCONST	= (1ULL << 45);
static const uint64 DTF_VAR_ADDRESS		= (1ULL << 46);

static const uint64 DTF_FUNC_THIS		= (1ULL << 47);

static const uint64 DTF_VAR_ALIASING	= (1ULL << 48);
static const uint64 DTF_FPARAM_UNUSED	= (1ULL << 49);
static const uint64 DTF_DEPRECATED		= (1ULL << 50);
static const uint64 DTF_FUNC_NO_RETURN	= (1ULL << 51);
static const uint64 DTF_PLACED			= (1ULL << 52);
static const uint64 DTF_NO_PAGE_CROSS	= (1ULL << 53);

static const uint64 DTF_ASM_PRESERVE_A	= (1ULL << 54);
static const uint64 DTF_ASM_PRESERVE_X	= (1ULL << 55);
static const uint64 DTF_ASM_PRESERVE_Y	= (1ULL << 56);

static const uint64	DTF_MEMMAP					= (1ULL << 57);
static const uint64 DTF_FPARAM_RANGE_LIMITED	= (1ULL << 58);
static const uint64	DTF_PRECOMPILED				= (1ULL << 59);
static const uint64 DTF_FUNC_DELETED			= (1ULL << 60);

class Declaration;

enum ScopeLevel
{
	SLEVEL_SCOPE,
	SLEVEL_USING,
	SLEVEL_GLOBAL,
	SLEVEL_STATIC,
	SLEVEL_NAMESPACE,
	SLEVEL_TEMPLATE,
	SLEVEL_CLASS,
	SLEVEL_FUNCTION,
	SLEVEL_LOCAL,
};

class DeclarationScope
{
public:
	DeclarationScope(DeclarationScope * parent, ScopeLevel level, const Ident * name = nullptr);
	~DeclarationScope(void);

	const Ident* Mangle(const Ident* ident) const;

	Declaration* Insert(const Ident* ident, Declaration* dec);
	Declaration* Lookup(const Ident* ident, ScopeLevel limit = SLEVEL_GLOBAL);

	void End(const Location & loc);
	void Clear(void);

	void UseScope(DeclarationScope* scope);

	template<typename F> void Iterate(F && f);

	ScopeLevel		mLevel;
	const Ident	*	mName;

	DeclarationScope* Clone(void) const;

	DeclarationScope* mParent;
protected:
	struct Entry
	{
		const Ident* mIdent;
		Declaration* mDec;
	};
	Entry	* mHash;
	int			mHashSize, mHashFill;
	ExpandingArray<DeclarationScope*>	mUsed;
};

template<typename F> void DeclarationScope::Iterate(F&& f)
{
	for (int i = 0; i < mHashSize; i++)
	{
		if (mHash[i].mIdent)
			f(mHash[i].mIdent, mHash[i].mDec);
	}
}

enum ExpressionType : uint8
{
	EX_ERROR,
	EX_VOID,
	EX_CONSTANT,
	EX_VARIABLE,
	EX_ASSIGNMENT,
	EX_INITIALIZATION,
	EX_BINARY,
	EX_RELATIONAL,
	EX_PREINCDEC,
	EX_PREFIX,
	EX_POSTFIX,
	EX_POSTINCDEC,
	EX_INDEX,
	EX_QUALIFY,
	EX_CALL,
	EX_INLINE,
	EX_VCALL,
	EX_DISPATCH,
	EX_LIST,
	EX_COMMA,
	EX_RETURN,
	EX_SEQUENCE,
	EX_WHILE,
	EX_IF,
	EX_ELSE,
	EX_FOR,
	EX_FORBODY,
	EX_DO,
	EX_SCOPE,
	EX_BREAK,
	EX_CONTINUE,
	EX_TYPE,
	EX_TYPECAST,
	EX_LOGICAL_AND,
	EX_LOGICAL_OR,
	EX_LOGICAL_NOT,
	EX_ASSEMBLER,
	EX_UNDEFINED,
	EX_SWITCH,
	EX_CASE,
	EX_DEFAULT,
	EX_CONDITIONAL,
	EX_ASSUME,
	EX_BANKOF,
	EX_CONSTRUCT,
	EX_CLEANUP,
	EX_RESULT,
	EX_PACK,
	EX_PACK_TYPE,
	EX_LABEL,
	EX_GOTO,
	EX_AGGREGATE
};

static const uint32 ANAFL_LHS = (1U << 0);
static const uint32 ANAFL_RHS = (1U << 1);
static const uint32 ANAFL_ASSIGN = (1U << 2);
static const uint32 ANAFL_ALIAS = (1U << 3);
static const uint32 ANAFL_COPY = (1U << 4);
static const uint32 ANAFL_ADDRESS = (1U << 5);


class Expression
{
public:
	Expression(const Location& loc, ExpressionType type);
	~Expression(void);


	Location				mLocation, mEndLocation;
	Expression			*	mLeft, * mRight;
	Declaration			*	mDecValue, * mDecType;

	uint32					mUID;

	Token					mToken;
	ExpressionType			mType;
	AsmInsType				mAsmInsType;
	AsmInsMode				mAsmInsMode;
	bool					mConst;
	uint8					mFlags;

	Expression* LogicInvertExpression(void);
	Expression* ConstantFold(Errors * errors, LinkerSection* dataSection, Linker * linker = nullptr);
	Expression* ConstantDereference(Errors* errors, LinkerSection* dataSection);
	bool HasSideEffects(void) const;
	Expression* ListAppend(Expression* lexp);
	Expression* ToAlternateThis(Declaration* pthis, Declaration* nthis);
	Expression* Clone(void) const;
	Expression* Clone(const Location & loc) const;
	Expression* ToVarConst(Declaration* pvar, Declaration* pconst) const;

	void ReplaceVariable(Declaration* pvar, Declaration* nvar);

	bool IsSame(const Expression* exp) const;
	bool IsRValue(void) const;
	bool IsLValue(void) const;
	bool IsConstRef(void) const;
	bool IsVolatile(void) const;

	void Dump(int ident) const;
};

class Declaration
{
public:
	Declaration(const Location & loc, DecType type);
	~Declaration(void);

	uint32				mUID;

	Location			mLocation, mEndLocation;
	DecType				mType;
	Token				mToken;
	Declaration		*	mBase, * mParams, * mParamPack, * mNext, * mPrev, * mConst, * mMutable, * mVolatile;
	Declaration		*	mDefaultConstructor, * mDestructor, * mCopyConstructor, * mCopyAssignment, * mMoveConstructor, * mMoveAssignment;
	Declaration		*	mVectorConstructor, * mVectorDestructor, * mVectorCopyConstructor, * mVectorCopyAssignment, * mVectorMoveConstructor, * mVectorMoveAssignment;
	Declaration		*	mVTable, * mClass, * mTemplate;
	Declaration		*	mForwardParam, * mForwardCall;

	Expression*			mValue, * mReturn;
	DeclarationScope*	mScope;
	int					mOffset, mSize, mVarIndex, mNumVars, mComplexity, mLocalSize, mAlignment, mFastCallBase, mFastCallSize, mFastCallBase2, mFastCallSize2, mStride, mStripe, mVariadicSize;
	uint8				mShift, mBits;
	int64				mInteger, mMinValue, mMaxValue;
	double				mNumber;
	uint64				mFlags, mCompilerOptions, mOptFlags;
	const Ident		*	mIdent, * mQualIdent, * mMangleIdent;
	LinkerSection	*	mSection;
	LinkerRegion	*	mInlayRegion;
	const uint8		*	mData;
	LinkerObject	*	mLinkerObject;
	int					mUseCount;
	TokenSequence	*	mTokens;
	Parser			*	mParser;

	int64 MinInteger(void) const;
	int64 MaxInteger(void) const;
	int64 CastInteger(int64 v) const;

	GrowingArray<Declaration*>		mCallers, mCalled, mFriends, mMayCall;
	ExpandingArray<Expression*>		mReferences;

	bool CanAssign(const Declaration* fromType) const;
	bool IsSame(const Declaration* dec) const;
	bool IsDerivedFrom(const Declaration* dec) const;
	bool IsSubType(const Declaration* dec) const;
	bool IsConstRefSame(const Declaration* dec) const;
	bool IsConstSame(const Declaration* dec) const;
	bool IsSameValue(const Declaration* dec) const;
	bool IsSameParams(const Declaration* dec) const;
	bool IsSameMutable(const Declaration* dec) const;

	bool IsTemplateSame(const Declaration* dec, const Declaration* tdec) const;
	bool IsTemplateSameParams(const Declaration* dec, const Declaration* tdec) const;
	bool IsSameTemplate(const Declaration* dec) const;

	bool IsIntegerType(void) const;
	bool IsNumericType(void) const;
	bool IsSimpleType(void) const;
	bool IsReference(void) const;
	bool IsIndexed(void) const;
	bool ContainsArray(void) const;
	bool IsShortIntStruct(void) const;
	bool IsComplexStruct(void) const;
	bool HasConstructor(void) const;
	bool IsPointerType(void) const;
	bool IsValueType(void) const;
	bool IsStructOrUnion(void) const;

	void SetDefined(void);

	Declaration* ToConstType(void);
	Declaration* ToMutableType(void);
	Declaration* ToVolatileType(void);
	Declaration* ToAlternateThis(Declaration* pthis, int nthis = 1);

	Declaration* ToStriped(int stripe);
	Declaration* ToStriped(Errors* errors);
	Declaration* Clone(void);
	Declaration* Last(void);

	Declaration* BuildPointer(const Location& loc);
	Declaration* BuildReference(const Location& loc, DecType type = DT_TYPE_REFERENCE);
	Declaration* BuildConstPointer(const Location& loc);
	Declaration* BuildConstReference(const Location& loc, DecType type = DT_TYPE_REFERENCE);
	Declaration* BuildRValueRef(const Location& loc);
	Declaration* BuildConstRValueRef(const Location& loc);
	Declaration* NonRefBase(void);
	Declaration* BuildArrayPointer(void);
	Declaration* BuildAddressOfPointer(void);
	Declaration* DeduceAuto(Declaration* dec);
	Declaration* DeduceAutoInit(Declaration* dec);
	Declaration* ConstCast(Declaration* ntype);
	bool IsNullConst(void) const;
	bool IsAuto(void) const;
	bool HasConstMember(void) const;

	DecType ValueType(void) const;

	bool CanResolveTemplate(Expression* pexp, Declaration* tdec);
	bool ResolveTemplate(Declaration* fdec, Declaration * tdec, bool same, bool preliminary);
	bool ResolveTemplate(Expression* pexp, Declaration* tdec);
	bool ResolveTemplateParameterList(Expression* pexp, Declaration* pdec, bool preliminary);

	Declaration* ExpandTemplate(DeclarationScope* scope);

	const Ident* MangleIdent(void);
	const Ident* FullIdent(void);

	int Stride(void) const;
	int Alignment(void) const;
};

void InitDeclarations(void);

extern Declaration* TheVoidTypeDeclaration, * TheConstVoidTypeDeclaration, * TheSignedIntTypeDeclaration, * TheUnsignedIntTypeDeclaration, * TheConstCharTypeDeclaration, * TheCharTypeDeclaration, * TheSignedCharTypeDeclaration, * TheUnsignedCharTypeDeclaration;
extern Declaration* TheBoolTypeDeclaration, * TheFloatTypeDeclaration, * TheVoidPointerTypeDeclaration, * TheConstVoidPointerTypeDeclaration, * TheSignedLongTypeDeclaration, * TheUnsignedLongTypeDeclaration;
extern Declaration* TheVoidFunctionTypeDeclaration, * TheConstVoidValueDeclaration;
extern Declaration* TheCharPointerTypeDeclaration, * TheConstCharPointerTypeDeclaration;
extern Declaration* TheNullptrConstDeclaration, * TheZeroIntegerConstDeclaration, * TheZeroFloatConstDeclaration, * TheNullPointerTypeDeclaration;
extern Declaration* TheTrueConstDeclaration, * TheFalseConstDeclaration;
extern Expression* TheVoidExpression;

