//-----------------------------------------------------------
// 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_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"

class ProjectReader : public Awk
{
public:

  ProjectReader ()
  {
  }
  
  const cmt_string& get_project_name () const
  {
    return (m_project);
  }
  
  void filter (const cmt_string& line)
  {
    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)
  {
    if (m_output != "")
      {
	m_output += "\n";
      }
    
    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;
      }
  }
  
private:
  cmt_string m_output;
  const cmt_string& m_project;
};

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

void ProjectFactory::reset ()
{
  Project::clear_all ();
  m_first = 0;
  m_previous = 0;
  m_id = 0;
}

/*
  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& path,
					 const cmt_string& source,
					 Project* parent)
{
  cmt_string compressed_path = path;
  CmtSystem::compress_path (compressed_path);

  static Project::ProjectVector& Projects = Project::projects ();
  
  for (int i = 0; i < Projects.size (); i++)
    {
      Project& p = Projects[i];
      
      if ((p.get_cmtpath () == compressed_path) ||
	  (p.get_cmtpath_pwd () == compressed_path))
	{
	  if (parent != 0)
	    {
	      p.add_parent (parent);
	      parent->add_child (&p);
	    }
	  return (&p);
	}
    }
  
  Project* project = 0;
  Project* cmt = 0;
  
  bool is_current = false;
  
  cmt_string here = CmtSystem::pwd ();
  if (!CmtSystem::cd (compressed_path)) return (0);

  cmt_string name;
  cmt_string release;
  CmtSystem::basename (compressed_path, release);

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

  cmt_string text;

  /*
    Now Figure out the project name from the project file
    Or construct an automatic project name when project file does no exist
    or does not specify the project name
   */
  if (CmtSystem::cd ("cmt") && CmtSystem::test_file (Project::get_project_file_name ()))
    {
      text.read (Project::get_project_file_name ());

      ProjectReader reader;

      reader.run (text);

      name = reader.get_project_name ();
      if (name == "")
	{
	  CmtSystem::dirname (compressed_path, name);
	  CmtSystem::basename (name, name);

	  /*
	  char buffer[20];
	  m_id++;
	  sprintf (buffer, "Project%d", m_id);
	  name = buffer;
	  */
	}
    }
  else
    {
      char buffer[20];
      if (source == "default path")
	{
	  strcpy (buffer, "CMT");
	  name = buffer;
	}
      else
	{
	  CmtSystem::dirname (compressed_path, name);
	  CmtSystem::basename (name, name);
	  //m_id++;
	  //sprintf (buffer, "Project%d", m_id);
	}
    }

  project = Project::add (name, release);

  if (parent != 0)
    {
      project->add_parent (parent);
      parent->add_child (project);
    }

  if (source == "default path")
    {
      cmt = project;
      is_current = false;
    }
  else
    {
      if (m_first == 0)
	{
	  m_first = project;
	}
    }

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

  if (is_current)
    {
      //
      // The current project defines a tag with its name
      //

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

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

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

  CmtSystem::cd (here);

  return (project);
}

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

//----------------------------------------------------------
void Project::create (const cmt_string& name)
{
  cout << "------------------------------------------" << endl;
  cout << "Configuring environment for project " << name << endl;
  cout << "CMT version " << Cmt::get_cmt_version () << "." << endl;
  cout << "------------------------------------------" << endl;

  if (!CmtSystem::test_directory ("cmt"))
    {
      if (!CmtSystem::mkdir ("cmt"))
        {
          cout << "Cannot create the cmt directory" << endl;
          return;
        }
      else
        {
          cout << "Installing the cmt directory" << endl;
        }
    }

  CmtSystem::cd ("cmt");

  if (!CmtSystem::test_file (get_project_file_name ()))
    {
      cout << "Creating a new project file" << endl;

      ofstream f (get_project_file_name ());
      if (f)
        {
          f << "project " << name << endl;
          f << endl;
          f.close ();
        }
    }
  else
    {
      cmt_string text;
      text.read (get_project_file_name ());

      ProjectPatcher p (name);

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

      cout << "project file already there" << endl;
    }
}

//----------------------------------------------------------
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_pwd == compressed_path) return (&p);
    }

  return (0);
}

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

  static ProjectVector& Projects = projects ();

  Project* result = 0;

  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) 
	{
	  result = &p;
	}
    }

  return (result);
}

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

  //cout << "Project::add> name=" << name << endl;

  {
    Project* project;

    project = find_by_name (name);
    if (project != 0) 
      {
	if (!Cmt::get_quiet ())
	  {
	    if (release != project->get_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.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);
}

/*----------------------------------------------------------*/
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 ()
{
  /*
  static ProjectVector& Projects = projects ();

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

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

  Project* current = get_current ();

  if (current != 0) current->show ();
}

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 post (Project* p)
  {
  }
};

/*----------------------------------------------------------*/
void Project::show_paths ()
{
  VisitorForShowPaths visitor;

  start_visit (visitor);
}

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

  return (name);
}

//----------------------------------------------------------
void Project::fill_selection (int depth, CmtSystem::cmt_string_vector& path_selections)
{
  static ProjectVector& Projects = projects ();

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

      const cmt_string& p = project.get_cmtpath ();
      const cmt_string& pwd = project.get_cmtpath_pwd ();
      const cmt_string& src = project.get_cmtpath_source ();

      if (src != "default path")
	{
	  if (depth > 0)
	    {
	      cmt_string& s1 = path_selections.add ();
	      s1 = p;
	      cmt_string& s2 = path_selections.add ();
	      s2 = pwd;
	      depth--;

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

//----------------------------------------------------------
void Project::broadcast (IProjectAction& action)
{
  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)
{
  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 ();

  for (int 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 ();

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

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

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

  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& w = project.m_cmtpath_pwd;
      const cmt_string& s = project.m_cmtpath_source;

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

      if (CmtSystem::test_directory (p))
	{
	  if (path.find (p) != cmt_string::npos)
	    {
	      return (p);
	    }

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

      if (p == w) continue;

      if (CmtSystem::test_directory (w))
	{
	  if (path.find (w) != 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::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;
    }

  Project* p = get_current ();

  if (p == 0) return;

  visitor.pre (p);
  p->visit (visitor);
  visitor.post (p);
}

//----------------------------------------------------------
class VisitorForFillCMTPATH : public IProjectVisitor
{
public:
  VisitorForFillCMTPATH (cmt_string& buffer) : m_buffer (buffer)
  {
  }

  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 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);
}

//----------------------------------------------------------
Project::Project () : m_name ("")
{
  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_cmtpath () const
{
  return (m_cmtpath);
}

//----------------------------------------------------------
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_cmtpath (const cmt_string& path)
{
  m_cmtpath = 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::clear ()
{
  m_name = "";
  m_release = "";
  m_cmtpath = "";
  m_cmtpath_pwd = "";
  m_cmtpath_source = "";

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

  m_build_strategy_mask = 0;
  m_build_strategy = DefaultBuildStrategy;
  m_setup_strategy_mask = 0;
  m_setup_strategy = DefaultSetupStrategy;

  m_configured = false;

  m_prototypes_tag = 0;
  m_no_prototypes_tag = 0;
  m_with_installarea_tag = 0;
  m_without_installarea_tag = 0;
  
  m_setup_config_tag = 0;
  m_setup_no_config_tag = 0;
  m_setup_root_tag = 0;
  m_setup_no_root_tag = 0;
  m_setup_cleanup_tag = 0;
  m_setup_no_cleanup_tag = 0;
}

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

  int i;

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

      if (child->get_name () == p->get_name ())
	{
	  // Already registered as a child (no cycle permitted!!)
	  return;
	}
    }

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

      if (parent->get_name () == p->get_name ())
	{
	  // Already registered
	  return;
	}
    }

  m_parents.push_back (p);

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

  if (m_setup_strategy_mask != 0)
    {
      p->set_setup_strategy (m_setup_strategy_mask, m_setup_strategy & PrototypesMask);
      p->set_setup_strategy (m_setup_strategy_mask, m_setup_strategy & InstallAreaMask);
    }

  if (m_build_strategy_mask != 0)
    {
      p->set_build_strategy (m_build_strategy_mask, m_build_strategy & SetupConfigMask);
      p->set_build_strategy (m_build_strategy_mask, m_build_strategy & SetupRootMask);
      p->set_build_strategy (m_build_strategy_mask, m_build_strategy & SetupCleanupMask);
    }
}

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

  int i;

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

      if (parent->get_name () == p->get_name ())
	{
	  // Already registered as a parent (no cycle permitted!!)
	  return;
	}
    }

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

      if (child->get_name () == p->get_name ())
	{
	  // Already registered
	  return;
	}
    }

  m_children.push_back (p);
}

//----------------------------------------------------------
void Project::configure ()
{
  /*

  <project>_prototypes
  <project>_no_prototypes
  <project>_rebuild_makefile
  <project>_keep_makefile
  <project>_with_installarea
  <project>_without_installarea

  <project>_setup_config
  <project>_setup_no_config
  <project>_setup_root
  <project>_setup_no_root
  <project>_setup_cleanup
  <project>_setup_no_cleanup

  */

  if (m_configured) return;
  m_configured = true;

  cmt_string tag_name;

  tag_name = m_name;
  tag_name += "_prototypes";
  m_prototypes_tag = Tag::add (tag_name, PriorityConfig, "PROJECT", 0);
  
  tag_name = m_name;
  tag_name += "_no_prototypes";
  m_no_prototypes_tag = Tag::add (tag_name, PriorityConfig, "PROJECT", 0);
  
  m_prototypes_tag->add_tag_exclude (m_no_prototypes_tag);
  m_no_prototypes_tag->add_tag_exclude (m_prototypes_tag);
  m_prototypes_tag->mark ();
  
  tag_name = m_name;
  tag_name += "_with_installarea";
  m_with_installarea_tag = Tag::add (tag_name, PriorityConfig, "PROJECT", 0);
  
  tag_name = m_name;
  tag_name += "_without_installarea";
  m_without_installarea_tag = Tag::add (tag_name, PriorityConfig, "PROJECT", 0);
  
  m_with_installarea_tag->add_tag_exclude (m_without_installarea_tag);
  m_without_installarea_tag->add_tag_exclude (m_with_installarea_tag);
  m_without_installarea_tag->mark ();



  tag_name = m_name;
  tag_name += "_setup_config";
  m_setup_config_tag = Tag::add (tag_name, PriorityConfig, "PROJECT", 0);
  
  tag_name = m_name;
  tag_name += "_setup_no_config";
  m_setup_no_config_tag = Tag::add (tag_name, PriorityConfig, "PROJECT", 0);
  
  m_setup_config_tag->add_tag_exclude (m_setup_no_config_tag);
  m_setup_no_config_tag->add_tag_exclude (m_setup_config_tag);
  m_setup_config_tag->mark ();

  tag_name = m_name;
  tag_name += "_setup_root";
  m_setup_root_tag = Tag::add (tag_name, PriorityConfig, "PROJECT", 0);
  
  tag_name = m_name;
  tag_name += "_setup_no_root";
  m_setup_no_root_tag = Tag::add (tag_name, PriorityConfig, "PROJECT", 0);
  
  m_setup_root_tag->add_tag_exclude (m_setup_no_root_tag);
  m_setup_no_root_tag->add_tag_exclude (m_setup_root_tag);
  m_setup_root_tag->mark ();

  tag_name = m_name;
  tag_name += "_setup_cleanup";
  m_setup_cleanup_tag = Tag::add (tag_name, PriorityConfig, "PROJECT", 0);
  
  tag_name = m_name;
  tag_name += "_setup_no_cleanup";
  m_setup_no_cleanup_tag = Tag::add (tag_name, PriorityConfig, "PROJECT", 0);
  
  m_setup_cleanup_tag->add_tag_exclude (m_setup_no_cleanup_tag);
  m_setup_no_cleanup_tag->add_tag_exclude (m_setup_cleanup_tag);
  m_setup_cleanup_tag->mark ();

  if (Cmt::get_debug ())
    {
      cout << "Project::configure> " << m_name
	   << " " << m_prototypes_tag
	   << " " << m_no_prototypes_tag
	   << " " << m_with_installarea_tag
	   << " " << m_without_installarea_tag
	   << " " << m_setup_config_tag
	   << " " << m_setup_no_config_tag
	   << " " << m_setup_root_tag
	   << " " << m_setup_no_root_tag
	   << " " << m_setup_cleanup_tag
	   << " " << m_setup_no_cleanup_tag
	   << endl;
    }
}

//----------------------------------------------------------
void Project::use_action (const cmt_string& name, const cmt_string& release)
{
  //cerr << "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 : pi/<name>/<release>
  //   + when found, this should become a new CMTPATH entry
  //   +             the new project is then parsed ... etc...

  cmt_string cmtprojectpath = CmtSystem::getenv ("CMTPROJECTPATH");
  cmt_string sep;
  sep += CmtSystem::path_separator ();

  //cerr << "cmtprojectpath = " << cmtprojectpath << endl;
  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;
      p += CmtSystem::file_separator ();
      p += release;

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

	  IProjectFactory& factory = ProjectFactory::instance ();

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

	  break;
	}
    }
}


//----------------------------------------------------------
Project& Project::operator = (const Project& other)
{
  m_name = other.m_name;
  m_cmtpath = other.m_cmtpath;
  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 ()
{
  static int level = 0;

  bool is_current = false;

  cmt_string here = CmtSystem::pwd ();

  if (here.find (m_cmtpath) == 0) 
    {
      if (m_cmtpath_source != "default path")
	{
	  is_current = true;
	}
    }

  for (int tab = 0; tab < level; tab++) cout << "  ";
  cout << m_name << " " << m_release << " (in " << m_cmtpath << ")";

  if (is_current) cout << " (current)";

  int i;

  for (i = 0; i < m_parents.size (); i++)
    {
      Project* p = m_parents[i];
      if (p == 0) continue;
      cout << " P=" << p->get_name ();
    }

  for (i = 0; i < m_children.size (); i++)
    {
      Project* p = m_children[i];
      if (p == 0) continue;
      cout << " C=" << p->get_name ();
    }

  cout << endl;

  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 ();
      level--;
    }
}


//----------------------------------------------------------
void Project::show_strategies () const
{
}

//----------------------------------------------------------
int Project::get_build_strategy () const
{
  int result = 0;

  result = Cmt::get_current_build_strategy ();

  int i;

  bool request_non_default_prototypes = false;
  bool request_non_default_installarea = false;

  int m_prototypes = 0;
  int m_installarea = 0;

  for (i = 0; i < m_children.size (); i++)
    {
      const Project* p = m_children[i];
      int s = p->get_build_strategy ();

      int s_prototypes = s & ~PrototypesMask;

      if (s_prototypes != DefaultPrototypesStrategy)
	{
	  // keep it. At least one of the children has specified a non-default strategy
	  request_non_default_prototypes = true;
	  m_prototypes = s_prototypes;
	}

      int s_installarea = s & ~InstallAreaMask;

      if (s_installarea != DefaultInstallAreaStrategy)
	{
	  // keep it. At least one of the children has specified a non-default strategy
	  request_non_default_installarea = true;
	  m_installarea = s_installarea;
	}
    }

  if (m_build_strategy_mask != 0)
    {
      result &= ~m_build_strategy_mask;
      result |= m_build_strategy;
    }
  else
    {
      if (request_non_default_prototypes)
	{
	  result |= m_prototypes;
	}

      if (request_non_default_installarea)
	{
	  result |= m_installarea;
	}
    }

  return (result);
}

//----------------------------------------------------------
int Project::get_setup_strategy () const
{
  int result = 0;

  /*
  if (m_reference != 0) result = m_reference->get_setup_strategy ();
  else if (m_predecessor != 0) result = m_predecessor->get_setup_strategy ();
  else result = Cmt::get_current_setup_strategy ();
  */

  result = Cmt::get_current_setup_strategy ();

  if (m_setup_strategy_mask != 0)
    {
      result &= ~m_setup_strategy_mask;
      result |= m_setup_strategy;
    }

  return (result);
}

//----------------------------------------------------------
void Project::set_build_strategy (int mask, int strategy)
{
  m_build_strategy_mask |= mask;
  m_build_strategy &= ~mask;
  m_build_strategy |= strategy;

  cmt_string to_tag_name = m_name;
  cmt_string to_untag_name = m_name;

  Tag* to_tag = 0;
  Tag* to_untag = 0;

  switch (strategy)
    {
    case Prototypes:
      to_tag_name += "_prototypes";
      to_untag_name += "_no_prototypes";
      break;
    case NoPrototypes:
      to_tag_name += "_no_prototypes";
      to_untag_name += "_prototypes";
      break;
    case WithInstallArea:
      to_tag_name += "_with_installarea";
      to_untag_name += "_without_installarea";
      break;
    case WithoutInstallArea:
      to_tag_name += "_without_installarea";
      to_untag_name += "_with_installarea";
      break;
    }

  to_tag = Tag::find (to_tag_name);
  to_untag = Tag::find (to_untag_name);

  if (to_untag != 0)
    {
      to_untag->unmark ();
    }

  if (to_tag != 0)
    {
      to_tag->mark ();
    }
  
  for (int i = 0; i < m_parents.size (); i++)
    {
      Project* project = m_parents[i];

      if ((project->m_build_strategy_mask & mask) == 0)
	{
	  project->set_build_strategy (mask, strategy);
	}
    }
}

//----------------------------------------------------------
void Project::set_setup_strategy (int mask, int strategy)
{
  m_setup_strategy_mask |= mask;
  m_setup_strategy &= ~mask;
  m_setup_strategy |= strategy;

  cmt_string to_tag_name = m_name;
  cmt_string to_untag_name = m_name;

  Tag* to_tag = 0;
  Tag* to_untag = 0;

  switch (strategy)
    {
    case SetupConfig:
      to_tag_name += "_setup_config";
      to_untag_name += "_setup_no_config";
      break;
    case SetupNoConfig:
      to_tag_name += "_setup_no_config";
      to_untag_name += "_setup_config";
      break;
    case SetupRoot:
      to_tag_name += "_setup_root";
      to_untag_name += "_setup_no_root";
      break;
    case SetupNoRoot:
      to_tag_name += "_setup_no_root";
      to_untag_name += "_setup_root";
      break;
    case SetupCleanup:
      to_tag_name += "_setup_cleanup";
      to_untag_name += "_setup_no_cleanup";
      break;
    case SetupNoCleanup:
      to_tag_name += "_setup_no_cleanup";
      to_untag_name += "_setup_cleanup";
      break;
    }

  to_tag = Tag::find (to_tag_name);
  to_untag = Tag::find (to_untag_name);

  if (to_untag != 0)
    {
      to_untag->unmark ();
    }

  if (to_tag != 0)
    {
      to_tag->mark ();
    }
  
  for (int i = 0; i < m_parents.size (); i++)
    {
      Project* project = m_parents[i];

      if ((project->m_setup_strategy_mask & mask) == 0)
	{
	  project->set_setup_strategy (mask, strategy);
	}
    }
}

