//
// 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.
//
// MakeLibCfa.cc --
//
// Author           : Richard C. Bilson
// Created On       : Sat May 16 10:33:33 2015
// Last Modified By : Rob Schluntz
// Last Modified On : Fri Apr 22 13:54:15 2016
// Update Count     : 40
//

#include "MakeLibCfa.h"
#include "SynTree/Visitor.h"
#include "SynTree/Declaration.h"
#include "SynTree/Type.h"
#include "SynTree/Expression.h"
#include "SynTree/Statement.h"
#include "SynTree/Initializer.h"
#include "CodeGen/OperatorTable.h"
#include "Common/UniqueName.h"

namespace LibCfa {
	class MakeLibCfa : public Visitor {
	  public:
		void visit( FunctionDecl* funcDecl );
		void visit( ObjectDecl* objDecl );

		std::list< Declaration* > &get_newDecls() { return newDecls; }
	  private:
		std::list< Declaration* > newDecls;
	};

	void makeLibCfa( std::list< Declaration* > &prelude ) {
		MakeLibCfa maker;
		acceptAll( prelude, maker );
		prelude.splice( prelude.end(), maker.get_newDecls() );
	}

	void MakeLibCfa::visit( FunctionDecl* origFuncDecl ) {
		if ( origFuncDecl->get_linkage() != LinkageSpec::Intrinsic ) return;
		if ( origFuncDecl->get_statements() ) return;

		FunctionDecl *funcDecl = origFuncDecl->clone();
		CodeGen::OperatorInfo opInfo;
		bool lookResult = CodeGen::operatorLookup( funcDecl->get_name(), opInfo );
		assert( lookResult );
		assert( ! funcDecl->get_statements() );
		UntypedExpr *newExpr = new UntypedExpr( new NameExpr( funcDecl->get_name() ) );
		UniqueName paramNamer( "_p" );
		std::list< DeclarationWithType* >::iterator param = funcDecl->get_functionType()->get_parameters().begin();
		assert( param != funcDecl->get_functionType()->get_parameters().end() );

		for ( ; param != funcDecl->get_functionType()->get_parameters().end(); ++param ) {
			if ( (*param)->get_name() == "" ) {
				(*param)->set_name( paramNamer.newName() );
				(*param)->set_linkage( LinkageSpec::C );
			}
			newExpr->get_args().push_back( new VariableExpr( *param ) );
		} // for

		funcDecl->set_statements( new CompoundStmt( std::list< Label >() ) );
		newDecls.push_back( funcDecl );

		switch ( opInfo.type ) {
		  case CodeGen::OT_INDEX:
		  case CodeGen::OT_CALL:
		  case CodeGen::OT_PREFIX:
		  case CodeGen::OT_POSTFIX:
		  case CodeGen::OT_INFIX:
		  case CodeGen::OT_PREFIXASSIGN:
		  case CodeGen::OT_POSTFIXASSIGN:
		  case CodeGen::OT_INFIXASSIGN:
				funcDecl->get_statements()->get_kids().push_back( new ReturnStmt( std::list< Label >(), newExpr ) );
				break;
		  case CodeGen::OT_CTOR:
		  	// ctors don't return a value
		  	if ( funcDecl->get_functionType()->get_parameters().size() == 1 ) {
		  		// intrinsic default constructors should do nothing
		  		// delete newExpr;
		  		break;
		  	} else {
		  		assert( funcDecl->get_functionType()->get_parameters().size() == 2 );
		  		// anything else is a single parameter constructor that is effectively a C-style assignment
		  		// delete newExpr->get_function();
		  		assert(newExpr->get_args().size()==2);
		  		newExpr->set_function( new NameExpr( "?=?" ) );
			  	funcDecl->get_statements()->get_kids().push_back( new ExprStmt( std::list< Label >(), newExpr ) );
		  	}
		  	break;
		  case CodeGen::OT_DTOR:
		  	// intrinsic destructors should do nothing
		  	// delete newExpr;
		  	break;
		  case CodeGen::OT_CONSTANT:
		  case CodeGen::OT_LABELADDRESS:
			// there are no intrinsic definitions of 0/1 or label addresses as functions
			assert( false );
		} // switch
	}

	void MakeLibCfa::visit( ObjectDecl* origObjDecl ) {
		if ( origObjDecl->get_linkage() != LinkageSpec::Intrinsic ) return;

		ObjectDecl *objDecl = origObjDecl->clone();
		assert( ! objDecl->get_init() );
		std::list< Expression* > noDesignators;
		objDecl->set_init( new SingleInit( new NameExpr( objDecl->get_name() ), noDesignators, false ) ); // cannot be constructed
		newDecls.push_back( objDecl );
	}
} // namespace LibCfa
