//-----------------------------------------------------------
// Copyright Christian Arnault LAL-Orsay CNRS
// arnault@lal.in2p3.fr
// Modified by garonne@lal.in2p3.fr
// Modified by Grigory Rybkin
// 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_project.h"
#include "cmt_database.h"
#include "cmt_system.h"
#include "cmt_awk.h"
#include "cmt_syntax.h"
#include "cmt_tag.h"
#include "cmt_error.h"
#include "cmt_log.h"

class ProjectReader : public Awk
{
public:

  ProjectReader ()
  {
  }
  
  const cmt_string& get_project_name () const
  {
    return (m_project);
  }
  
  void filter (const cmt_string& line)
  {
    if (0 == line.size () ||
	line[0] == '#') return;
    CmtSystem::cmt_string_vector words;
    CmtSystem::split (line, " \t", words);
    if (words[0] == "project")
      {
	m_project = words[1];
      }
  }
  
private:
  cmt_string m_project;
};

class ProjectPatcher : public Awk
{
public:

  ProjectPatcher (const cmt_string& p) : m_project (p)
  {
  }

  void commit ()
  {
    m_output.write (Project::get_project_file_name ());
  }

  void filter (const cmt_string& line)
  {
    CmtSystem::cmt_string_vector words;
    CmtSystem::split (line, " \t", words);
    if (words[0] == "project")
      {
	m_output += "project ";
	m_output += m_project;
      }
    else
      {
	m_output += line;
      }

    m_output += "\n";
  }
  
private:
  cmt_string m_output;
  const cmt_string& m_project;
};

IProjectFactory& ProjectFactory::instance ()
{
  static ProjectFactory me;
  
  return (me);
}

void ProjectFactory::reset ()
{
  Project::clear_all ();
}

static bool get_release_from_path (const CmtSystem::cmt_string_vector& items, 
				   const cmt_string& path, 
				   const cmt_string& name, 
				   cmt_string& real_name, 
				   cmt_string& release)
{
  bool result = false;

  release = "";
  real_name = "";

  cmt_string p = path;

  if ((items.size () == 0) && (name == ""))
    {
      // There is no CMTPROJECTPATH and no expected project name.
      // We have no way to find a project structure
      // So we only expect a 2-level structure.

      CmtSystem::basename (p, release);
      CmtSystem::dirname (p, p);
      CmtSystem::basename (p, real_name);
      
      return (false);
    }

  for (;;)
    {
      if (p == "")
	{
	  // Protection: we found no matching project name
	  // and path was outside any CMTPROJECTPATH

	  p = path;

	  CmtSystem::basename (p, release);
	  CmtSystem::dirname (p, p);
	  CmtSystem::basename (p, real_name);

	  return (false);
	}

      cmt_string n;

      CmtSystem::basename (p, n);
      CmtSystem::dirname (p, p);

      if (n == name)
	{
	  real_name = name;
	  result = true;
	  break;
	}

      CmtSystem::basename (p, real_name);

      for (int i = 0; i < items.size (); i++)
	{
	  const cmt_string& item = items[i];
	  if (p == item)
	    {
	      // We have reached an item of CMTPROJECTPATH, no need to continue
	      return (false);
	    }
	}

      if (release == "")
	{
	  release = n;
	}
      else
	{
	  cmt_string r;

	  r = n;
	  r += CmtSystem::file_separator ();
	  r += release;
	  release = r;
	}
    }

  //cerr << "$CMT> GRFP> path=" << path << " name=" << name << " rel=" << release << endl;

  return (result);
}
 

/*
  Every new CMTPATH entry becomes associated with a dedicated PROJECT
  This function will understand this new entry to CMTPATH and understand it:
  - it can correspond to an existing project (ie already declared)
  - it's a new project
  - then it tries to read and parse its project file 
*/
Project* ProjectFactory::create_project (const cmt_string& specified_name,
					 const cmt_string& path,
					 const cmt_string& source,
					 Project* parent)
{
  cmt_string compressed_path = path;
  CmtSystem::compress_path (compressed_path);
  bool specify_name = (specified_name != "");

  if (Cmt::get_debug ())
    {
      cout << "Creating project " << specified_name << " in " << path << " from " << source << " with parent " << ((parent==0)? "0" : parent->get_name ()) << endl;
      Project::show_all ();
    }

  cmt_string sep;
  sep = CmtSystem::path_separator ();

  cmt_string cmtprojectpath = Symbol::get_env_value ("CMTPROJECTPATH"); 
  CmtSystem::cmt_string_vector items;
  CmtSystem::split (cmtprojectpath, sep, items);

  cmt_string here = CmtSystem::pwd ();
  if (!CmtSystem::cd (compressed_path)) return (0);
  cmt_string pwd = CmtSystem::pwd ();

  static Project::ProjectVector& Projects = Project::projects ();

  int i;
  
  for (i = 0; i < Projects.size (); i++)
    {
      Project& p = Projects[i];
      
      if ((p.get_cmtpath () == compressed_path) ||
	  (p.get_cmtpath_pwd () == pwd) ||
	  (specify_name && (p.get_name () == specified_name)))
	{
	  cmt_string r;
	  cmt_string n;

	  get_release_from_path (items, compressed_path, p.get_name (), n, r);

	  if (r != p.get_release ())
	    {
	      CmtMessage::warning ("Project " + p.get_name ()
				   + " requested with conflicting releases "
				   + p.get_release () + " and " + r
				   + ", latter (in " + compressed_path + ") ignored");
	      /*
	      CmtMessage::error ("Project " + p.get_name ()
				   + " requested with conflicting releases "
				   + p.get_release () + " and " + r);
	      */
	      /*
	      if (!Cmt::get_quiet ())
		{
		  cerr << "#CMT> Project " << p.get_name ()
		       << " requested with conflicting releases " << p.get_release () << " and " << r << endl;
		}
	      */
	      //CmtError::set (CmtError::project_release_conflict, p.get_name ());
	    }
  
	  bool updated (false);
	  if (parent != 0)
	    {
	      if (!p.has_parent (parent) || !parent->has_child (&p))
		{
	      p.add_parent (parent);
	      parent->add_child (&p);

	      // Since p is a new parent, we should propagate the settings UP.

	      parent->update_strategies_from_children ();
	      updated = true;
		}

	    }

	  CmtSystem::cd (here);
	  if (updated) Project::order_all ();
	  return (&p);
	}
    }


  Project* project = 0;
  Project* cmt = 0;
  
  bool is_current = false;
  
  cmt_string name = specified_name;
  cmt_string project_name;
  cmt_string release;

  //
  // Figure out if this is the current project
  //
  //  if (here.find (pwd) == 0) is_current = true;

  // In case there are symlinks

  //  cmt_string here_real, pwd_real;
  //cerr << "realpath_: create_project" << endl;
  /*
  if (CmtSystem::realpath_ (here, here_real) && CmtSystem::realpath_ (pwd, pwd_real))
    {
      if (here_real.find (pwd_real) == 0) is_current = true;
    }
  */
  cmt_string pwd_real;
  if (!CmtSystem::realpath_ (pwd, pwd_real))
    {
      CmtError::set (CmtError::file_access_error, "Cannot compute real path `" +
      		     pwd + "'");
      CmtError::print ();
      return 0;
      //      if (Cmt::get_current_dir_real ().find (pwd_real) == 0) is_current = true;
    }

  cmt_string text;

  /*
    Now Figure out the project name from the project file
    or does not specify the project name
  */
  bool has_project_file = false;

  if (CmtSystem::cd ("cmt") && CmtSystem::test_file (Project::get_project_file_name ()))
    {
      has_project_file = true;
      text.read (Project::get_project_file_name ());

      ProjectReader reader;

      reader.run (text);

      project_name = reader.get_project_name ();
    }

  enum
    {
      without_project_file   = 0x01,
      with_project_file      = 0x02,
      without_project_name   = 0x04,
      with_project_name      = 0x08,
      without_specified_name = 0x10,
      with_specified_name    = 0x20,
      names_mismatch         = 0x40,
      names_match            = 0x80
    };

  int status = ((has_project_file) ? with_project_file : without_project_file) |
    ((has_project_file && (project_name != "")) ? with_project_name : without_project_name) |
    ((specify_name) ? with_specified_name : without_specified_name) |
    ((specify_name && has_project_file && (project_name == specified_name)) ? names_match : names_mismatch);

  if (source == "default path")
    {
      name = "CMT";
    }
  else
    {
      cmt_string n;
	  
      switch (status)
	{
	case with_project_file | without_project_name | without_specified_name | names_mismatch:

	  // The project is neither specified from the caller nor from the project file

	  /*
	    if (!Cmt::get_quiet ())
	    {
	    cerr << "#CMT> Warning: project name unspecified in project file." << endl;
	    }
	  */
      
	  get_release_from_path (items, compressed_path, "", name, release);

	  break;
	  
	case with_project_file | without_project_name | with_specified_name | names_mismatch:
	  
	  // The name is only specified from the caller
	  // find this specified name in the path
	  
	  if (get_release_from_path (items, compressed_path, specified_name, name, release))
	    {
	      // The specified name has been found from the path.
	      // We believe in the release spec.
	    }
	  else
	    {
	      // The specified name is not in the path.
	      /*
		if (!Cmt::get_quiet ())
		{
		cerr << "#CMT> Warning: specified project name "
		<< specified_name
		<< " from the caller does not match path." << endl;
		}
	      */
	      name = specified_name;
	    }
	  
	  break;
	  
	case with_project_file | with_project_name | with_specified_name | names_match:
	  
	  // We have a double specification: from the caller and from the project file.
	  // And both specifications are consistent.  
	  
	  if (get_release_from_path (items, compressed_path, specified_name, name, release))
	    {
	      // The specified name has been found from the path.
	      // We believe in the release spec.
	    }
	  else
	    {
	      // The specified name is not in the path.
	      /*
		if (!Cmt::get_quiet ())
		{
		cerr << "#CMT> Warning: specified project name "
		<< specified_name
		<< " from project file and from caller does not match path." << endl;
		}
	      */
	      name = specified_name;
	    }
	  
	  break;
	  
	case with_project_file | with_project_name | with_specified_name | names_mismatch:
	  
	  // We have a double specification: from the caller and from the project file.
	  // Specifications are inconsistent!!
	  
          /*
	    if (!Cmt::get_quiet ())
	    {
	    cerr << "#CMT> Warning: specified project name "
	    << specified_name
	    << " inconsistent with name "
	    << project_name
	    << " from project file." << endl;
	    }
          */
	  
	  if (get_release_from_path (items, compressed_path, specified_name, n, release))
	    {
	      // name from caller wins.
	    }
	  else if (get_release_from_path (items, compressed_path, project_name, name, release))
	    {
	      // name from project file wins.
	    }
	  else
	    {
	      // The specified name is not in the path.
	      
	      CmtMessage::warning ("none of specified project names "
				   + specified_name + " from graph and "
				   + project_name + " from project file match path.");
	      /*
	      if (!Cmt::get_quiet ())
		{
		  cerr << "#CMT> Warning: none of specified project names "
		       << specified_name
		       << " from graph and "
		       << project_name
		       << " from project file match path." << endl;
		}
	      */

	      name = specified_name;
	    }
	  
	  break;
	  
	case with_project_file | with_project_name | without_specified_name | names_mismatch:
	  
	  // Project name is specified in the project file but not from the caller.
	  
	  if (get_release_from_path (items, compressed_path, project_name, name, release))
	    {
	      // The specified name has been found from the path.
	      // We believe in the release spec.

	    }
	  else
	    {
	      // The specified name is not in the path.

	      /*
		if (!Cmt::get_quiet ())
		{
		cerr << "#CMT> Warning: specified project name "
		<< project_name
		<< " from project file does not match path." << endl;
		}
	      */

	      name = project_name;
	    }
	  
	  break;
	  
	case without_project_file | without_project_name | without_specified_name | names_mismatch:
	  
	  // The project is not specified from the caller and there is no project file
	  // This corresponds to the backward compatibility
	  // For the moment, assume /name/release/ structure where release is one level only
	  
	  /*
	    if (!Cmt::get_quiet ())
	    {
	    cerr << "#CMT> Warning: project name is not specified "
	    << " (no project file)." << endl;
	    }
	  */
	  
	  CmtSystem::basename (compressed_path, release);
	  CmtSystem::dirname (compressed_path, name);
	  CmtSystem::basename (name, name);

          if (name == "")
            {
              name = release;
              release = "";
            }
	  
	  break;
	  
	case without_project_file | without_project_name | with_specified_name | names_mismatch:
	  
	  // The name is only specified from the caller
	  // find this specified name in the path
	  
	  if (get_release_from_path (items, compressed_path, specified_name, name, release))
	    {
	      // The specified name has been found from the path.
	      // We believe in the release spec.
	    }
	  else
	    {
	      // The specified name is not in the path.
	      /*
		if (!Cmt::get_quiet ())
		{
		cerr << "#CMT> Warning: specified project name "
		<< specified_name
		<< " from project graph does not match path." << endl;
		}
	      */
	      name = specified_name;
	    }
	  
	  break;
	}
    }

  if (name == "")
    {
      name = "Project";
    }

  project = Project::add (name, release);
      
  if (parent != 0)
    {
      project->add_parent (parent);
      parent->add_child (project);

      // Since project is a new child, we should propagate the settings UP.

      parent->update_strategies_from_children ();
    }
  /*
  else if ((name != "CMTUSERCONTEXT") && (name != "CMTHOME"))
    //  else
    {
      // this project has no parent thus it should become the top project.
      // Let's look for all projects without parent.
      // they will become children of this project.

      for (i = 0; i < Projects.size (); i++)
	{
	  Project* p = &(Projects[i]);
	  const cmt_string& n = p->get_name ();
	  if (n == name) continue;
	  //	  if (p->get_name () == name) continue;
	  if ((n == "CMTUSERCONTEXT") || (n == "CMTHOME")) continue;
	  if (!p->has_parents ())
	    {
	      project->add_child (p);
	      p->add_parent (project);
	    }
	}

      // Since project is a new parent, we should upgrade its settings

      project->update_strategies_from_children ();
    }
  */
  if (source == "default path")
    {
      cmt = project;
      is_current = false;
    }

  project->set_cmtpath (compressed_path);
  project->set_cmtpath_real (pwd_real);
  project->set_cmtpath_pwd (pwd);
  project->set_cmtpath_source (source);

  //  project->set_is_current (is_current);
  /*
  if (is_current)
    {
      //      cerr << "current: " << project->get_name () << endl;
      //
      // The current project defines a tag with its name
      //

      Tag* tag;
      
      tag = Tag::add (project->get_name (), PriorityConfig, "PROJECT", 0);
      tag->mark ();
    }
  */

  //  Project::order_all ();

  if (text != "")
    {
      // Last step is to parse the project file

      if (Cmt::get_debug ())
	{
	  cout << "About to parse project file [" << text << "]" << endl;
	}

      // First create the Project.m_use for the policy

      Use* project_use = Use::create (project->get_cmtpath(), 
                                      "package_policy_for_project_" + project->get_name(),                                       
                                      project->get_release (), "", "");                                                         

      project_use->done      = true;
      project_use->discarded = false;
      project_use->m_hidden  = true;
      project_use->selected  = true;
      project_use->change_path (project->get_cmtpath(), project);
      //      project_use->m_located = true;
      cmt_map <Use*, bool> visited;
      project_use->set_auto_imports(Unspecified, Off, visited);
      //project_use->set_auto_imports(Off);
      project_use->initial_scope = ScopePublic;      
      project->set_use(project_use);
      
      
      // add at the uses level ?????
      /*static Use::UsePtrVector& uses = Use::get_ordered_uses ();
	bool found = false;
	int  size  = uses.size ();
	cerr << "\n size:"<<size<< ":" << endl;     
	for (int n = 0; n < size; n++)
	{
	Use* tuse = uses[n];
	cerr << "\tpackage file [" << tuse->get_package_name() << "]" <<project_use->get_package_name()<< endl;                    
	if (tuse->get_package_name()==project_use->get_package_name())
	found=true;

	}
    
	if (not found)
	{
        uses.push_back (project_use);
        project_use->m_index = uses.size () - 1;
	}
      */

      SyntaxParser::parse_project_file_text (text, 
					     Project::get_project_file_name (),
					     project);
    }



  CmtSystem::cd (here);
  Project::order_all ();

  return (project);
}

/*----------------------------------------------------------*/
/*                                                          */
/*  Operations on Projects                                  */
/*                                                          */
/*----------------------------------------------------------*/

//----------------------------------------------------------
bool Project::create (const cmt_string& name, 
		      const cmt_string& release, 
		      const cmt_string& path)
{
  cmt_string pwd = CmtSystem::pwd ();

  if (CmtMessage::active (Error))
    {
  cerr << "------------------------------------------" << endl;
  cerr << "Configuring environment for project " << name << " " << release << " (from " << pwd << ") ";

  if (path != "")
    {
      cerr << " in " << path;
    }

  cerr << endl;
  cerr << "CMT version " << Cmt::get_cmt_version () << "." << endl;
  cerr << "------------------------------------------" << endl;
    }

  if (path != "")
    {
      if (!CmtSystem::mkdir (path))
	{
          CmtMessage::error ("Cannot create the " + path + " directory");
	  //          cerr << "Cannot create the " << path << " directory" << endl;
          return (false);
	}

      if (!CmtSystem::cd (path))
	{
          CmtMessage::error ("Cannot access the " + path + " directory");
	  //          cerr << "Cannot access the " << path << " directory" << endl;
          return (false);
	}
    }

  if (!CmtSystem::mkdir (name))
    {
      CmtMessage::error ("Cannot create the " + name + " directory");
      //      cerr << "Cannot create the " << name << " directory" << endl;
      return (false);
    }

  if (!CmtSystem::cd (name))
    {
      CmtMessage::error ("Cannot access the " + name + " directory");
      //      cerr << "Cannot access the " << name << " directory" << endl;
      return (false);
    }

  if (release != "")
    {
      if (!CmtSystem::mkdir (release))
	{
	  CmtMessage::error ("Cannot create the " + release + " directory");
	  //	  cerr << "Cannot create the " << release << " directory" << endl;
	  return (false);
	}
      
      if (!CmtSystem::cd (release))
	{
	  CmtMessage::error ("Cannot access the " + release + " directory");
	  //	  cerr << "Cannot access the " << release << " directory" << endl;
	  return (false);
	}
    }

  if (!CmtSystem::test_directory ("cmt"))
    {
      if (!CmtSystem::mkdir ("cmt"))
        {
          CmtMessage::error ("Cannot create the cmt directory");
	  //          cerr << "Cannot create the cmt directory" << endl;
          return (false);
        }
      else
        {
	  if (CmtMessage::active (Info))
	    cerr << "Installing the cmt directory" << endl;
        }
    }

  CmtSystem::cd ("cmt");

  if (!CmtSystem::test_file (get_project_file_name ()))
    {
      if (CmtMessage::active (Info))
	cerr << "Creating a new project file" << endl;

      ofstream f (get_project_file_name ());
      if (f)
        {
          f << "project " << name << endl;
          f << endl;
          f.close ();
        }
      else
	{
          CmtMessage::error ("Cannot create the project file");
	  //          cerr << "Cannot create the project file" << endl;
          return (false);
	}
    }
  else
    {
      cmt_string text;
      text.read (get_project_file_name ());

      ProjectPatcher p (name);

      p.run (text);
      p.commit ();

      if (CmtMessage::active (Info))
	cerr << "project file already there" << endl;
    }

  return (true);
}

//----------------------------------------------------------
Project* Project::find_by_name (const cmt_string& name)
{
  static ProjectVector& Projects = projects ();

  for (int i = 0; i < Projects.size (); i++)
    {
      Project& p = Projects[i];
      if (p.m_name == name) return (&p);
      
    }

  return (0);
}

//----------------------------------------------------------
Project* Project::find_by_cmtpath (const cmt_string& cmtpath)
{
  cmt_string compressed_path = cmtpath;
  CmtSystem::compress_path (compressed_path);

  static ProjectVector& Projects = projects ();

  for (int i = 0; i < Projects.size (); i++)
    {
      Project& p = Projects[i];

      if (p.m_cmtpath == compressed_path) return (&p);
      if (p.m_cmtpath_real == compressed_path) return (&p);
      if (p.m_cmtpath_pwd == compressed_path) return (&p);
    }

  return (0);
}

//----------------------------------------------------------
Project* Project::get_current ()
{
  /*
  cmt_string here = CmtSystem::pwd ();

  // In case there are symlinks
  cmt_string here_real;
  CmtSystem::realpath_ (here, here_real);
  */
  static ProjectVector& Projects = projects ();

  for (int i = (Projects.size () - 1); i >= 0; i--)
    {
      Project& p = Projects[i];

//       if (here.find (p.m_cmtpath_pwd) == 0) 
// 	{
// 	  result = &p;
// 	}

//      if (here.find (p.m_cmtpath) == 0) 
      if (p.is_current ()) 
	//      if (here_real.find (p.m_cmtpath_real) == 0) 
	{
	  return &p;
	}
    }

  return 0;
}

//----------------------------------------------------------
Project* Project::add (const cmt_string& name,
		       const cmt_string& release)
{
  static ProjectVector& Projects = projects ();

  {
    Project* project;

    project = find_by_name (name);
    if (project != 0) 
      {
	//	if (!Cmt::get_quiet ())
	//	  {
	    if (release != project->get_release ())
	      {
		//if (CmtMessage::active (Verbose))
		CmtMessage::error ("Project " + name
				   + " requested with conflicting releases "
				   + project->get_release () + " and " + release);
		//		cerr << "#CMT> Project " << name << " requested with conflicting releases " << project->get_release () << " and " << release << endl;
		CmtError::set (CmtError::project_release_conflict, name);
	      }
	    //	  }

	// Project objects are recreated here to follow the hierarchy
	// This duplication is needed for properly applying the strategies
	Project& p = Projects.add ();

	p.set_name (name);
	p.set_release (release);
	p.configure ();

	return (&p);

	//return (project);
      }
  }

  Project& project = Projects.add ();
  project.clear ();
  project.set_name (name);
  project.set_release (release);
  project.configure ();

  return (&project);
}

//----------------------------------------------------------
Project::ProjectVector& Project::projects ()
{
  static Database& db = Database::instance ();
  static ProjectVector& Projects = db.projects ();

  return (Projects);
}

//----------------------------------------------------------
Project::ProjectPtrVector& Project::ordered_projects ()
{
  static Database& db = Database::instance ();
  static ProjectPtrVector& Projects = db.ordered_projects ();

  return (Projects);
}

//----------------------------------------------------------
void Project::order_all ()
//Project::ProjectPtrVector Project::ordered_projects ()
{
  ProjectPtrVector OrderedProjects;
  //  static ProjectPtrVector OrderedProjects;
  static Project::ProjectPtrVector& orderedProjects = Project::ordered_projects ();

  static Project::ProjectVector& Projects = Project::projects ();
  const int size = Projects.size ();
  
  int offset (0);
  int iuser (-1);
  int ihome (-1);
  for (int i = 0; i < 2; i++)
    {
      if (offset < size)
	{
	  const cmt_string& name = Projects[offset].get_name ();
	  if (name == "CMTUSERCONTEXT")
	    {
	      iuser = offset;
	      ++offset;
	    }
	  else if (name == "CMTHOME")
	    {
	      ihome = offset;
	      ++offset;
	    }
	  else
	    break;
	}
    }

  OrderedProjects.resize (size);

  Use& use = Use::current();
  cmt_string current_path;
  cmt_string current_path_real;
  if (use.located ())
    {
      current_path_real = use.get_realpath ();
      if (current_path_real.size ())
	CmtMessage::verbose ("Using current use realpath `" + current_path_real + "'");
      else
	{
	  current_path = use.real_path;
	  CmtMessage::verbose ("Using current use real_path `" + current_path + "'");
	}
    }
  else
    current_path = Cmt::get_current_dir ();

  if (0 == current_path_real.size () &&
      !CmtSystem::realpath_ (current_path, current_path_real))
    {
      CmtError::set (CmtError::file_access_error, "Cannot compute real path `" +
      		     current_path + "'");
      CmtError::print ();
      return;
      //CmtMessage::error ("Cannot compute real path `" + Use::current().real_path + "'");
    }

  Project* p_cur (0);
  for (int i = 0; i < size; i++)
    {
      Project& p = Projects[i];
      if (i >= offset)
	p.m_order = -1;
      else
	p.m_order = -2;
      p.m_visits = 0;
      //
      // The current project specification
      //
      if (current_path_real.find (p.get_cmtpath_real ()) == 0)
	{
	  p_cur = &p;
	  if (p.is_current ()) continue;

	  p.set_is_current (true);
	  //
	  // The current project defines a tag with its name
	  //
	  
	  Tag* tag;
	  
	  tag = Tag::add (p.get_name (), PriorityConfig, "PROJECT", 0);
	  tag->mark ("PROJECT");
	}
      else if (p.is_current ())
	{
	  //
	  // Unset the tag with its name
	  //
	  Tag* tag = Tag::find (p.get_name ());
	  if (tag != 0)
	    tag->unmark ();

	  p.set_is_current (false);
	}
    }

  if (size == offset)
    {
      for (int i = 0; i < offset; i++)
	{
	  Project* p = &(Projects[i]);
	  OrderedProjects[i] = p;
	}
      /*
      if (2 == offset)
	{
	  OrderedProjects[0]->add_child (OrderedProjects[1]);
	  OrderedProjects[1]->add_parent (OrderedProjects[0]);
	  OrderedProjects[0]->update_strategies_from_children ();
	}
      */
      for (int i = OrderedProjects.size () - 2; i >= 0; i--)
	{
	  if (!OrderedProjects[i]->has_parents () &&
	      OrderedProjects[i]->get_children_size () == 0)
	    {
	      OrderedProjects[i]->add_child (OrderedProjects[i + 1]);
	      OrderedProjects[i]->update_strategies_from_children ();
	      OrderedProjects[i]->erase_child (OrderedProjects[i + 1]);
	    }
	}

      orderedProjects = OrderedProjects;
      //      return OrderedProjects;
      return;
    }

  Project* p (p_cur);
  //  Project* p = get_current ();

  //  cerr << "get_current: " << p << " offset: " << offset << endl;
  /*
  if (p == 0)
    {
      p = &(Projects[offset]);
      //      p = &(Projects[0]);
    }
  */
  //  cerr << "p: " << p << " offset: " << offset << " name: " << p->get_name () << endl;

  int order (-1);
  /*
  if ((p->get_name () != "CMTUSERCONTEXT") && (p->get_name () != "CMTHOME"))
    {
      ProjectPtrVector projs;
      p->m_order = ++order;
      //      p->m_visits++;
      projs.push_back (p);
      
      visit (offset, order, projs);
      //  visit (order, projs);
    }
  //  cerr << "order: " << order << endl;
  //  int unordered = size - offset - (order + 1);
  */
  ProjectPtrVector projs;

  if (p != 0 && (p->get_name () != "CMTUSERCONTEXT") && (p->get_name () != "CMTHOME"))
    {
      p->m_order = ++order;
      projs.push_back (p);
    }

  if (p == 0 ||
      !p->has_parents ())
    // may come from CMTPATH entry
      /*
      (!p->has_parents () && p->get_children_size () == 0)
      // comes from CMTPATH entry
      )
      */
    {
      for (int i = offset; i < size; i++)
	{
	  Project* q = &(Projects[i]);
	  if (q != p && !q->has_parents ())
	    { // may come from CMTPATH entry
	      q->m_order = ++order;
	      projs.push_back (q);
	    }
	}
    }

  visit (offset, order, projs);

  int beg (0);
  if  (-1 != iuser)
    {
      OrderedProjects[0] = &(Projects[iuser]);
      //      cerr << "OrderedProjects[0]: " << (OrderedProjects[0])->get_name () << endl;
      ++beg;
    }

  int noorder (0);
  for (int i = offset; i < size; i++)
    //  for (int i = 0; i < size; i++)
    {
      Project* p = &(Projects[i]);
      int j = p->m_order;
      if (-1 == j)
	{
	  OrderedProjects[beg + order + 1 + noorder++] = p;
	  //	  cerr << "no: OrderedProjects[" << beg + order + noorder << "]: " << (OrderedProjects[beg + order + noorder])->get_name () << endl;
	  //	  OrderedProjects[order + 1 + noorder++] = p;
	  /*
	  if (CmtMessage::active (Verbose))
	    CmtMessage::warning ("Not ordered project " + p->get_name () +
				 " in path " + p->get_cmtpath_pwd () +
				 " from " + p->get_cmtpath_source ());
	  continue;
	  */
	}
      else if (-2 != j)
	{
	  OrderedProjects[beg + j] = p;
	}
      /*
      else if  (-2 == j)
	{
	}
      else
	{
	  OrderedProjects[beg + j] = p;
      //      OrderedProjects[j] = p;
	  //	  cerr << "OrderedProjects[" << beg + j << "]: " << (OrderedProjects[beg + j])->get_name () << endl;
	}
      */
    }

  if (p != 0)
    {
      for (int i = 0; i < noorder; i++)
	OrderedProjects.pop_back ();
    }
  if  (-1 != ihome)
    {
      OrderedProjects[OrderedProjects.size () - 1] = &(Projects[ihome]);
    }
  /*
  if (p)
    {
      if  (-1 != ihome)
	{
	  OrderedProjects[beg + order + 1] = &(Projects[ihome]);
	}
      for (int i = 0; i < noorder; i++)
	OrderedProjects.pop_back ();
    }
  else
    {
      if  (-1 != ihome)
	{
	  OrderedProjects[size - 1] = &(Projects[ihome]);
	}
    }
  */
      /*
  if  (-1 != iuser)
    {
      OrderedProjects[0]->add_child (OrderedProjects[1]);
      OrderedProjects[1]->add_parent (OrderedProjects[0]);
      OrderedProjects[0]->update_strategies_from_children ();
    }
      */

//   if  (-1 != ihome)
//     {
      /*
      cerr << "beg: " << beg << " order: " << order << endl;
      cerr << "noorder: " << noorder << " size - offset: " << size - offset << endl;
      */
      /*
      if (noorder != size - offset)
	{  // the last ordered project
	  OrderedProjects[beg + order]->add_child (OrderedProjects[size - 1]);
	  OrderedProjects[size - 1]->add_parent (OrderedProjects[beg + order]);
	  OrderedProjects[beg + order]->update_strategies_from_children ();
	}
      else
	{
	  OrderedProjects[size - 2]->add_child (OrderedProjects[size - 1]);
	  OrderedProjects[size - 1]->add_parent (OrderedProjects[size - 2]);
	  OrderedProjects[size - 2]->update_strategies_from_children ();
	}
      */
//     }

  for (int i = OrderedProjects.size () - 2; i >= 0; i--)
    {
      if (!OrderedProjects[i]->has_parents () &&
	  OrderedProjects[i]->get_children_size () == 0)
	{
	  OrderedProjects[i]->add_child (OrderedProjects[i + 1]);
	  OrderedProjects[i]->update_strategies_from_children ();
	  OrderedProjects[i]->erase_child (OrderedProjects[i + 1]);
	}
    }

  orderedProjects = OrderedProjects;
  //  return OrderedProjects;
  return;
}

/*----------------------------------------------------------*/
void Project::clear_all ()
{
  static ProjectVector& Projects = projects ();

  for (int i = 0; i < Projects.size (); i++)
    {
      Project& project = Projects[i];
      project.clear ();
    }

  Projects.clear ();
}

/*----------------------------------------------------------*/
void Project::show_all (PrintMode mode)
//void Project::show_all ()
{
  static const Project::ProjectPtrVector& Ordered = Project::ordered_projects ();
  static Project::ProjectVector& Projects = Project::projects ();
  
  for (int i = 0; i < Projects.size (); i++)
    {
      Project& p = Projects[i];
      p.m_visited = false;
    }

  switch (mode)
    {
    case Xml :
      Cmt::print_xml_prolog ("projects");
      cout << "<projects>";
      break;
    default:
      break;
    }

  for (int i = 0; i < Ordered.size (); i++)
    Ordered[i]->show (mode);
  //    Ordered[i]->show ();

  for (int i = 0; i < Projects.size (); i++)
    Projects[i].show (mode);
  //    Projects[i].show ();

  switch (mode)
    {
    case Xml :
      cout << "</projects>" << endl;
      break;
    default:
      break;
    }
  /*
  Project* p = get_current ();

  if (p == 0)
    {
      if (Ordered.size () == 0) return;

      p = Ordered[0];
    }
  */
  //  p->show ();
}

/*----------------------------------------------------------*/
void Project::show_container (const cmt_string& path)
//void Project::show_container ()
{
  Project* p (0);
  if (path == "")
    {
      p = get_current ();
    }
  else
    {
      p = find_by_cmtpath (find_in_cmt_paths (path));
    } 

  if (p == 0)
    {
      cmt_string msg ("No project found for path "
		      + (path != "" ? path : (Use::current()).real_path)
		      );
      CmtMessage::warning (msg);
      CmtError::set (CmtError::warning, msg);
      return;
    }
  Use* use = &(p->m_container);
  if (use->get_package_name () == "")
    {
      cmt_string msg ("No container specified for project " + p->get_name ()
		      + " (" + p->get_cmtpath () + ")");
      CmtMessage::warning (msg);
      CmtError::set (CmtError::warning, msg);
      return;
    }
  
  if (!use->located ())
    {
      CmtMessage::warning ("container " + use->get_info ()
			   + " not found");
      CmtError::set (CmtError::package_not_found, use->get_package_name ());
    }
  else
    {
      static const cmt_string empty;
      cmt_string p = use->real_path;
      if (use->path != "")
	{
	  int pos = p.find_last_of (use->path);
	  if (pos != cmt_string::npos)
	    {
	      p.erase (pos);
	    }
	}
      
      cout << "container " << use->get_package_name ()
	   << " " << use->version;
      
      if (CmtSystem::absolute_path (use->path))
	{
	  if (!Cmt::get_quiet ()) 
	    {
	      cout << " (" << use->path << ")";
	    }
	}
      else
	{
	  cout << " " << use->path;
	}
      
      if (!Cmt::get_quiet ()) 
	{
	  if (p != "") cout << " (" << p << ")";
	  if (use->auto_imports == Off) cout << " (no_auto_imports)";
	}
      
      cout << endl;
    }
}

/*----------------------------------------------------------*/
void Project::show_specified_strategies_for_all ()
{
  static ProjectVector& Projects = projects ();

  for (int i = 0; i < Projects.size (); i++)
    {
      const Project& project = Projects[i];
      project.show_specified_strategies ();
    }
}

/*----------------------------------------------------------*/
class VisitorForShowPaths : public IProjectVisitor
{
public:
  VisitorForShowPaths ()
  {
  }

  void pre (Project* p)
  {
    const cmt_string& w = p->get_cmtpath_pwd ();
    const cmt_string& s = p->get_cmtpath_source ();

    if (s == "default path") return;

    if (CmtSystem::test_directory (w))
      {
	cout << "# Add path " << w << " from " << s << endl;
      }
  }

  void in (Project* p)
  {
    const cmt_string& w = p->get_cmtpath_pwd ();
    const cmt_string& s = p->get_cmtpath_source ();

    if (s == "default path") return;

    if (CmtSystem::test_directory (w))
      {
	cout << "# Add path " << w << " from " << s << endl;
      }
  }

  void in_again (Project* p)
  {
    const cmt_string& w = p->get_cmtpath_pwd ();
    const cmt_string& s = p->get_cmtpath_source ();

    if (s == "default path") return;

    if (CmtSystem::test_directory (w))
      {
	cout << "# Remove path " << w << " from " << s << endl;
	cout << "# Add path " << w << " from " << s << endl;
      }
  }

  void post (Project* p)
  {
  }
};

/*----------------------------------------------------------*/
void Project::show_paths (const CmtSystem::cmt_string_vector& arguments,
			  ostream& out)
//void Project::show_paths ()
{
  if (arguments.size () == 0 ||
      Cmt::get_action () != action_show_path)
    {
      const ProjectPtrVector& Ordered = Project::ordered_projects ();
      for (int i = 0; i < Ordered.size (); i++)
	{
	  const Project* p = Ordered[i];
	  const cmt_string& w = p->get_cmtpath_pwd ();
	  const cmt_string& s = p->get_cmtpath_source ();
	  
	  if (s == "default path") continue;
	  
	  if (CmtSystem::test_directory (w))
	    {
	      out << "# Add path " << w << " from " << s << endl;
	    }
	}
    }
  else if (arguments.size () == 1 && cmt_string (arguments[0]) == "-d")
    {
      const ProjectVector& Projects = projects ();
      for (int i = 0; i < Projects.size (); i++)
	{
	  Project& p = Projects[i];
	  const cmt_string& w = p.get_cmtpath_pwd ();
	  const cmt_string& s = p.get_cmtpath_source ();
	  
	  if (s == "default path") continue;
	  
	  if (CmtSystem::test_directory (w))
	    {
	      out << "# Create path " << w << " from " << s << endl;
	    }
	}
      /*
      VisitorForShowPaths visitor;
      start_visit (visitor);
      */
    }
  else
    CmtMessage::error ("show_paths: unexpected argument(s)");
}

//----------------------------------------------------------
const cmt_string& Project::get_project_file_name ()
{
  static const cmt_string name = "project.cmt";

  return (name);
}

//----------------------------------------------------------
/**
 *  Fill with projects (pointers) for CMTPATH entries >=1 and <= depth
 *  except for CMTUSERCONTEXT project or CMTHOME project
 *  unless either is current project.
 *  Special values of depth:
 *   0: CMTUSERCONTEXT project only
 *  -1: CMTHOME project only
 *
 *  @param depth in CMTPATH up to which fill
 *  @param path_selections vector to fill
 */
void Project::fill_selection (int depth, ConstProjectPtrVector& path_selections)
{
  static ProjectPtrVector& Ordered = Project::ordered_projects ();
  int curdepth = depth;
  for (int i = 0; i < Ordered.size (); i++)
    {
      const Project* project = Ordered[i];
      const cmt_string& src = project->get_cmtpath_source ();

      if (src == "default path") continue;

      if (depth > 0)
	{
	  // project for CMTPATH entries >=1 and <= depth
	  // except for CMTUSERCONTEXT path or CMTHOME path
	  // OR
	  // CMTUSERCONTEXT path if it's current project
	  // CMTHOME        path if it's current project
	  if (i == 0
	      && !project->is_current ()
	      && src == "CMTUSERCONTEXT") continue;
	  // this is CMTUSERCONTEXT
	  if (i == Ordered.size () - 1
	      && !project->is_current ()
	      && src == "CMTHOME") continue;
	  // this is CMTHOME
	  path_selections.push_back (project);
	  //cerr << "fill: " << i << ": " << project->get_cmtpath () << ": " << curdepth << endl;
	  if (--curdepth == 0) break;
	}
      else if (depth == 0)
	{
	  // CMTUSERCONTEXT path only
	  if (i == 0
	      && src == "CMTUSERCONTEXT")
	    {
	      path_selections.push_back (project);
	      break;
	    }
	}
      else if (depth == -1)
	{
	  // CMTHOME path only
	  if (i == Ordered.size () - 1
	      && src == "CMTHOME")
	    {
	      path_selections.push_back (project);
	      break;
	    }
	}
    }
}

//----------------------------------------------------------
// void Project::fill_selection (int depth, CmtSystem::cmt_string_vector& path_selections)
// {
//   static ProjectPtrVector& Ordered = Project::ordered_projects ();
//   int curdepth = depth;
//   for (int i = 0; i < Ordered.size (); i++)
//     {
//       const Project* project = Ordered[i];

//       const cmt_string& p = project->get_cmtpath ();
//       const cmt_string& pwd = project->get_cmtpath_pwd ();
//       const cmt_string& p_real = project->get_cmtpath_real ();
//       const cmt_string& src = project->get_cmtpath_source ();

//       if (src == "default path") continue;

//       if (depth > 0)
// 	{
// 	  // packages from CMTPATH entries >=1 and <= depth
// 	  // except for CMTUSERCONTEXT path or CMTHOME path
// 	  // OR
// 	  // CMTUSERCONTEXT path if it's current project
// 	  // CMTHOME        path if it's current project
// 	  if (i == 0
// 	      && !project->is_current ()
// 	      && src == "CMTUSERCONTEXT") continue;
// 	  // this is CMTUSERCONTEXT
// 	  if (i = Ordered.size () - 1
// 	      && !project->is_current ()
// 	      && src == "CMTHOME") continue;
// 	  // this is CMTHOME
// 	  cmt_string& s1 = path_selections.add ();
// 	  s1 = p;
// 	  if (pwd != p)
// 	    {
// 	      cmt_string& s2 = path_selections.add ();
// 	      s2 = pwd;
// 	    }
// 	  if (p_real != p && p_real != pwd)
// 	    {
// 	      cmt_string& s3 = path_selections.add ();
// 	      s3 = p_real;
// 	    }
// 	  if (--curdepth == 0) break;
// 	}
//       else if (depth == 0)
// 	{
// 	  // CMTUSERCONTEXT path only
// 	  if (i == 0
// 	      && src == "CMTUSERCONTEXT")
// 	    {
// 	      cmt_string& s1 = path_selections.add ();
// 	      s1 = p;
// 	      if (pwd != p)
// 		{
// 		  cmt_string& s2 = path_selections.add ();
// 		  s2 = pwd;
// 		}
// 	      if (p_real != p && p_real != pwd)
// 		{
// 		  cmt_string& s3 = path_selections.add ();
// 		  s3 = p_real;
// 		}
// 	      break;
// 	    }
// 	}
//       else if (depth == -1)
// 	{
// 	  // CMTHOME path only
// 	  if (i = Ordered.size () - 1
// 	      && src == "CMTHOME")
// 	    {
// 	      cmt_string& s1 = path_selections.add ();
// 	      s1 = p;
// 	      if (pwd != p)
// 		{
// 		  cmt_string& s2 = path_selections.add ();
// 		  s2 = pwd;
// 		}
// 	      if (p_real != p && p_real != pwd)
// 		{
// 		  cmt_string& s3 = path_selections.add ();
// 		  s3 = p_real;
// 		}
// 	      break;
// 	    }
// 	}
// // 	  if (depth > 0)
// // 	    {
// // 	      cmt_string& s1 = path_selections.add ();
// // 	      s1 = p;
// // 	      if (pwd != p)
// // 		{
// // 		  cmt_string& s2 = path_selections.add ();
// // 		  s2 = pwd;
// // 		}
// // 	      if (p_real != p && p_real != pwd)
// // 		{
// // 		  cmt_string& s3 = path_selections.add ();
// // 		  s3 = p_real;
// // 		}
// // 	      if (src != "CMTHOME" && src != "CMTUSERCONTEXT")
// // 		depth--;

// // 	      if (depth == 0) break;
// // 	    }

//     }
// }

//----------------------------------------------------------
/**
 *  Fill with projects (pointers) for CMTUSERCONTEXT and CMTHOME projects
 *  (if any exists)
 *  unless either is current project.
 *  
 *  @param path_exclusions vector to fill
 */
void Project::fill_exclusion (ConstProjectPtrVector& path_exclusions)
{
  static ProjectPtrVector& Ordered = Project::ordered_projects ();
  for (int i = 0; i < Ordered.size (); i++)
    {
      const Project* project = Ordered[i];
      const cmt_string& src = project->get_cmtpath_source ();

      if (!project->is_current ()
	  && (src == "CMTUSERCONTEXT" || src == "CMTHOME"))
	path_exclusions.push_back (project);
    }
}

//----------------------------------------------------------
void Project::broadcast (IProjectAction& action)
{
  //  const ProjectPtrVector Projects = Project::ordered_projects ();
  static ProjectPtrVector& Projects = Project::ordered_projects ();

  for (int i = 0; i < Projects.size (); i++)
    {
      const Project* project = Projects[i];

      if (!action.run (*project)) break;
    }
  /*
  static ProjectVector& Projects = projects ();

  for (int i = 0; i < Projects.size (); i++)
    {
      const Project& project = Projects[i];

      if (!action.run (project)) break;
    }
  */
}

//----------------------------------------------------------
void Project::reverse_broadcast (IProjectAction& action)
{
  //  const ProjectPtrVector Projects = Project::ordered_projects ();
  static ProjectPtrVector& Projects = Project::ordered_projects ();

  for (int i = (Projects.size () - 1); i >= 0; i--)
    {
      const Project* project = Projects[i];

      if (!action.run (*project)) break;
    }
  /*
  static ProjectVector& Projects = projects ();

  for (int i = (Projects.size () - 1); i >= 0; i--)
    {
      const Project& project = Projects[i];

      if (!action.run (project)) break;
    }
  */
}

//----------------------------------------------------------
void Project::scan_paths (PathScanner& scanner, PathScanner::actor& a)
{
  static ProjectVector& Projects = projects ();

  int i;

  for (i = 0; i < Projects.size (); i++)
    {
      Project& p  = Projects[i];
      p.m_visited = false;
    }

  for (i = 0; i < Projects.size (); i++)
    {
      const Project& project = Projects[i];
       
      const cmt_string& p = project.m_cmtpath;
      scanner.scan_path (p, a);
    }
}

//----------------------------------------------------------
void Project::scan_paths_for_package (PathScanner& scanner, const cmt_string& name)
{
  static ProjectVector& Projects = projects ();

  bool found (false);
  for (int i = 0; i < Projects.size (); i++)
    {
      const Project& project = Projects[i];

      const cmt_string& p = project.m_cmtpath;
      if (scanner.scan_package (p, name))
	found = true;
    }

  if (!found)
    CmtError::set (CmtError::package_not_found, name);
}

//----------------------------------------------------------
cmt_string Project::find_in_cmt_paths (const cmt_string& path, bool realpath)
{
  //const cmt_string pwd = CmtSystem::pwd ();

  // In case there are symlinks
  cmt_string path_real;
  //cerr << "realpath_: find_in_cmt_paths" << endl;
  if (realpath)
    path_real = path;
  else if (!CmtSystem::realpath_ (path, path_real))
    return ("");

  static ProjectVector& Projects = projects ();

  for (int i = 0; i < Projects.size (); i++)
    {
      const Project& project = Projects[i];

      const cmt_string& p = project.m_cmtpath;
      const cmt_string& p_real = project.m_cmtpath_real;
      const cmt_string& w = project.m_cmtpath_pwd;
      const cmt_string& s = project.m_cmtpath_source;

      if (s == "default path") continue;

      // MUST be directory if project added
      //      if (CmtSystem::test_directory (p))
	{
// 	  if (path.find (p) != cmt_string::npos)
// 	    {
// 	      return (p);
// 	    }

	  if (path_real.find (p_real) == 0) //!= cmt_string::npos)
	    {
	      return (p);
	    }

	  // To become the current area, a path must correspond to the current package
	  if (path.find (w) == 0)// != cmt_string::npos)
	    {
	      return (p);
	    }
	}

	/* This code is never used since p is returned above
      if (p == w) continue;

      // MUST be directory if project added
      //      if (CmtSystem::test_directory (w))
	{
	  if (path.find (w) == 0)// != cmt_string::npos)
	    {
	      return (w);
	    }
	}
	*/
    }

  return ("");
}

//----------------------------------------------------------
void Project::visit (IProjectVisitor& visitor)
{
  if (m_visited) return;
  m_visited = true;

  int i;

  for (i = 0; i < get_children_size (); i++)
    {
      Project* child = get_child (i);

      if (child->visited ()) continue;

      visitor.in (child);
    }

  for (i = 0; i < m_children.size (); i++)
    {
      Project* child = m_children[i];
      child->visit (visitor);
    }
}

//----------------------------------------------------------
void Project::visit (IProjectVisitor& visitor, ProjectPtrVector& projects)
{
  int size = projects.size ();
  if (0 == size) return;

  ProjectPtrVector children;
  for (int j = 0; j < size; j++)
    {
      Project* p = projects[j];
      if (20 == p->m_visits)
	continue;
      for (int i = 0; i < p->get_children_size (); i++)
	{
	  Project* child = p->get_child (i);
	  if (0 == child->m_visits)	  
	    visitor.in (child);
	  else
	    visitor.in_again (child);
	  child->m_visits++;
	  children.push_back (child);
	}
    }

  visit (visitor, children);
}

//----------------------------------------------------------
/**
 *  Visit the projects tree and order the projects.
 *  Order is the projects upon which the project depends directly,
 *  then the direct dependencies of the of the first dependency, of the second
 *  dependency and so on. That is first left to right, then downwards.
 *  @param offset the offset from which to use all the projects
 *  @param order the order of the last project visited
 *  @param projs vector of projects to visit and order
 */
void Project::visit (const int offset, int& order, ProjectPtrVector& projs)
{
  int size = projs.size ();
  //  cerr << "visit: " << offset << " " << order << " " << size << endl;
  if (0 == size) return;
  static ProjectVector& all = Project::projects ();
  /*
  cerr << "@ visit: " << order;
  for (int j = 0; j < size; j++)
    {
      Project* p = projs[j];
      cerr << " " << p->get_name ();
    }
  cerr << " @" << endl;
  */
  ProjectPtrVector children;
  for (int j = 0; j < size; j++)
    {
      Project* p = projs[j];
      p->m_visits++;
      // Avoid looping in case of circular project dependencies
      if (500 <= p->m_visits)
	//      if (100 <= p->m_visits)
	continue;
      for (int i = 0; i < p->get_children_size (); i++)
	{
	  Project* child = p->get_child (i);
	  const int chorder = child->m_order;
	  const int porder = p->m_order;
	  /*
	  cerr << ">>> " << p->get_name () << " child: " << i << endl;
	  cerr << child->get_name () << " in: " << chorder << endl;
	  */
	  if (-2 == chorder)
	    continue;
	  else if (-1 == chorder)
	    { // not ordered yet, i.e. visited for the first time
	      child->m_order = ++order;
	    }
	  else if (child->is_current ())
	    //	  else if (0 == chorder)
	    { // the project we started with, i. e. the current project:
	      //     o circular dependency
	      //     o do want to keep it first no matter what
	      if (CmtMessage::active (Verbose))
		CmtMessage::warning ("Circular dependency on project: "
				     + child->get_name ()
				     + " " + child->get_release ()
				     + " " + child->get_cmtpath ());
	    }
	  else if ((0 <= chorder) && (chorder < porder))
	    //	  else if ((0 < chorder) && (chorder < porder))
	    { // ordered already, want to put it after the parent in the order
	      for (int k = offset; k < all.size (); k++)
		{
		  Project& q = all[k];
		  if (&q == child)
		    {// the child we are putting after the parent in the order
		      q.m_order = porder;
		      //	      cerr << "Moved back: " << q.get_name () << " order: " << q.m_order << endl;
		    }
		  else if ((chorder < q.m_order) && (q.m_order <= porder))
		    q.m_order--;
		}
	    }
	  //  cerr << child->get_name () << " out: " << child->m_order << endl;
	  //	  child->m_visits++;
	  bool unknown (true);
	  for (int j = 0; j < children.size (); j++)
	    {
	      if (children[j] == child)
		{
		  unknown = false;
		  break;
		}
	    }
	  if (unknown)
	    {
	      children.push_back (child);
	    }
	}
    }

  visit (offset, order, children);
}

//----------------------------------------------------------
void Project::start_visit (IProjectVisitor& visitor)
{
  static Project::ProjectVector& Projects = Project::projects ();
  
  for (int i = 0; i < Projects.size (); i++)
    {
      Project& p = Projects[i];
      p.m_visited = false;
      p.m_visits = 0;
    }

  Project* p = get_current ();

  if (p == 0)
    {
      if (Projects.size () == 0) return;

      p = &(Projects[0]);
    }

  //  visitor.pre (p);
  //p->visit (visitor);
  //  visitor.post (p);
  visitor.in (p);
  p->m_visits++;
  ProjectPtrVector projs;
  projs.push_back (p);
  visit (visitor, projs);
}

//----------------------------------------------------------
class VisitorForFillCMTPATH : public IProjectVisitor
{
public:
  VisitorForFillCMTPATH (cmt_string& buffer) : m_buffer (buffer)
  {
    buffer = "path CMTPATH \"\" \n";
  }

  void pre (Project* p)
  {
    const cmt_string& w = p->get_cmtpath_pwd ();
    const cmt_string& s = p->get_cmtpath_source ();

    if (s == "default path") return;

    if (CmtSystem::test_directory (w))
      {
	m_buffer += "path_append CMTPATH \"";
	m_buffer += w;
	m_buffer += "\" \n";
      }
  }

  void in (Project* p)
  {
    const cmt_string& w = p->get_cmtpath_pwd ();
    const cmt_string& s = p->get_cmtpath_source ();

    if (s == "default path") return;

    if (CmtSystem::test_directory (w))
      {
	m_buffer += "path_append CMTPATH \"";
	m_buffer += w;
	m_buffer += "\" \n";
      }
  }

  void in_again (Project* p)
  {
    const cmt_string& w = p->get_cmtpath_pwd ();
    const cmt_string& s = p->get_cmtpath_source ();

    if (s == "default path") return;

    if (CmtSystem::test_directory (w))
      {
	m_buffer += "path_remove CMTPATH \"";
	m_buffer += w;
	m_buffer += "\" \n";
	m_buffer += "path_append CMTPATH \"";
	m_buffer += w;
	m_buffer += "\" \n";
      }
  }

  void post (Project* p)
  {
    //cerr << "Buffer = " << m_buffer << endl;
  }

private:
  cmt_string& m_buffer;

};

//----------------------------------------------------------
void Project::fill_cmtpaths (cmt_string& buffer)
{
  /*
    Try to re-create all CMTPATH items from project definitions.
    The goal is to generate CMTPATH even if this EV was not pre-set
    which is the case when CMTPROJECTPATH is only used
  */

  /*
  VisitorForFillCMTPATH visitor (buffer);

  start_visit (visitor);
  */
  const ProjectPtrVector& Ordered = Project::ordered_projects ();

  buffer = "path CMTPATH \"\" \n";
  for (int i = 0; i < Ordered.size (); i++)
    {
      const Project* p = Ordered[i];
      const cmt_string& w = p->get_cmtpath_pwd ();
      const cmt_string& s = p->get_cmtpath_source ();
      
      if (s == "default path") continue;
      
      if (CmtSystem::test_directory (w))
	{
	  buffer += "path_append CMTPATH \"";
	  buffer += w;
	  buffer += "\" \n";
	}
    }
}

//----------------------------------------------------------
Project::Project () : m_name (""), m_author("")
{
  clear ();
}

//----------------------------------------------------------
const cmt_string& Project::get_name () const
{
  return (m_name);
}

//----------------------------------------------------------
const cmt_string& Project::get_release () const
{
  return (m_release);
}

//----------------------------------------------------------
const cmt_string& Project::get_container_name () const
{
  return (m_container_name);
}

//----------------------------------------------------------
const cmt_string& Project::get_container_version () const
{
  return (m_container_version);
}

//----------------------------------------------------------
const cmt_string& Project::get_container_path () const
{
  return (m_container_path);
}

//----------------------------------------------------------
const Use& Project::get_container () const
{
  return (m_container);
}

//----------------------------------------------------------
const cmt_string& Project::get_cmtpath () const
{
  return (m_cmtpath);
}

//----------------------------------------------------------
const cmt_string& Project::get_cmtpath_real () const
{
  return (m_cmtpath_real);
}

//----------------------------------------------------------
const cmt_string& Project::get_cmtpath_pwd () const
{
  return (m_cmtpath_pwd);
}

//----------------------------------------------------------
const cmt_string& Project::get_cmtpath_source () const
{
  return (m_cmtpath_source);
}

//----------------------------------------------------------
int Project::get_children_size () const
{
  return (m_children.size ());
}

//----------------------------------------------------------
Project* Project::get_child (int index) const
{
  if (index < 0) return (0);
  if (index >= m_children.size ()) return (0);
  return (m_children[index]);
}

//----------------------------------------------------------
bool Project::visited () const
{
  return (m_visited);
}

//----------------------------------------------------------
void Project::set_name (const cmt_string& name)
{
  m_name = name;
}

//----------------------------------------------------------
void Project::set_release (const cmt_string& release)
{
  m_release = release;
}

//----------------------------------------------------------
void Project::set_container_name (const cmt_string& name)
{
  m_container_name = name;
}

//----------------------------------------------------------
void Project::set_container_version (const cmt_string& container_version)
{
  m_container_version = container_version;
}

//----------------------------------------------------------
void Project::set_container_path (const cmt_string& container_path)
{
  m_container_path = container_path;
}

//----------------------------------------------------------
void Project::set_cmtpath (const cmt_string& path)
{
  m_cmtpath = path;
}

//----------------------------------------------------------
void Project::set_cmtpath_real (const cmt_string& path)
{
  m_cmtpath_real = path;
}

//----------------------------------------------------------
void Project::set_cmtpath_pwd (const cmt_string& path)
{
  m_cmtpath_pwd = path;
}

//----------------------------------------------------------
void Project::set_cmtpath_source (const cmt_string& source)
{
  m_cmtpath_source = source;
}

//----------------------------------------------------------
void Project::set_is_current (bool is_current)
{
  m_is_current = is_current;
}

//----------------------------------------------------------
bool Project::is_current () const
{
  return m_is_current;
}

//----------------------------------------------------------
void Project::clear ()
{
  m_name    = "";
  m_release = "";
  m_cmtpath = "";
  m_cmtpath_real = "";
  m_cmtpath_pwd    = "";
  m_cmtpath_source = "";
  m_use            = 0;

  m_parents.clear ();
  m_children.clear ();

  m_configured = false;

  m_strategies.clear ();
  m_is_current = false;
}

//----------------------------------------------------------
bool Project::has_parents () const
{
  return ((m_parents.size () > 0));
}

//----------------------------------------------------------
bool Project::has_parent (Project* p) const
{
  if (p == 0) return (false);
  if (p == this) return (false);

  const cmt_string& name = p->get_name ();

  int i;

  for (i = 0; i < m_parents.size (); i++)
    {
      const Project* parent = m_parents[i];
      if (parent == 0) continue;

      if (parent->get_name () == name)
	{
	  // registered as a parent
	  return (true);
	}

      if (parent->has_parent (p))
	{
	  // recurse
	  return (true);
	}
    }

  return (false);
}

//----------------------------------------------------------
bool Project::has_child (Project* p) const
{
  if (p == 0) return (false);
  if (p == this) return (false);

  const cmt_string& name = p->get_name ();

  int i;

  for (i = 0; i < m_children.size (); i++)
    {
      const Project* child = m_children[i];
      if (child == 0) continue;

      if (child->get_name () == name)
	{
	  // registered as a child
	  return (true);
	}

      if (child->has_child (p))
	{
	  // recurse
	  return (true);
	}
    }

  return (false);
}

//----------------------------------------------------------
void Project::add_parent (Project* p)
{
  if (p == 0) return;
  if (p == this) return;

  //cerr << "Adding parent " << p->get_name () << " to " << m_name << endl;

  if (has_child (p)) return;
  if (has_parent (p)) return;

  m_parents.push_back (p);
}

//----------------------------------------------------------
void Project::add_child (Project* p)
{
  if (p == 0) return;
  if (p == this) return;

  if (has_child (p)) return;
  if (has_parent (p)) return;

  m_children.push_back (p);
}

//----------------------------------------------------------
void Project::erase_child (Project* p)
{
  if (p == 0) return;
  if (p == this) return;

  if (!has_child (p)) return;

  const cmt_string& name = p->get_name ();

  for (int i = 0; i < m_children.size (); i++)
    {
      const Project* child = m_children[i];
      if (child == 0) continue;

      if (child->get_name () == name)
	{
	  // registered as a child
	  m_children.erase (i);
	  break;
	}
    }
}

//----------------------------------------------------------
void Project::configure ()
{
  if (m_configured) return;
  m_configured = true;

  set_default_strategy ("SetupConfig");
  set_default_strategy ("SetupRoot");
  set_default_strategy ("SetupCleanup");
  set_default_strategy ("SetupScripts");
  set_default_strategy ("BuildPrototypes");
  set_default_strategy ("InstallArea");
  set_default_strategy ("VersionDirectory");
}

/**---------------------------------------------------------
   A container statement is met in the project file
*/
void Project::container_action (const CmtSystem::cmt_string_vector& words)
//void Project::container_action (const cmt_string& name, const cmt_string& version)
{
  //
  // complete syntax : "container <package> <version> <path>"  
  // minimal syntax  : "container <package>"
  //
  //  o if <version> is omitted then take any version available
  //  o <version> can be specified using "v*" or "v<n>r*" or "v<n>r<m>p*"
  //
  //  o the notation "v*" is preferred to omission (particularly since
  //    omission does not permit <path>)
  //
  if (words.size () < 2) return;

  CmtSystem::cmt_string_vector ewords;
  for (int i = 1; i < words.size (); i++)
    {
      const cmt_string& w = words[i];
      cmt_string ew = w;

      Symbol::expand (ew);
      if (ew != w)
        {
          CmtSystem::cmt_string_vector ws;
          CmtSystem::split (ew, " ", ws);

          for (int j = 0; j < ws.size (); ++j)
            {
	      ewords.push_back (ws[j]);
            }
        }
      else
        {
	  ewords.push_back (w);
        }
    }

  cmt_string name, version, path;
  if (ewords.size () > 0) name = ewords[0];
  if (ewords.size () > 1) version = ewords[1];
  if (ewords.size () > 2) path = ewords[2];

  Use* use = &m_container;
  if (name == "")
    {
      use->clear ();
      return;
    }
  if (version == "") version = "*";

  use->set (name, version, path);
  use->get_package ()->remove_use (use);
  CmtSystem::cd (m_cmtpath_pwd);
  if (use->move_to ("", true))
    {
      if (use->get_realpath ().find (this->get_cmtpath_real ()) == 0)
	use->set_project (this);
    }
  else
    if (0 == CmtSystem::getenv ("CMTPEDANTIC").size () &&
	0 == CmtSystem::getenv ("PEDANTIC").size ())
      CmtMessage::warning
	(CmtError::get_error_name (CmtError::configuration_error)
	 + ": Cannot locate container: " + use->get_info ());
    else
      CmtError::set(CmtError::configuration_error,
		    "Cannot locate container: " + use->get_info ());
  
  CmtSystem::cd (m_cmtpath_pwd + CmtSystem::file_separator () + "cmt");

  if (CmtError::get_last_error_code () == CmtError::configuration_error)
    return;

  set_container_name (name);
  set_container_version (version);
  set_container_path (path);
}

/**---------------------------------------------------------
   A use statement is met in the project file
*/
void Project::use_action (const cmt_string& name, const cmt_string& release)
{
  if (Cmt::get_debug ())
    {
      cout << "Use action " << name << " " << release << endl;
    }

  // A project with its release is specified
  //
  // Is this project already visible?
  // If not: look for it
  //   + get CMTPROJECTPATH
  //   + search from all entries of CMTPROJECTPATH : p(i)/<name>/<release>
  //   + when found, this should become a new CMTPATH entry
  //   +             the new project is then parsed ... etc...

  // First test it wilcard is used
  int v = -1;
  int r = -1;
  int p = -1;
  cmt_string new_release = release;
  CmtSystem::is_version_directory (new_release, v, r, p);  
  bool use_has_wild_card = (v == -1) || (r == -1) || (p == -1);
  if (use_has_wild_card)
    {
      cmt_string selected_release = "";

      if (select_release(name, new_release,selected_release))
	{
          // cerr <<"selected_release: "<<selected_release<<endl;    
          new_release = selected_release;
	}         
    }

  cmt_string cmtprojectpath = Symbol::get_env_value ("CMTPROJECTPATH");
  cmt_string sep;
  sep = CmtSystem::path_separator ();

  CmtSystem::cmt_string_vector items;
  CmtSystem::split (cmtprojectpath, sep, items);

  bool found = false;

  for (int i = 0; i < items.size (); i++)
    {
      const cmt_string& item = items[i];
      cmt_string p = item;
      p += CmtSystem::file_separator ();
      p += name;
      if (new_release != "")
	{
	  p += CmtSystem::file_separator ();
	  p += new_release;
	}

      if (CmtSystem::test_directory (p))
	{
	  //cerr << "Project directory " << p << " exists " << endl;

	  found = true;

	  IProjectFactory& factory = ProjectFactory::instance ();

	  factory.create_project (name, p, "ProjectPath", this);

	  break;
	}
    }

  if (!found)
    {
      Project* p = Project::find_by_name (name);

      if (p != 0)
	{
	  found = true;
	  p->add_parent (this);
	  add_child (p);

	  update_strategies_from_children ();
	}
    }
    
  if (!found && (cmtprojectpath != ""))
    {
      CmtMessage::warning ("Project " + name + " " + release + " requested by " + m_name + " not found in CMTPROJECTPATH");
      //      cerr << "#CMT> Project " << name << " " << release << " requested by " << m_name << " not found in CMTPROJECTPATH" << endl;
    }
}

//---------------------------------------------------------
bool Project::select_release(const cmt_string& name, const cmt_string& release, cmt_string& result)
{
  cmt_string selected_release = "";

  int v = -1;
  int r = -1;
  int p = -1;
  CmtSystem::is_version_directory (release, v, r, p); 

  int selected_v = -1;
  int selected_r = -1;
  int selected_p = -1;
  CmtSystem::cmt_string_vector releases = get_project_releases(name);
  for (int j = 0; j < releases.size (); j++)
    {
      int new_v = -1;
      int new_r = -1;
      int new_p = -1;
      CmtSystem::is_version_directory (releases[j], new_v, new_r, new_p);
      if (v == -1)
	{
	  if (selected_v < new_v)
	    {
	      selected_v = new_v;
	      selected_r = -1;
	      selected_p = -1;
	      selected_release = releases[j];
	    }
	  else if (selected_v == new_v)
	    {
	      // We compare the r
	      if (selected_r < new_r)
		{
		  selected_r = new_r;
		  selected_p = -1;
		  selected_release = releases[j];                               
		}
	      else if (selected_r == new_r)
		{
		  // We compare the p
		  if (selected_p < new_p)
		    {
		      selected_p = new_p;
		      selected_release = releases[j];                               
		    }
		  // else if ? not possible                                                           
		}                               
	    }
	}
      else if (r == -1)
	{
	  if (v == new_v)
	    {
	      // We compare the r                                                     
	      if (selected_r < new_r)
		{
		  selected_r = new_r;
		  selected_p = -1;
		  selected_release = releases[j];                               
		}
	      else if (selected_r == new_r)
		{ 
		  // We compare the p
		  if (selected_p < new_p)
		    {
		      selected_p = new_p;
		      selected_release = releases[j];                               
		    }
		  // else if ? not possible
		}
	    }
	}
      else if (p == -1)
	{
	  if ((v == new_v) && (r == new_r))
	    { 
	      // We compare the p
	      if (selected_p < new_p)
		{
		  selected_p = new_p;
		  selected_release = releases[j];                               
		}
	      // else if ? not possible
	    }        
	}       

      //cerr << "v:" << new_v << ", r:" << new_r << ", p:" << new_p << endl;             
      //cerr << "req v:" << v << ", req r:" << r << ", req p:" << p << endl;        

    }

  // cerr << "selected release: " << selected_release << endl;  

  if (selected_release == "") return false;

  result = selected_release;   

  return (true);
}

//---------------------------------------------------------
const CmtSystem::cmt_string_vector Project::get_project_releases (const cmt_string& name) const
{
  CmtSystem::cmt_string_vector releases;
  cmt_string cmtprojectpath = Symbol::get_env_value ("CMTPROJECTPATH");

  static cmt_string sep = CmtSystem::path_separator ();

  CmtSystem::cmt_string_vector items;
  CmtSystem::split (cmtprojectpath, sep, items);    

  for (int i = 0; i < items.size (); i++)
    {
      const cmt_string& item = items[i];
      cmt_string p = item;
      p += CmtSystem::file_separator ();
      p += name;

      if (CmtSystem::test_directory (p))
	{ 
	  CmtSystem::cmt_string_vector directories;
	  CmtSystem::scan_dir (p, directories);

	  for (int j = 0; j < directories.size (); j++)
	    {
	      if  (CmtSystem::test_directory(directories[j]))
		{
		  cmt_string release;
		  CmtSystem:: basename(directories[j], release);

		  if (CmtSystem::is_version_directory(release))
		    {	                           
		      cmt_string& name_entry = releases.add ();
		      name_entry = release; 
		    }             
		}	                    
	    }       	                
	}
    }
  return (releases);
}

//----------------------------------------------------------
Project& Project::operator = (const Project& other)
{
  m_name = other.m_name;
  m_cmtpath = other.m_cmtpath;
  m_cmtpath_real = other.m_cmtpath_real;
  m_cmtpath_pwd = other.m_cmtpath_pwd;
  m_cmtpath_source = other.m_cmtpath_source;

  return (*this);
}

//----------------------------------------------------------
bool Project::operator == (const cmt_string& name) const
{
  return ((m_name == name));
}

//----------------------------------------------------------
bool Project::operator != (const cmt_string& name) const
{
  return ((m_name != name));
}

//----------------------------------------------------------
void Project::show (PrintMode mode)
//void Project::show ()
{
  if (m_visited) return;
  m_visited = true;

  static int level = 0;

  //  bool is_current = false;

  //  cmt_string here = CmtSystem::pwd ();
  // In case there are symlinks
  /*
  cmt_string here_real;
  CmtSystem::realpath_ (CmtSystem::pwd (), here_real);

  //  if (here.find (m_cmtpath) == 0) 
  if (here_real.find (m_cmtpath_real) == 0) 
    {
      if (m_cmtpath_source != "default path")
	{
	  is_current = true;
	}
    }
  */
  switch (mode)
    {
    case Xml :
      cout << "<project" << (m_is_current ? " current=\"yes\"" : "") << ">";
      //      if (m_order >= 0)
      cout << "<order>" << m_order << "</order>";
      cout << "<name>" << m_name << "</name><version>" << (m_name != "CMTUSERCONTEXT" && m_name != "CMTHOME" ? m_release : "v0") << "</version><cmtpath>" << m_cmtpath << "</cmtpath>";
      break;
    default :
  for (int tab = 0; tab < level; tab++) cout << "  ";
  cout << m_name << " " << (m_name != "CMTUSERCONTEXT" && m_name != "CMTHOME" ? m_release : "v0") << " (in " << m_cmtpath << ")";
  //  cout << m_name << " " << m_release << " (in " << m_cmtpath << ")";

  if (m_is_current) cout << " (current)";
  //  if (is_current) cout << " (current)";
      break;
    }

  int i;

  switch (mode)
    {
    case Xml :
      cout << "<clients>";
      break;
    default:
      break;
    }
  for (i = 0; i < m_parents.size (); i++)
    {
      Project* p = m_parents[i];
      if (p == 0) continue;
      switch (mode)
	{
	case Xml :
	  //	  cout << "<name>" << p->get_name () << "</name>";
	  cout << "<project" << (p->is_current () ? " current=\"yes\"" : "") << ">";
	  //  if (p->m_order >= 0)
	  cout << "<order>" << p->m_order << "</order>";
	  cout << "<name>" << p->get_name () << "</name><version>" << (p->get_name () != "CMTUSERCONTEXT" && p->get_name () != "CMTHOME" ? p->get_release () : "v0") << "</version><cmtpath>" << p->get_cmtpath () << "</cmtpath>";
	  cout << "</project>";
	  break;
	default :
      cout << " P=" << p->get_name ();
	  break;
	}
    }
  switch (mode)
    {
    case Xml :
      cout << "</clients>";
      break;
    default:
      break;
    }

  switch (mode)
    {
    case Xml :
      cout << "<uses>";
      break;
    default:
      break;
    }
  for (i = 0; i < m_children.size (); i++)
    {
      Project* p = m_children[i];
      if (p == 0) continue;
      switch (mode)
	{
	case Xml :
	  //	  cout << "<name>" << p->get_name () << "</name>";
	  cout << "<project" << (p->is_current () ? " current=\"yes\"" : "") << ">";
	  //	  if (p->m_order >= 0)
	  cout << "<order>" << p->m_order << "</order>";
	  cout << "<name>" << p->get_name () << "</name><version>" << (p->get_name () != "CMTUSERCONTEXT" && p->get_name () != "CMTHOME" ? p->get_release () : "v0") << "</version><cmtpath>" << p->get_cmtpath () << "</cmtpath>";
	  cout << "</project>";
	  break;
	default :
      cout << " C=" << p->get_name ();
	  break;
	}
    }
  switch (mode)
    {
    case Xml :
      cout << "</uses>";
      cout << "</project>";
      break;
    default :
  cout << endl;
      break;
    }


/*
  if (m_visited) return;

  m_visited = true;
*/

  for (i = 0; i < m_children.size (); i++)
    {
      Project* p = m_children[i];
      if (p == 0) continue;
      level++;
      p->show (mode);
      //      p->show ();
      level--;
    }
}


//----------------------------------------------------------
void Project::show_specified_strategies () const
{
  int i;

  for (i = 0; i < m_strategies.size (); i++)
    {
      const Strategy& s = m_strategies[i];
      if (s.m_specified)
	{
	  const StrategyDef* def = s.m_definition;
 
	  cout << "# Project " << m_name
	       << " sets " << def->m_keyword
	       << " strategy to " << ((s.m_value) ? def->m_on_value : def->m_off_value);

	  if (s.m_context != "")
	    {
	      cout << " (from package " << s.m_context << ")";
	    }

	  cout << endl;
	}
    }
}

//----------------------------------------------------------
bool Project::has_strategy (const StrategyDef* definition) const
{
  int i;

  for (i = 0; i < m_strategies.size (); i++)
    {
      const Strategy& s = m_strategies[i];
      if (s.m_definition == definition)
	{
	  return (true);
	}
    }

  return (false);
}

//----------------------------------------------------------
bool Project::get_strategy (const cmt_string& name) const
{
  static StrategyMgr& mgr = StrategyMgr::instance ();

  StrategyDef* def = mgr.find_strategy (name);
  if (def == 0)
    {
      CmtMessage::warning ("strategy " + name + " undefined");
      //      cerr << "#CMT> strategy " << name << " undefined" << endl;
      return (false);
    }

  return (get_strategy (def));
}

//----------------------------------------------------------
bool Project::is_specified (const StrategyDef* definition) const
{
  int i;

  for (i = 0; i < m_strategies.size (); i++)
    {
      Strategy& s = m_strategies[i];
      if (s.m_definition == definition)
	{
	  // This strategy is applied in this project
	  return (s.m_specified);
	}
    }

  // This strategy is not applied in this project
  return (false);
}

//----------------------------------------------------------
bool Project::get_strategy (const StrategyDef* def) const
{
  int i;

  for (i = 0; i < m_strategies.size (); i++)
    {
      Strategy& s = m_strategies[i];
      if (s.m_definition == def)
	{
	  // This strategy is applied in this project
	  if (s.m_specified)
	    {
	      return (s.m_specified_value);
	    }
	  return (s.m_value);
	}
    }

  // This strategy is not applied in this project
  return (def->m_default_value);
}

//----------------------------------------------------------
void Project::set_default_strategy (const cmt_string& name)
{
  static StrategyMgr& mgr = StrategyMgr::instance ();

  StrategyDef* def = mgr.find_strategy (name);
  if (def == 0)
    {
      CmtMessage::warning ("strategy " + name + " undefined");
      //      cerr << "#CMT> strategy " << name << " undefined" << endl;
      return;
    }

  update_strategy (def, def->m_default_value);
}


//----------------------------------------------------------
void Project::set_strategy (const cmt_string& name, const cmt_string& value, const cmt_string& context)
{
  static StrategyMgr& mgr = StrategyMgr::instance ();

  StrategyDef* def = mgr.find_strategy (name);
  if (def == 0)
    {
      CmtMessage::warning ("strategy " + name + " undefined");
      //      cerr << "#CMT> strategy " << name << " undefined" << endl;
      return;
    }

  bool b_value = false;

  if (value == def->m_on_value)
    {
      b_value = true;
    }
  else if (value == def->m_off_value)
    {
      b_value = false;
    }
  else
    {
      CmtMessage::warning ("requested strategy value " + value + " undefined in strategy " + name);
      //      cerr << "#CMT> requested strategy value " << value << " undefined in strategy " << name << endl;
      return;
    }

  set_strategy (def, b_value, context);
}

//----------------------------------------------------------
void Project::set_strategy (StrategyDef* definition, bool b_value, const cmt_string& context)
{
  bool need_strategy = true;

  int i;

  for (i = 0; i < m_strategies.size (); i++)
    {
      Strategy& s = m_strategies[i];
      if (s.m_definition == definition)
	{
	  // This strategy is already applied in this project. Let's change it's value
	  s.set (definition, b_value, get_name ());
	  if (context != "")
	    {
	      if (s.m_context != "") s.m_context += " ";
	      s.m_context += context;
	    }
	  need_strategy = false;
	  break;
	}
    }

  if (need_strategy)
    {
      // This strategy is not yet applied in this project.

      Strategy& s = m_strategies.add ();
      s.clear ();
      s.set (definition, b_value, get_name ());
      s.m_context = context;
    }
  
  for (i = 0; i < m_parents.size (); i++)
    {
      Project* project = m_parents[i];

      project->update_strategy (definition, b_value);
    }

  const ProjectPtrVector& OrderedProjects = Project::ordered_projects ();
  for (int i = OrderedProjects.size () - 2; i >= 0; i--)
    {
      if (this != OrderedProjects[i + 1]) continue;
      if (!OrderedProjects[i]->has_parents () &&
	  OrderedProjects[i]->get_children_size () == 0)
	{
	  OrderedProjects[i]->update_strategy (definition, b_value);
	}
      break;
    }
}

/**----------------------------------------------------------
   The strategy value is changed because of indirect influences
   - default strategy at initialization time
   - change in the children
   - change in the children list

   (This is not a specification : see the set_strategy method)
*/
void Project::update_strategy (StrategyDef* definition, bool b_value)
{
  bool need_strategy = true;
  bool specified = false;

  int i;

  for (i = 0; i < m_strategies.size (); i++)
    {
      Strategy& s = m_strategies[i];
      if (s.m_definition == definition)
	{
	  need_strategy = false;

	  if (!s.m_specified)
	    {
	      // This strategy is already applied in this project. Let's change it's value
	      s.update (definition, b_value, get_name ());
	    }
	  else
	    {
	      specified = true;
	    }
	  break;
	}
    }

  if (need_strategy)
    {
      // This strategy is not yet applied in this project.

      Strategy& s = m_strategies.add ();
      s.clear ();
      s.update (definition, b_value, get_name ());
    }

  if (!specified)
    {
      for (i = 0; i < m_parents.size (); i++)
	{
	  Project* project = m_parents[i];

	  project->update_strategy (definition, b_value);
	}

      const ProjectPtrVector& OrderedProjects = Project::ordered_projects ();
      for (int i = OrderedProjects.size () - 2; i >= 0; i--)
	{
	  if (this != OrderedProjects[i + 1]) continue;
	  if (!OrderedProjects[i]->has_parents () &&
	      OrderedProjects[i]->get_children_size () == 0)
	    {
	      OrderedProjects[i]->update_strategy (definition, b_value);
	    }
	  break;
	}
    }
}

/**----------------------------------------------------------
   At least one of the children has changed this strategy
   Or the list of children has changed.
   We need to update the strategy value accordingly
   This will not change the specified value for this strategy
*/
void Project::update_strategy_from_children (StrategyDef* definition)
{
  // If this strategy is specified we don't care what happens from the children

  //cerr << "Updating strategy " << definition->m_name << " from children for project " << m_name << endl;

  int i;

  for (i = 0; i < m_strategies.size (); i++)
    {
      Strategy& s = m_strategies[i];
      if (s.m_definition == definition)
	{
	  // This strategy is applied in this project.

	  if (s.m_specified)
	    {
	      // There will be no impact since the strategy is specified

	      //cerr << "This strategy is specified in this project" << endl;
	      return;
	    }

	  break;
	}
    }

  // The strategy is not specified locally so we will now figure out
  // which strategy has to be considered from the mixture of specifications
  // from all children.

  // Algorithm:
  // - We consider children by pairs
  // - a child that specifies its strategy wins over a child that does not
  // - when the two children have the same level of priority we consider the priority value

  Project* selected = 0;
  bool selected_is_specified = false;
  bool selected_value = definition->m_default_value;

  for (i = 0; i < m_children.size (); i++)
    {
      Project* p = m_children[i];

      //cerr << "Checking strategy for child " << p->get_name () << endl;

      bool is_specified = p->is_specified (definition);
      bool value = p->get_strategy (definition);

      if (selected == 0)
	{
	  selected = p;
	  selected_is_specified = is_specified;
	  selected_value = value;
	  continue;
	}

      if (is_specified == selected_is_specified)
	{
	  if (selected_value != value)
	    {
	      // same level of priority but different values -> we must decide
	      bool priority_value = definition->m_priority_value;
	      if (value == priority_value)
		{
		  selected = p;
		  selected_is_specified = is_specified;
		  selected_value = value;
		}
	    }
	}
      else
	{
	  if (is_specified)
	    {
	      selected = p;
	      selected_is_specified = is_specified;
	      selected_value = value;
	    }
	}
    }

  update_strategy (definition, selected_value);  
}

/**----------------------------------------------------------
   At least one of the children has changed its strategies
   Or the list of children has changed.
   We need to update the strategy values accordingly
   This will not change the specified values
*/
void Project::update_strategies_from_children ()
{
  StrategyDef::StrategyDefs& defs = StrategyMgr::get_definitions ();

  //cerr << "Updating strategies from children for project " << m_name << endl;

  int i;

  for (i = 0; i < defs.size (); i++)
    {
      StrategyDef* def = defs[i];
      
      update_strategy_from_children (def);
    }

  for (i = 0; i < m_parents.size (); i++)
    {
      Project* p = m_parents[i];
      p->update_strategies_from_children ();
    }
}

/**----------------------------------------------------------
   The StrategyMgr singleton
*/
StrategyMgr& StrategyMgr::instance ()
{
  static StrategyMgr me;
  return (me);
}

/**----------------------------------------------------------
   The StrategyMgr constructor
   Here are primarily constructed all strategy definitions
*/
StrategyMgr::StrategyMgr ()
{
  m_defs.clear ();

  StrategyDef* s;

  s = new StrategyDef;
  s->m_keyword = "build";
  s->m_name = "BuildPrototypes";
  s->m_on_value = "prototypes";
  s->m_off_value = "no_prototypes";
  s->m_default_value = true;
  s->m_priority_value = false;

  m_defs.push_back (s);

  s = new StrategyDef;
  s->m_keyword = "build";
  s->m_name = "InstallArea";
  s->m_on_value = "with_installarea";
  s->m_off_value = "without_installarea";
  s->m_default_value = false;
  s->m_priority_value = true;

  m_defs.push_back (s);

  s = new StrategyDef;
  s->m_keyword = "setup";
  s->m_name = "SetupConfig";
  s->m_on_value = "config";
  s->m_off_value = "no_config";
  s->m_default_value = true;
  s->m_priority_value = false;

  m_defs.push_back (s);

  s = new StrategyDef;
  s->m_keyword = "setup";
  s->m_name = "SetupRoot";
  s->m_on_value = "root";
  s->m_off_value = "no_root";
  s->m_default_value = true;
  s->m_priority_value = false;

  m_defs.push_back (s);

  s = new StrategyDef;
  s->m_keyword = "setup";
  s->m_name = "SetupCleanup";
  s->m_on_value = "cleanup";
  s->m_off_value = "no_cleanup";
  s->m_default_value = true;
  s->m_priority_value = false;

  m_defs.push_back (s);

  s = new StrategyDef;
  s->m_keyword = "setup";
  s->m_name = "SetupScripts";
  s->m_on_value = "scripts";
  s->m_off_value = "no_scripts";
  s->m_default_value = true;
  s->m_priority_value = false;

  m_defs.push_back (s);

  s = new StrategyDef;
  s->m_keyword = "structure";
  s->m_name = "VersionDirectory";
  s->m_on_value = "with_version_directory";
  s->m_off_value = "without_version_directory";
  if (Cmt::get_current_structuring_style () != without_version_directory)
    {
      s->m_default_value = true;
      s->m_priority_value = false;
    }
  else
    {
      s->m_default_value = false;
      s->m_priority_value = true;
    }

  m_defs.push_back (s);
}

/**----------------------------------------------------------
   Find a strategy definition by its name
*/
StrategyDef* StrategyMgr::find_strategy (const cmt_string& name)
{
  static StrategyMgr& me = instance ();

  int i;

  for (i = 0; i < me.m_defs.size (); i++)
    {
      StrategyDef* def = me.m_defs[i];
      if (def->m_name == name)
	{
	  return (def);
	}
    }

  return (0);
}

/**----------------------------------------------------------
   Retreive the default value defined for a given strategy
*/
bool StrategyMgr::get_default_strategy (const cmt_string& name)
{
  StrategyDef* def = find_strategy (name);
  if (def == 0) return (false);
  return (def->m_default_value);
}

/**----------------------------------------------------------
   Retreive the priority value defined for a given strategy
   This value is used when two children of a project request two conflicting strategy values
*/
bool StrategyMgr::get_priority_strategy (const cmt_string& name)
{
  StrategyDef* def = find_strategy (name);
  if (def == 0) return (false);
  return (def->m_priority_value);
}

/**----------------------------------------------------------
   Return the vector of all existing strategy definitions
*/
StrategyDef::StrategyDefs& StrategyMgr::get_definitions ()
{
  static StrategyMgr& me = instance ();

  return (me.m_defs);
}

//-----------------------------------------------------------
Strategy::Strategy ()
{
  clear ();
}

//-----------------------------------------------------------
void Strategy::clear ()
{
  m_definition = 0;
  m_specified = false;
  m_specified_value = false;
  m_value = false;
  m_on_tag = 0;
  m_off_tag = 0;
}

/**----------------------------------------------------------
   Specify a new value for this strategy.
   This only happens when a strategy statement is met in a project file or in a requirements file.
*/
void Strategy::set (StrategyDef* definition, bool value, const cmt_string& project_name)
{
  //cerr << "Setting strategy " << definition->m_name << " for project " << project_name << " to " << value << endl;

  m_definition = definition;
  m_specified = true;
  m_specified_value = value;

  update (definition, value, project_name);
}

/**----------------------------------------------------------
   Change the effective value for this strategy.
   This has no impact on to the specified value.
   This will adapt the tag settings
*/
void Strategy::update (StrategyDef* definition, bool value, const cmt_string& project_name)
{
  //cerr << "Updating strategy " << definition->m_name << " for project " << project_name << " to " << value << endl;

  m_value = value;
  m_definition = definition;

  cmt_string to_tag_name = project_name;
  cmt_string to_untag_name = project_name;

  to_tag_name += "_";
  to_untag_name += "_";

  if (m_value)
    {
      to_tag_name += m_definition->m_on_value;
      to_untag_name += m_definition->m_off_value;
    }
  else
    {
      to_tag_name += m_definition->m_off_value;
      to_untag_name += m_definition->m_on_value;
    }

  m_on_tag = Tag::find (to_tag_name);
  m_off_tag = Tag::find (to_untag_name);

  if (m_on_tag == 0 || m_off_tag == 0)
    {
      m_on_tag = Tag::add (to_tag_name, PriorityConfig, "PROJECT", 0);
      m_off_tag = Tag::add (to_untag_name, PriorityConfig, "PROJECT", 0);

      m_on_tag->add_tag_exclude (m_off_tag);
      m_off_tag->add_tag_exclude (m_on_tag);
    }

  m_off_tag->unmark ();
  m_on_tag->mark ("PROJECT");
}

//-----------------------------------------------------------
const cmt_string& StrategyDef::get_default_value () const
{
  if (m_default_value)
    {
      return (m_on_value);
    }
  else
    {
      return (m_off_value);
    }
}

//-----------------------------------------------------------
void Project::set_author (const cmt_string& name)
{
  // cerr << "set_author" << name << endl;      
  this->m_author = name;
}

//-----------------------------------------------------------
const cmt_string& Project::get_author () const
{
  return (m_author);
}

//-----------------------------------------------------------
void Project::project_author_action (const CmtSystem::cmt_string_vector& words)
{
  if (m_author != "") m_author += "\n";

  for (int i = 1; i < words.size (); i++)
    {
      const cmt_string& w = words[i];
      
      if (i > 1) m_author += " ";
      m_author += w;  
    }
}

//-----------------------------------------------------------
Use*  Project::get_use () const
{
  return m_use;    
}  

//-----------------------------------------------------------
void  Project::set_use (Use* use)
{  
  this->m_use = use;
}

//-----------------------------------------------------------
