//
// 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.
//
// FixNames.cc --
//
// Author           : Richard C. Bilson
// Created On       : Mon May 18 07:44:20 2015
// Last Modified By : Peter A. Buhr
// Last Modified On : Thu Mar 16 07:50:30 2017
// Update Count     : 16
//

#include <memory>

#include "FixNames.h"
#include "SynTree/Declaration.h"
#include "SynTree/Expression.h"
#include "SynTree/Visitor.h"
#include "SymTab/Mangler.h"
#include "OperatorTable.h"
#include "FixMain.h"

namespace CodeGen {
	class FixNames : public Visitor {
	  public:
		virtual void visit( ObjectDecl *objectDecl );
		virtual void visit( FunctionDecl *functionDecl );

		virtual void visit( CompoundStmt *compoundStmt );
	  private:
		int scopeLevel = 1;

		void fixDWT( DeclarationWithType *dwt );
	};

	std::string mangle_main() {
		FunctionType* main_type;
		std::unique_ptr<FunctionDecl> mainDecl { new FunctionDecl( "main", Type::StorageClasses(), LinkageSpec::Cforall,
																   main_type = new FunctionType( Type::Qualifiers(), true ), nullptr )
				};
		main_type->get_returnVals().push_back( 
			new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr )
		);

		auto && name = SymTab::Mangler::mangle( mainDecl.get() );
		// std::cerr << name << std::endl;
		return name;
	}
	std::string mangle_main_args() {
		FunctionType* main_type;
		std::unique_ptr<FunctionDecl> mainDecl { new FunctionDecl( "main", Type::StorageClasses(), LinkageSpec::Cforall, 
																   main_type = new FunctionType( Type::Qualifiers(), false ), nullptr )
				};
		main_type->get_returnVals().push_back( 
			new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr )
		);

		mainDecl->get_functionType()->get_parameters().push_back( 
			new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr )
		);

		mainDecl->get_functionType()->get_parameters().push_back( 
			new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0, 
			new PointerType( Type::Qualifiers(), new PointerType( Type::Qualifiers(), new BasicType( Type::Qualifiers(), BasicType::Char ) ) ), 
			nullptr )
		);

		auto&& name = SymTab::Mangler::mangle( mainDecl.get() );
		// std::cerr << name << std::endl;
		return name;
	}

	bool is_main(const std::string& name) {
		static std::string mains[] = { 
			mangle_main(), 
			mangle_main_args()
		};

		for(const auto& m : mains) {
			if( name == m ) return true;
		}
		return false;
	}

	void fixNames( std::list< Declaration* > translationUnit ) {
		FixNames fixer;
		acceptAll( translationUnit, fixer );
	}

	void FixNames::fixDWT( DeclarationWithType *dwt ) {
		if ( dwt->get_name() != "" ) {
			if ( LinkageSpec::isDecoratable( dwt->get_linkage() ) ) {
				dwt->set_mangleName( SymTab::Mangler::mangle( dwt ) );
				dwt->set_scopeLevel( scopeLevel );
			} // if
		} // if
	}

	void FixNames::visit( ObjectDecl *objectDecl ) {
		Visitor::visit( objectDecl );
		fixDWT( objectDecl );
	}

	void FixNames::visit( FunctionDecl *functionDecl ) {
		Visitor::visit( functionDecl );
		fixDWT( functionDecl );

		if(is_main( SymTab::Mangler::mangle(functionDecl, true, true) )) {
			int nargs = functionDecl->get_functionType()->get_parameters().size();
			if( !(nargs == 0 || nargs == 2 || nargs == 3) ) {
				throw SemanticError("Main expected to have 0, 2 or 3 arguments\n", functionDecl); 
			}
			functionDecl->get_statements()->get_kids().push_back( new ReturnStmt( noLabels, new ConstantExpr( Constant( new BasicType( Type::Qualifiers(), BasicType::SignedInt ), "0") ) ) );
			CodeGen::FixMain::registerMain( functionDecl );
		}
	}

	void FixNames::visit( CompoundStmt *compoundStmt ) {
		scopeLevel++;
		Visitor::visit( compoundStmt );
		scopeLevel--;
	}
} // namespace CodeGen

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