/*
 * ======================================================
 * This function appends all additional subroutines
 * called inside the user subroutine. It is specialised
 * for CUDA in the related subclass
 * ======================================================
 */
void FortranCUDAUserSubroutine::appendAdditionalSubroutines ( SgScopeStatement * moduleScope,
  FortranParallelLoop * parallelLoop, FortranProgramDeclarationsAndDefinitions * declarations,
  FortranConstantDeclarations * CUDAconstants, std::vector < SgProcedureHeaderStatement * > * allCalledRoutines)
{
  using std::vector;
  using boost::iequals;
  /*
   * ======================================================
   * First removes duplicates in calledRoutines itself
   * ======================================================
   */
  sort ( calledRoutines.begin(), calledRoutines.end() );
  calledRoutines.erase ( unique ( calledRoutines.begin(), calledRoutines.end() ), calledRoutines.end() );

  Debug::getInstance ()->debugMessage ("Before removing, the list of routine calls found in the user kernels is: ",
   Debug::FUNCTION_LEVEL, __FILE__, __LINE__);
  vector < SgProcedureHeaderStatement * > :: iterator routinesIt2;
  for ( routinesIt2 = calledRoutines.begin (); routinesIt2 != calledRoutines.end (); routinesIt2++ )
  {
    string appendingSubroutine = (*routinesIt2)->get_name ().getString ();
    Debug::getInstance ()->debugMessage (appendingSubroutine,
      Debug::FUNCTION_LEVEL, __FILE__, __LINE__);
  }
  
  /*
   * ======================================================
   * The removes routines already appended by other user
   * kernels, using the list in allCalledRoutines
   * ======================================================
   */
  Debug::getInstance ()->debugMessage ("Removing global duplicates, the number of routines in the list is: '"
    + boost::lexical_cast<string> ((int) calledRoutines.size()) + "'", Debug::FUNCTION_LEVEL, __FILE__, __LINE__);

  vector < SgProcedureHeaderStatement * > :: iterator routinesIt;
  for ( routinesIt = calledRoutines.begin (); routinesIt != calledRoutines.end (); ) //routinesIt++ )
  {
    string appendingSubroutine = (*routinesIt)->get_name ().getString ();

    Debug::getInstance ()->debugMessage ("Checking routine for deletion: '"
      + appendingSubroutine + "'", Debug::FUNCTION_LEVEL, __FILE__, __LINE__);

    bool foundAndErased = false;
    vector < SgProcedureHeaderStatement * > :: iterator finder;
    for ( finder = allCalledRoutines->begin (); finder != allCalledRoutines->end (); finder++ )
    {
      Debug::getInstance ()->debugMessage ("Checking against: '"
        + (*finder)->get_name ().getString () + "'", Debug::FUNCTION_LEVEL, __FILE__, __LINE__);

      if ( iequals ((*finder)->get_name ().getString (), appendingSubroutine) )
      {
        /*
         * ======================================================
         * Routine already appended by another user kernel:
         * delete it from list of routines to be appended for
         * this user kernel, and exit this loop
         * ======================================================
         */      
        Debug::getInstance ()->debugMessage ("Deleting: '"
          + appendingSubroutine + "'", Debug::FUNCTION_LEVEL, __FILE__, __LINE__);
        
        calledRoutines.erase (routinesIt++);

        if ( calledRoutines.empty () ) return;

        foundAndErased = true;

        routinesIt--;
        break;
      }      
    }

    if ( foundAndErased == false )
    {
        /*
         * ======================================================
         * New routine: it must be added to the list of 
         * routines called by all previous user kernels because
         * recursively called routines need to discard those
         * already appended by this routine
         * ======================================================
         */
         Debug::getInstance ()->debugMessage ("Not found, appending: '"
          + appendingSubroutine + "'", Debug::FUNCTION_LEVEL, __FILE__, __LINE__);
        
        allCalledRoutines->push_back ( *routinesIt );
        
        routinesIt++;
    }
  }
  
  
  vector < SgProcedureHeaderStatement * > :: iterator it;
  for ( it = calledRoutines.begin(); it != calledRoutines.end(); it++ )
  {
   
    string calledSubroutineName = (*it)->get_name ().getString ();
    
    Debug::getInstance ()->debugMessage ("Appending new subroutine '"
        + calledSubroutineName + "'", Debug::FUNCTION_LEVEL, __FILE__, __LINE__);
   
    FortranCUDAUserSubroutine * newRoutine = new FortranCUDAUserSubroutine ( moduleScope, 
        parallelLoop, declarations, calledSubroutineName );

    newRoutine->createFormalParameterDeclarations ();
    newRoutine->createStatements ();
   
    additionalSubroutines.push_back (newRoutine);        
  }
  
  vector < FortranUserSubroutine * > :: iterator itRecursive;
  for ( itRecursive = additionalSubroutines.begin(); itRecursive != additionalSubroutines.end(); itRecursive++ )
  {
    FortranCUDAUserSubroutine * cudaSubroutineCasting = (FortranCUDAUserSubroutine *) *itRecursive;

    CUDAconstants->patchReferencesToConstants (
      (cudaSubroutineCasting )->getSubroutineHeaderStatement ());
          
    RoseHelper::forceOutputOfCodeToFile (
      (cudaSubroutineCasting )->getSubroutineHeaderStatement ());
  }

  for ( itRecursive = additionalSubroutines.begin(); itRecursive != additionalSubroutines.end(); itRecursive++ )
  {
    FortranCUDAUserSubroutine * cudaSubroutineCasting = (FortranCUDAUserSubroutine *) *itRecursive;

    cudaSubroutineCasting->appendAdditionalSubroutines (moduleScope, parallelLoop, declarations, CUDAconstants, allCalledRoutines);
  }
}