//-----------------------------------------------------------
// Copyright Christian Arnault LAL-Orsay CNRS
// arnault@lal.in2p3.fr
// See the complete license in cmt_license.txt "http://www.cecill.info". 
//-----------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "cmt_include.h"
#include "cmt_use.h"
#include "cmt_symbol.h"
#include "cmt_log.h"

/*----------------------------------------------------------*/
/*                                                          */
/*  Operations on Include                                   */
/*                                                          */
/*----------------------------------------------------------*/

/**
   Executed to parse an include_dirs statement
   It postpones the macro expansion to the post-processing stage
   (during set_standard_macros) 
 */
void Include::action (const CmtSystem::cmt_string_vector& words, Use* use)
{
  cmt_string name;

  if (use == 0) use = &(Use::current());

  if (Cmt::get_current_access () == UserMode)
    {
      if (use->get_current_scope () == ScopePrivate) return;
    }

  for (int i = 1; i < words.size (); i++)
    {
      /*
	statement words may contain macro references at that level
	they are stored as they are.
	They will be expanded later on using the parse_all method

	(called from set_standard_macros)

       */
      name = words[i];
      if (name == "") return;

      add (name, use);
    }
}

/**
   Search a database entry for this include_dirs matching the
   duet {name, use}
 */
Include* Include::find (const cmt_string& name, Use* use)
{
  int include_index;

  if (use == 0) use = &(Use::current());

  if (use->includes.size () == 0) return (0);

  for (include_index = 0;
       include_index < use->includes.size ();
       include_index++)
    {
      Include& incl = use->includes[include_index];

      if (incl.use != use) continue;

      if (incl.name == name)
        {
          return (&incl);
        }
    }

  return (0);
}

/**
   Add a unique entry for this include_dirs specification given by the duet {name, use}
 */
Include* Include::add (const cmt_string& name, Use* use)
{
  if (name == "") return (0);

  if (use == 0) use = &(Use::current());

  {
    Include* incl;

    incl = find (name, use);
    if (incl != 0) return (incl);
  }

  Include& incl = use->includes.add ();

  incl.name = name;
  incl.use = use;

  return (&incl);
}

/*----------------------------------------------------------*/
void Include::clear_all ()
{
  for (int include_number = 0;
       include_number < (Use::current()).includes.size ();
       include_number++)
    {
      Include& incl = (Use::current()).includes[include_number];

      incl.clear ();
    }
}

/**
   Post processing of the include_dirs statements of a Use object.
   This is meant to expand all macro references used in the
   include_dirs statements

   Note that this may create new Include objects. Thus the loop is
   only performed onto the existing objects before the post-processing step
 */
void Include::parse_all (Use* use)
{
  int size;
  int i;

  size = use->includes.size ();
  for (i = 0; i < size; i++)
    {
      Include& incl = use->includes[i];

      incl.parse ();
    }
}

/*----------------------------------------------------------*/
void Include::clear ()
{
  use = 0;
}

/**
   Post processing of an include_dirs statement.

   The name field is expanded for its macro references
   and split into individual words.
   Each word in turn generates a new Include object
   The old Include object is inactivated by setting an empty name
 */
void Include::parse ()
{
  cmt_string new_name = name;

  Symbol::expand (new_name);

  if (new_name == name) return;

  CmtSystem::cmt_string_vector ws;

  ws.clear ();

  CmtSystem::split (new_name, " ", ws);

  name = "";

  for (int j = 0; j < ws.size (); j++)
    {
      const cmt_string& w = ws[j];

      add (w, use);
    }

  use = 0;
}

/*----------------------------------------------------------*/
Include::Include ()
{
  clear ();
}

/*----------------------------------------------------------*/
Include::~Include ()
{
  use = 0;
}

