//
// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// Indexer.h --
//
// Author           : Richard C. Bilson
// Created On       : Sun May 17 21:38:55 2015
// Last Modified By : Peter A. Buhr
// Last Modified On : Thu Aug 17 16:09:12 2017
// Update Count     : 8
//

#pragma once

#include <iosfwd>             // for ostream
#include <list>               // for list
#include <string>             // for string
#include <functional>         // for function

#include "SynTree/Visitor.h"  // for Visitor
#include "SynTree/SynTree.h"  // for AST nodes

namespace ResolvExpr {
class Cost;
}

namespace SymTab {
	class Indexer {
	  public:
		explicit Indexer();

		Indexer( const Indexer &that );
		Indexer( Indexer &&that );
		virtual ~Indexer();
		Indexer& operator= ( const Indexer &that );
		Indexer& operator= ( Indexer &&that );

		// when using an indexer manually (e.g., within a mutator traversal), it is necessary to tell the indexer
		// explicitly when scopes begin and end
		void enterScope();
		void leaveScope();

		struct IdData {
			DeclarationWithType * id = nullptr;
			Expression * baseExpr = nullptr; // WithExpr

			/// non-null if this declaration is deleted
			BaseSyntaxNode * deleteStmt = nullptr;

			// NOTE: shouldn't need either of these constructors, but gcc-4 does not properly support initializer lists with default members.
			IdData() = default;
			IdData( DeclarationWithType * id, Expression * baseExpr, BaseSyntaxNode * deleteStmt ) : id( id ), baseExpr( baseExpr ), deleteStmt( deleteStmt ) {}

			Expression * combine( ResolvExpr::Cost & cost ) const;
		};

		/// Gets all declarations with the given ID
		void lookupId( const std::string &id, std::list< IdData > &out ) const;
		/// Gets the top-most type declaration with the given ID
		NamedTypeDecl *lookupType( const std::string &id ) const;
		/// Gets the top-most struct declaration with the given ID
		StructDecl *lookupStruct( const std::string &id ) const;
		/// Gets the top-most enum declaration with the given ID
		EnumDecl *lookupEnum( const std::string &id ) const;
		/// Gets the top-most union declaration with the given ID
		UnionDecl *lookupUnion( const std::string &id ) const;
		/// Gets the top-most trait declaration with the given ID
		TraitDecl *lookupTrait( const std::string &id ) const;

		void print( std::ostream &os, int indent = 0 ) const;

		/// looks up a specific mangled ID at the given scope
		IdData * lookupIdAtScope( const std::string &id, const std::string &mangleName, unsigned long scope );
		const IdData * lookupIdAtScope( const std::string &id, const std::string &mangleName, unsigned long scope ) const;
		/// returns true if there exists a declaration with C linkage and the given name with a different mangled name
		bool hasIncompatibleCDecl( const std::string &id, const std::string &mangleName, unsigned long scope ) const;
		/// returns true if there exists a declaration with C linkage and the given name with the same mangled name
		bool hasCompatibleCDecl( const std::string &id, const std::string &mangleName, unsigned long scope ) const;
		// equivalents to lookup functions that only look at tables at scope `scope` (which should be >= tables->scope)
		NamedTypeDecl *lookupTypeAtScope( const std::string &id, unsigned long scope ) const;
		StructDecl *lookupStructAtScope( const std::string &id, unsigned long scope ) const;
		EnumDecl *lookupEnumAtScope( const std::string &id, unsigned long scope ) const;
		UnionDecl *lookupUnionAtScope( const std::string &id, unsigned long scope ) const;
		TraitDecl *lookupTraitAtScope( const std::string &id, unsigned long scope ) const;

		typedef std::function<bool(IdData &, const std::string &)> ConflictFunction;

		void addId( DeclarationWithType * decl, Expression * baseExpr = nullptr );
		void addDeletedId( DeclarationWithType * decl, BaseSyntaxNode * deleteStmt );

		void addType( NamedTypeDecl *decl );
		void addStruct( const std::string &id );
		void addStruct( StructDecl *decl );
		void addEnum( EnumDecl *decl );
		void addUnion( const std::string &id );
		void addUnion( UnionDecl *decl );
		void addTrait( TraitDecl *decl );

		/// adds all of the IDs from WithStmt exprs
		void addWith( std::list< Expression * > & withExprs, BaseSyntaxNode * withStmt );

		/// adds all of the members of the Aggregate (addWith helper)
		void addMembers( AggregateDecl * aggr, Expression * expr, ConflictFunction );

		/// convenience function for adding a list of Ids to the indexer
		void addIds( const std::list< DeclarationWithType * > & decls );

		/// convenience function for adding a list of forall parameters to the indexer
		void addTypes( const std::list< TypeDecl * > & tds );

		/// convenience function for adding all of the declarations in a function type to the indexer
		void addFunctionType( FunctionType * ftype );

		bool doDebug = false; ///< Display debugging trace?
	  private:
		struct Impl;

		Impl *tables;         ///< Copy-on-write instance of table data structure
		unsigned long scope;  ///< Scope index of this pointer

		/// Takes a new ref to a table (returns null if null)
		static Impl *newRef( Impl *toClone );
		/// Clears a ref to a table (does nothing if null)
		static void deleteRef( Impl *toFree );

		// Removes matching autogenerated constructors and destructors
		// so that they will not be selected
		// void removeSpecialOverrides( FunctionDecl *decl );
		void removeSpecialOverrides( const std::string &id, std::list< IdData > & out ) const;

		/// Ensures that tables variable is writable (i.e. allocated, uniquely owned by this Indexer, and at the current scope)
		void makeWritable();

		/// common code for addId, addDeletedId, etc.
		void addId( DeclarationWithType * decl, ConflictFunction, Expression * baseExpr = nullptr, BaseSyntaxNode * deleteStmt = nullptr );
	};
} // namespace SymTab

// Local Variables: //
// tab-width: 4 //
// mode: c++ //
// compile-command: "make install" //
// End: //
