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

static void show_packages ();

/*
 *   Design.
 *
 *   There is one central database of all Use objects. This in the Database using:
 *       Database::instance().get_instances ();
 *
 *   There is also a list of selected Use pointers available from
 *       Database::instance().get_ordered_uses ();
 *
 *   A new Use object is created when a new use statement requires it:
 *     o if the specified version is specified for the first time
 *     o if the specified path is specified for the first time
 *
 */

static bool add_request (const cmt_string& path,
			 const cmt_string& package_name,
			 const cmt_string& version)
{
  static cmt_map <cmt_string, bool> requests;

  cmt_string request = package_name;
  request += " ";
  request += version;
  request += " ";
  request += path;
  static bool b = true;

  bool new_request = false;

  if (!requests.has ((const cmt_string&) request))
    {
      new_request = true;
      requests.add ((const cmt_string&) request, b);
    }

  return (new_request);
}

class UseContext
{
public:

  static UseContext& current ()
  {
    static UseContext me;

    return (me);
  }

  UseContext ()
  {
    m_auto_imports = On;
  }

  UseContext (const UseContext& other)
  {
    m_auto_imports = other.m_auto_imports;
  }

  UseContext& operator = (const UseContext& other)
  {
    m_auto_imports = other.m_auto_imports;

    return (*this);
  }

  static void set_current (State auto_imports)
  {
    UseContext& c = current ();

    c.m_auto_imports = auto_imports;
  }

  static State get_current_auto_imports ()
  {
    UseContext& c = current ();

    return (c.m_auto_imports);
  }

private:
  State m_auto_imports;
};


class BestFitSelector
{
public:
  Use* operate (Use* ref_use, Use* new_use);
};

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

typedef enum
{
  IdenticalIds,

  IncompatibleMajorIds,
  NewMajorIdGreaterThanOld,
  OldMajorIdGreaterThanNew,
  ExplicitOldMajorIdWinsAgainstWildarded,
  ExplicitNewMajorIdWinsAgainstWildarded,

  ExplicitOldMinorIdWinsAgainstWildarded,
  ExplicitNewMinorIdWinsAgainstWildarded,
  NewMinorIdGreaterThanOld,

  ExplicitOldPatchIdWinsAgainstWildarded,
  ExplicitNewPatchIdWinsAgainstWildarded,
  NewPatchIdGreaterThanOld
} CompareStatus;

static CompareStatus compare_versions (const cmt_string& ref_version, const cmt_string& new_version)
{
  CompareStatus result = IdenticalIds;

  int old_v = -1;
  int old_r = -1;
  int old_p = -1;

  int new_v = -1;
  int new_r = -1;
  int new_p = -1;

  CmtSystem::is_version_directory (ref_version, old_v, old_r, old_p);

  CmtSystem::is_version_directory (new_version, new_v, new_r, new_p);

  if ((new_v != -1) && (old_v != -1) && (new_v != old_v))
    {
      /*
      if (!Cmt::get_quiet ())
	{
	  cout << "#1 Required version " << new_version <<
	    " incompatible with selected version " << ref_version <<
	    endl;
	}
      */

      if (new_v > old_v)
	{
	  result = NewMajorIdGreaterThanOld;
	}
      else
	{
	  result = OldMajorIdGreaterThanNew;
	}
      //result = IncompatibleMajorIds;
    }
  else if ((new_v == -1) || (old_v == -1))
    {
      //
      // we plan to discard new_use, but if it was specified as explicit 
      // and ref_use was wildcarded then new_use will win !!
      //
      // So then we'll have to understand where are the wild
      // cards... If they are on v or r, then we consider them.
      // 
      //


      if ((old_v == -1) && (new_v != -1))
	{
	  /*
	  if (!Cmt::get_quiet ())
	    {
	      cout << "#2 Select version " << new_version <<
		" instead of existing " << ref_version <<
		endl;
	    }
	  */

          result = ExplicitNewMajorIdWinsAgainstWildarded;
	}
      else
	{
	  /*
	  if (!Cmt::get_quiet ())
	    cout << "#3 keep version " << ref_version <<
	      " (ignore version " << new_version << ")" <<
	      endl;
	  */

          result = ExplicitOldMajorIdWinsAgainstWildarded;
	}
    }
  else if ((new_r == -1) || (old_r == -1) || (new_r < old_r))
    {
      //
      // we plan to discard new_use, but if it was specified as explicit 
      // and ref_use was wildcarded then new_use will win !!
      //
      // So then we'll have to understand where are the wild
      // cards... If they are on v or r, then we consider them.
      // 
      //


      if ((old_r == -1) && (new_r != -1))
	{
	  // old has wild card and new has not => new wins

	  /*
	  if (!Cmt::get_quiet ())
	    {
	      cout << "#4 Select release " << new_version
		   << " instead of existing " << ref_version
		   << endl;
	    }
	  */

          result = ExplicitNewMinorIdWinsAgainstWildarded;
	}
      else
	{
	  /*
	  if (!Cmt::get_quiet ())
	    cout << "#5 keep release " << ref_version <<
	      " (ignore release " << new_version << ")" <<
	      endl;
	  */

          result = ExplicitOldMinorIdWinsAgainstWildarded;
	}
    }
  else if (new_r > old_r)
    {
      /*
      if (!Cmt::get_quiet ())
        {
          cout << "#6 Select release " << new_version <<
            " instead of existing " << ref_version <<
            endl;
        }
      */
 
      result = NewMinorIdGreaterThanOld;
   }
  else if ((new_p == -1) || (old_p == -1) || (new_p < old_p))
    {
      //
      // we plan to discard new_use, but if it was specified as explicit 
      // and ref_use was wildcarded then new_use will win !!
      //


      if ((old_p == -1) && (new_p != -1))
	{
	  /*
	  if (!Cmt::get_quiet ())
	    {
	      cout << "#7 Select patch " << new_version <<
		" instead of existing " << ref_version <<
		endl;
	    }
	  */

	  result = ExplicitNewPatchIdWinsAgainstWildarded;
	}
      else
	{
	  /*
	  if (!Cmt::get_quiet ())
	    cout << "#8 keep patch " << ref_version <<
	      " (ignore version " << new_version << ")" <<
	      endl;
	  */

	  result = ExplicitOldPatchIdWinsAgainstWildarded;
	}
    }
  else if (new_p > old_p)
    {
      /*
      if (!Cmt::get_quiet ())
        {
          cout << "#9 Select patch " << new_version <<
            " instead of existing " << ref_version <<
            endl;
        }
      */

      result = NewPatchIdGreaterThanOld;
    }

  return (result);
}

//----------------------------------------------------------
//
//  Operations on Use
//
//----------------------------------------------------------

/**
 *  Mark all clients of the current package.
 */
void Use::select_clients (const cmt_string& package,
                          const cmt_string& version)
{
  static UsePtrVector& uses = get_ordered_uses ();

  int number;
  Use* use = 0;

  unselect_all ();
  undiscard_all ();

  for (number = uses.size () - 1; number >= 0; number--)
    {
      use = uses[number];
      if (use == 0) continue;
      if (use->is_selected ()) continue;
      use->select ();
      if (!use->is_client (package, version)) use->discard ();
    }
}

//----------------------------------------------------------
void Use::show_all (bool skip_discarded)
{
  show_all ("use ", skip_discarded);
}

//----------------------------------------------------------
void Use::show_cycles ()
{
  static int level = 0;
  static UsePtrVector stack;
  int size = 0;

  if (level == 0)
    {
      unselect_all ();
      unselect ();
      stack.clear ();
    }

  //for (int i = 0; i < level; i++) cout << "  ";
  //cout << "Use::show_cycles> " << level << " " << get_package_name () << endl;
  
  // Detect cycles.
  for (int k = 0; k < level; k++)
    {
      Use* u = stack[k];
      if (u == this) 
	{
	  for (;k < level; k++)
	    {
	      u = stack[k];
	      cout << u->get_package_name () << " ";
	    }

	  cout << endl;

	  return;
	}
    }

  // Save this to the stack.
  if (stack.size () <= level)
    {
      stack.push_back (this);
    }
  else
    {
      stack[level] = this;
    }

  if (selected) return;
  selected = true;

  size = sub_uses.size ();
  for (int n = 0; n < size; n++)
    {
      Use* use = sub_uses[n];
      
      if (use == 0) continue;
      
      //for (int i = 0; i < level; i++) cout << "  ";
      //cout << "Use::show_cycles> " << get_package_name () << " uses " << use->get_package_name () << endl;

      if (use->discarded)
	{
	  Use* u;
	  
	  u = use->get_selected_version ();
	  if (u == 0) continue;
	  
	  use = u;
	}
      
      level++;
      use->show_cycles ();
      level--;
    }
}

//----------------------------------------------------------
void Use::push_scope_section (ScopeType type)
{
  scope_sections.push_back (type);
}

//----------------------------------------------------------
void Use::pop_scope_section ()
{
  scope_sections.pop_back ();
}

//----------------------------------------------------------
void Use::close_scope_sections ()
{
  scope_sections.clear ();
}

//----------------------------------------------------------
ScopeType Use::get_current_scope () const
{
  if (scope_sections.size () == 0) return (initial_scope);
  const ScopeSection& s = scope_sections.back ();
  return (s.get_scope ());
}

//----------------------------------------------------------
void Use::show_all (const cmt_string& prefix, bool skip_discarded)
{
  static UsePtrVector& uses = get_ordered_uses ();

  Use* use;
  int number;

  if (Cmt::get_debug ())
    {
      cout << "use::show_all-1> ";
      
      int i;
      
      for (i = 0; i < uses.size (); i++)
	{
	  Use* u = uses[i];
	  cout << u->get_package_name () << "[" << u->get_index () << "]" << " ";
	}
      
      cout << endl;
    }

  unselect_all ();

  use = &(current ());
  use->unselect ();
  if (!Cmt::get_quiet ()) use->show_sub_uses ("", skip_discarded);

  if (uses.size () > 0)
    {
      if (!Cmt::get_quiet ())
        {
          cout << "#\n";
          cout << "# Selection :\n";
        }

      //
      //  First move the CMT package to the end of the use set.
      //  (ie. used by everybody)
      //
      use = Use::find ("CMT");
      Use::move (use);

      for (number = uses.size () - 1; number >= 0; number--)
        {
          use = uses[number];

          if (use->discarded) continue;

          if (!use->located ())
            {
              if (!Cmt::get_quiet ())
                {
                  cout << "# package " << use->get_package_name () <<
                      " " << use->version << " " << use->path << 
                      " not found" <<
                      endl;
                }
              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 << prefix << 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;
            }
        }

      if (Cmt::get_cmt_home () != "")
        {
          cout << prefix << CmtSystem::get_home_package () << " v0";
	  if (!Cmt::get_quiet ()) 
	    {
	      cout << " (" << Cmt::get_cmt_home () << ")";
	    }
	  cout << endl;
        }

      if (Cmt::get_cmt_user_context () != "")
        {
          cout << prefix << CmtSystem::get_user_context_package () << " v0";
	  if (!Cmt::get_quiet ()) 
	    {
	      cout << " (" << Cmt::get_cmt_user_context () << ")";
	    }
	  cout << endl;
        }
    }
}

/**
 *   This private class parses the use statement after macro expansion
 *   This is a finite state machine.
 *    It maintains the results of the parsing in terms of a package name,
 *   a version, a path, and state variables (no_auto_imports)
 */
class use_action_iterator
{
public:

  use_action_iterator ()
      {
        state = need_package;
        auto_imports = Unspecified;
      }

  void set (const cmt_string& w)
      {
        if (w.find (0, "-native_version=") != -1)
          {
               native_version = w.substr (16);
          }
          else if (w == "-auto_imports")
          {
            auto_imports = On;
          }
        else if (w == "-no_auto_imports")
          {
            auto_imports = Off;
          }
        else if (w == "|")
          {
            state = need_version_alias;
          }
        else
          {
            switch (state)
              {
                case need_package:
                  package = w;
                  state = need_version;
                  break;
                case need_version:
                  version = w;
                  state = need_path;
                  break;
                case need_path:
                  path = w;
		  while (path[path.size() - 1] == '/') path.erase (path.size() - 1);
		  while (path[path.size() - 1] == '\\') path.erase (path.size() - 1);
                  state = finished;
                  break;
                case need_version_alias:
                  version_alias = w;
                  state = need_path_alias;
                  break;
                case need_path_alias:
                  path_alias = w;
		  while (path_alias[path_alias.size() - 1] == '/') path_alias.erase (path_alias.size() - 1);
		  while (path_alias[path_alias.size() - 1] == '\\') path_alias.erase (path_alias.size() - 1);
                  state = finished;
                  break;
              }
          }
      }

  bool ok ()
      {
        if (package == "") return (false);
        if (CmtSystem::is_home_package (package, version)) return (false);
        if (CmtSystem::is_user_context_package (package, version)) return (false);
        if (CmtSystem::is_project_package (package, version)) return (false);

        return (true);
      }

  /**
   *
   *  Build or retreive the Use object corresponding to the parsed specification.
   *
   */
  Use* get_use (Use* parent)
      {
        static Use::UsePtrVector& uses = Use::get_ordered_uses ();

        if (version == "") version = "*";

        if (Cmt::get_debug ())
          {
            int i;

            cout << "use::action1> current=" << parent->get_package_name () <<
                " package=" << package << " ";

            for (i = 0; i < uses.size (); i++)
              {
                Use* u = uses[i];
                cout << u->get_package_name () << " ";
              }
            cout << endl;
          }

        const Use& cu = Use::current ();

        /**
         *   Do not continue the operations for private uses
         *  accessed from an external context.
         *
         *   Exceptions should be considered for 
         *     - cmt broadcast
         *     - cmt show uses
         */

        ActionType action = Cmt::get_action ();

        if (Cmt::get_debug ())
          {
            cout << "before adding " << package <<"> auto_imports=" << auto_imports
                 << " (current AI was " << UseContext::get_current_auto_imports () << ")" 
                 << " (Use::scope=" << parent->get_current_scope () << ")"
                 << " (parent=" << parent->get_package_name () << ")"
                 << endl;
          }

	bool hidden_by_scope = false;

	if (parent->get_current_scope () == ScopePrivate)
	  {
	    hidden_by_scope = true;

	    // Do not hide immediate children of the current package.
	    if ((parent == 0) || 
		(parent->get_package () == cu.get_package ())) hidden_by_scope = false;

	    // Override default rule according to the scope filtering mode.

	    if (Cmt::get_scope_filtering_mode () == reach_private_uses) hidden_by_scope = false;
	  }

	if (hidden_by_scope)
          {
            return (0);
          }

        // Here the version may contain wild cards

        UseContext save = UseContext::current ();

          /**
           *  "auto_imports" is the state which is specified on the use statement 
           *  currently being parsed.
           */
        switch (auto_imports)
          {
            case Unspecified:

                // unspecified => we forward the state saved in the current use context

              UseContext::set_current (UseContext::get_current_auto_imports ());
              break;
            case Off:

                // off => the context becomes constrained to be off

              UseContext::set_current (Off);
              break;
            case On:

                // on => if current context is off it is kept off
                //       otherwise it is forced to on

              if (UseContext::get_current_auto_imports () != Off)
                {
                  UseContext::set_current (On);
                }
              break;
          }

        if (Cmt::get_debug ())
          {
            cout << "about to add " << package << endl;
	    show_packages ();
	  }

        /// Now do create or retreive the Use object.
        Use* new_use = Use::add (path, package, version, 
                                 version_alias, path_alias, native_version,
                                 parent, auto_imports);

        if (new_use != 0)
          {
            if (Cmt::get_debug ())
              {
                cout << "after adding1 " << package
		     << "> auto_imports=" << new_use->auto_imports << endl;

		show_packages ();
              }

            switch (new_use->auto_imports)
              {
                case Unspecified:
                  new_use->auto_imports = UseContext::get_current_auto_imports ();
                  if (new_use->auto_imports == Unspecified)
		    {
		      new_use->auto_imports = On;
		    }
                  break;
                case On:
                  break;
                case Off:
                  if (UseContext::get_current_auto_imports () == On)
                    {
                        /**
                         *  Warning : this Use had been previously specified as -no_auto_imports
                         *  Now this new specification tries to turn it to auto_imports.
                         *  It will be required to propagate the change, according to the 
                         *  specifications:
                         *
                         *    for all sub_uses:
                         *       if it is unspecified OR specified as auto_imports:
                         *          turn it to auto_imports
                         *
                         */
                      new_use->set_auto_imports (On);
                    }
                  break;
              }


            if (Cmt::get_debug ())
              {
                cout << "after adding2 " << package 
		     << "> auto_imports=" << new_use->auto_imports << " ";
                cout << endl;
              }

            UseContext& c = UseContext::current ();
            c = save;

            Use::reorder (new_use, parent);
            
            if (Cmt::get_debug ())
              {
                cout << "use::action2> current=" << parent->get_package_name ()
                     << " package=" << package << " ";

                int i;
                
                for (i = 0; i < uses.size (); i++)
                  {
                    Use* u = uses[i];
                    cout << u->get_package_name () << "[" << u->get_index () << "]" << " ";
                  }

                cout << endl;
              }
          }

        return (new_use);
      }

private:

  enum
  {
    need_package,
    need_version,
    need_path,
    need_version_alias,
    need_path_alias,
    finished
  } state;
  
  State auto_imports;
  cmt_string native_version;
  cmt_string package;
  cmt_string version;
  cmt_string path;
  cmt_string version_alias;
  cmt_string path_alias;
};

//----------------------------------------------------------
Use* Use::action (const CmtSystem::cmt_string_vector& words, Use* parent)
{
  Use* new_use;

  //
  // complete syntax : "use <package> <version> <path> <-native_version=<version>>"  
  // minimal syntax  : "use <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 (0);

  use_action_iterator it;

  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)
            {
              const cmt_string& ww = ws[j];
              it.set (ww);
            }
        }
      else
        {
             it.set (ew);
        }
    }

  if (!it.ok ()) return (0);

  static int level = 0;

  level++;
  new_use = it.get_use (parent);
  level--;

  return (new_use);
}

//----------------------------------------------------------
void Use::author_action (const CmtSystem::cmt_string_vector& words)
{
  if (author != "") author += "\n";
  for (int i = 1; i < words.size (); i++)
    {
      const cmt_string& w = words[i];
      
      if (i > 1) author += " ";
      author += w;
    }
}
//----------------------------------------------------------
void Use::manager_action (const CmtSystem::cmt_string_vector& words)
{
  if (manager != "") manager += "\n";
  for (int i = 1; i < words.size (); i++)
    {
      const cmt_string& w = words[i];
      
      if (i > 1) manager += " ";
      manager += w;
    }
}

//----------------------------------------------------------
Use* Use::find (const cmt_string& package, 
                const cmt_string& version, 
                const cmt_string& path)
{
  Package* p = Package::find (package);
  if (p == 0) return (0);

  UsePtrVector& uses = p->get_uses ();

  int use_index;
  int size = uses.size ();

  for (use_index = 0; use_index < size; use_index++)
    {
      Use& use = (*uses[use_index]);

      // If the version argument is omitted then
      // take the first registered version
      if (version == "") return (&use);
          
      // Otherwise compare against specified_version and path
      //if ((use.specified_version == version) && 
      //  (use.specified_path == path)) return (&use);
      
      // what about comparing wild cards?

      if (use.specified_version == version) return (&use);
    }

  return (0);
}

//----------------------------------------------------------
int Use::find_index (const cmt_string& package_name, 
                     const cmt_string& version, 
                     const cmt_string& path)
{
  Package* package = Package::find (package_name);
  if (package == 0) return (-1);

  UsePtrVector& uses = package->get_uses ();

  int use_index;
  int size = uses.size ();
  for (use_index = 0; use_index < size; use_index++)
    {
      Use* use = uses[use_index];

      if (Cmt::get_debug ())
        {
          cout << "Use::find_index> " << package_name
               << " " << use
               << " " << use->m_index
               << endl;
        }

      // If the version argument is omitted then
      // take the first registered version
      if (version == "") return (use->m_index);
      
      // Otherwise compare against specified_version and path
      //if ((use.specified_version == version) && 
      //  (use.specified_path == path)) return (&use);
      
      // what about comparing wild cards?
      
      if (use->specified_version == version) return (use->m_index);
    }

  return (-1);
}

/**
 *   1) Given a Use object identified by its index in the ordered_uses() array,
 *   2) Given a vector of states containing the auto_imports states of all uses,
 *
 *     we want to switch to 'On' the auto_imports states of all
 *     sub_uses of the argument that were Off.
 *
 */
void Use::set_auto_imports_state (int use_index,
                                  cmt_vector<bool>& auto_imports_states)
{
  // check boundaries (this should be an error when out bondaries)
  if ((use_index < 0) || (use_index >= auto_imports_states.size ())) return;

  Use::UsePtrVector& uses = Use::get_ordered_uses ();

  if (Cmt::get_debug ())
    {
      int i;
      
      cout << "Use::set_auto_imports_state> use_index=" << use_index << " ";
      
      for (i = 0; i < auto_imports_states.size (); i++)
	{
	  cout << (auto_imports_states[i] ? "|" : "o");
	}

      for (i = 0; i < auto_imports_states.size (); i++)
	{
	  cout << " " << (uses[i])->get_package_name ();
	}

      cout << endl;
    }

  // check if this is already done (recursivity ending)
  if (auto_imports_states[use_index]) return;

  Use* use = uses[use_index];

  // We only have to deal with Use objects that were
  // actually turned Off
  if (use->auto_imports != Off) return;

  /**
   *  Here, use points to a Use object which had been declared as
   *  no_auto_imports. But we need to switch it On. Therefore, 
   *
   *    1) the state in auto_imports_states has to be set On 
   *    2) the operation has to be recursively propagated to all
   *         sub_uses which also had no_auto_imports. (only to those)
   *  
   */
  
  auto_imports_states[use_index] = true;

  int size = 0;

  size = use->sub_uses.size ();
  for (int i = 0; i < size; i++)
    {
      Use* u = use->sub_uses[i];

      // first find the index of this use.

      int j = u->m_index;

      if (j >= 0)
	{
	  if (u->sub_use_auto_imports[i] == Off)
	    {
	      set_auto_imports_state (j, auto_imports_states);
	    }
	  else
	    {
	      auto_imports_states[j] = true;
	    }
        }
    }
}

//----------------------------------------------------------
//
//  Move use to the end
//
//----------------------------------------------------------
void Use::move (Use* use)
{
  if (use == 0) return;

  static UsePtrVector& uses = get_ordered_uses ();

  int size = uses.size ();

  if (size == 0) return;

  //
  // On se positionne sur le pointeur.
  //

  int use_index = use->m_index;

  if (use_index < 0) return;

  //
  // On deplace tous les pointeurs d'une case en arriere
  //
  for (use_index++; use_index < size; use_index++)
    {
      Use* u = uses[use_index];
      u->m_index--;

      uses[use_index - 1] = uses[use_index];
    }

  //
  // use est donc replace en derniere position
  //

  use->m_index = size - 1;
  uses[size - 1] = use;
}

/**
 *   Reorder provider versus client in the UsePtrVector list, 
 *  so that the order reflects the fact that client makes use of provider.
 *   The result should be that provider appears before client in the list.
 */
void Use::reorder (Use* provider, Use* client)
{
  static UsePtrVector& uses = get_ordered_uses ();

  int use_index;

  int size = uses.size ();

  if (size == 0) return;
  if (provider == client) return;

  //
  // First locate the two use objects into the uses vector.
  //   -> provider_index and client_index
  //

  int provider_index = -1;
  int client_index = -1;

  provider_index = provider->m_index;
  client_index = client->m_index;

  if (Cmt::get_debug ())
    {
      cout << "Use::reorder> provider=" << provider_index << " client=" << client_index << endl;
    }

  //
  // Both objects must be installed in uses before acting.
  //
  if (provider_index == -1) return;
  if (client_index == -1) return;

  if (client_index < provider_index)
    {
      //
      // client is already before provider so job is finished
      //
      return;
    }

  //
  // before : <a a a P b b b C c c c>
  //                 ^       ^
  //                 ip      ic
  //
  //  1) move "P b b b" by one place to the right
  // thus :   <a a a P P b b b c c c>
  //
  //  2) move "C" to [ip]
  //
  // after  : <a a a C P b b b c c c>
  //
  
  for (use_index = client_index - 1; use_index >= provider_index; use_index--)
    {
      Use* use = uses[use_index];
      use->m_index++;

      uses[use_index + 1] = uses[use_index];
    }
  
  uses[provider_index] = client;
  client->m_index = provider_index;
}

//----------------------------------------------------------
void Use::clear_all ()
{
  static UsePtrVector& uses = get_ordered_uses ();
  static UseVector& instances = get_instances ();

  int use_index;

  for (use_index = 0; use_index < instances.size (); use_index++)
    {
      Use& use = instances[use_index];
      use.clear ();
    }

  uses.clear ();
  instances.clear ();
}

//----------------------------------------------------------
void Use::unselect_all ()
{
  static UsePtrVector& uses = get_ordered_uses ();

  int use_index;

  int size = uses.size ();

  if (size == 0) return;

  for (use_index = 0; use_index < size; use_index++)
    {
      Use* use = uses[use_index];

      if (use != 0)
        {
          use->unselect ();
        }
    }
}

//----------------------------------------------------------
void Use::undiscard_all ()
{
  static UsePtrVector& uses = get_ordered_uses ();

  int use_index;

  if (uses.size () == 0) return;

  for (use_index = 0; use_index < uses.size (); use_index++)
    {
      Use* use = uses[use_index];

      if (use != 0)
        {
          use->undiscard ();
        }
    }
}

//----------------------------------------------------------
void Use::fill_macro_all (cmt_string& buffer, const cmt_string& suffix)
{
  UsePtrVector& uses = get_ordered_uses ();

  buffer = "macro_append use_";
  buffer += suffix;
  buffer += " \" ";
  (Use::current()).fill_macro (buffer, suffix);

  for (int number = 0; number < uses.size (); number++)
    {
      Use* use = uses[number];
      
      Package* p = use->get_package ();
      if (p->is_cmt ()) continue;

      if (use->discarded) continue;
      if (use->auto_imports == Off) continue;

      use->fill_macro (buffer, suffix);
    }
  
  buffer += "\"";
}

//----------------------------------------------------------
Use::Use ()
{
  done = false;
  discarded = false;
  auto_imports = Unspecified;
  m_package = 0;
  m_index = -1;
  native_version = "";

  clear ();
}

//----------------------------------------------------------
Use::Use (const cmt_string& new_package,
          const cmt_string& new_version,
          const cmt_string& new_path)
{
  auto_imports = Unspecified;
  m_located = false;
  m_package = 0;
  m_index = -1;

  set (new_package, new_version, new_path);
}

//----------------------------------------------------------
Use::~Use ()
{
  clear ();
}

//----------------------------------------------------------
void Use::clear ()
{
  specified_path = "";
  path      = "";

  if (m_package != 0)
    {
      m_package->remove_use (this);
      m_package = 0;
    }

  version   = "";
  author    = "";
  manager   = "";
  real_path = "";

  prefix    = "";
  style     = mgr_style;
  initial_scope = ScopePublic;
  done      = false;
  discarded = false;
  selected  = false;
  auto_imports = Unspecified;

  includes.clear ();
  include_path = "";
  scripts.clear ();
  apply_patterns.clear ();
  ignore_patterns.clear ();

  sub_uses.clear ();
  sub_use_scopes.clear ();
  sub_use_auto_imports.clear ();

  requests.clear ();

  alternate_versions.clear ();
  alternate_paths.clear ();

  version_alias = "";
  path_alias    = "";

  m_located = false;
  m_has_native_version = false;
  m_index = -1;
}

//----------------------------------------------------------
void Use::set (const cmt_string& new_package,
               const cmt_string& new_version,
               const cmt_string& new_path,
               const cmt_string& new_version_alias,
               const cmt_string& new_path_alias)
{
  clear ();

  Package* p = Package::add (new_package);

  m_package = p;
  p->add_use (this);

  specified_path    = new_path;
  // specified_path.replace_all ("\\", "/");

  specified_version = new_version;
  version           = new_version;
  path              = specified_path;
  Symbol::expand (path);
  real_path         = "";
  style             = mgr_style;
  initial_scope     = ScopePublic;
  done              = false;
  discarded         = false;
  Cmt::build_prefix (new_package, prefix);

  version_alias = new_version_alias;
  path_alias    = new_path_alias;
}

//----------------------------------------------------------
void Use::change_path (const cmt_string& new_path)
{
  // 
  // This methods changes real_path after an actual location
  // where this package/version has been found.
  //

  real_path = "";

  if (new_path != "")
    {
      if ((path.size () > 0) &&
          (!CmtSystem::absolute_path (path)))
        {
          real_path = new_path;
          real_path += CmtSystem::file_separator ();
          real_path += path;
        }
      else
        {
          real_path = new_path;
        }
      // real_path.replace_all ("\\", "/");

      CmtSystem::compress_path (real_path);
    }
  
  m_located = true;
}

//----------------------------------------------------------
int Use::reach_package (const cmt_string& from_path)
{
  if (Cmt::get_debug ())
    {
      cout << "Use::reach_package> (" << get_package_name () << " " 
	   << version << ")from " << from_path << endl;
    }

  //
  // We try to reach a package/version starting from from_path
  //

  // check if from_path is at least real
  if ((from_path != "") && !CmtSystem::cd (from_path)) return (0);

  if (Cmt::get_debug ())
    {
      cout << "Use::reach_package-2>" << endl;
    }

  // check in case from_path is a new search path 
  if (from_path != real_path)
    {
      // Move to that prefix only if it is a relative path.
      if ((path.size () > 0) && (!CmtSystem::absolute_path (path)))
        {
          if (!CmtSystem::cd (path))
            {
              return (0);
            }
        }
    }

  if (Cmt::get_debug ())
    {
      cout << "Use::reach_package-3>" << endl;
    }

  // Special treatment for CMTHOME package...
  if (get_package_name () == CmtSystem::get_home_package ())
    {
      discarded = 1;
      if (!CmtSystem::test_file ("requirements"))
        {
          return (0);
        }
      else
        {
          return (1);
        }
    }

  if (Cmt::get_debug ())
    {
      cout << "Use::reach_package-4>" << endl;
    }

  // Special treatment for CMTUSERCONTEXT package...
  if (get_package_name () == CmtSystem::get_user_context_package ())
    {
      discarded = 1;
      if (!CmtSystem::test_file ("requirements"))
        {
          return (0);
        }
      else
        {
          return (1);
        }
    }

  // Special treatment for PROJECT package...
  if (get_package_name () == CmtSystem::get_project_package ())
    {
      discarded = 1;
      if (!CmtSystem::test_file ("project"))
        {
          return (0);
        }
      else
        {
          return (1);
        }
    }

  if (Cmt::get_debug ())
    {
      cout << "Use::reach_package-5>" << endl;
    }

  // Now from_path exists, try if the package exists there
  if (!CmtSystem::cd (get_package_name ()))
    {
      return (0);
    }

  if (Cmt::get_debug ())
    {
      cout << "Use::reach_package-6>" << endl;
    }

  if (CmtSystem::test_file ("cmt/requirements"))
    {
      CmtSystem::cd ("cmt");

      style = no_version_style;

      if (CmtSystem::test_file ("version.cmt"))
	{
	  cmt_string v;

	  v.read ("version.cmt");
	  int pos;
	  pos = v.find ('\n');
	  if (pos != cmt_string::npos) v.erase (pos);
	  pos = v.find ('\r');
	  if (pos != cmt_string::npos) v.erase (pos);

	  CompareStatus s = compare_versions (version, v);

	  if (Cmt::get_debug ())
	    {
	      cout << "Use::reach_package-6.1> version=" << version << " v=" << v << " s=" << s << endl;
	    }

	  switch (s)
	    {
	    case IdenticalIds:
	    case ExplicitOldMajorIdWinsAgainstWildarded:
	    case ExplicitOldMinorIdWinsAgainstWildarded:
	    case ExplicitOldPatchIdWinsAgainstWildarded:
	      break;
	    case ExplicitNewMajorIdWinsAgainstWildarded:
	    case ExplicitNewMinorIdWinsAgainstWildarded:
	    case NewMinorIdGreaterThanOld:
	    case ExplicitNewPatchIdWinsAgainstWildarded:
	    case NewPatchIdGreaterThanOld:
	      break;
	    case OldMajorIdGreaterThanNew:
	    case NewMajorIdGreaterThanOld:
	      break;
	    case IncompatibleMajorIds:
	      return (0);
	    }
	  version = v;
	}
      else if (version == "")
	{
	  version = "v0";
	}

      return (1);
    }
  else if (!CmtSystem::cd (version))
    {
      //
      // The specified version cannot be found per-se
      // There are alternate possibilities when it contains wild cards
      //
      if ((version == "") ||
          (version.find ("*") != cmt_string::npos))
        {
          static CmtSystem::cmt_string_vector versions;
          static cmt_string name;

          name = ".";
          name += CmtSystem::file_separator ();
          if (version == "") name += "*";
          else name += version;

          CmtSystem::scan_dir (name, versions);
          
          int i;
          bool found = false;
          
          for (i = 0; i < versions.size (); i++)
            {
              const cmt_string& vers = versions[i];
              
              if (Cmt::get_debug ())
                {
                  cout << "     ... version " << vers << " exists" << endl;
                }

              CmtSystem::basename (vers, name);
              
              int v;
              int r;
              int p;
              
              if (CmtSystem::is_version_directory (name, v, r, p))
                {
                  /*
                    This check is not sufficient !! We need to check in addition
                    that the selected directory is really the start of a true CMT
                    package (ie with either /mgr/requirements or /cmt/requirements below)
                  */

                  cmt_string req;

                  req = name;
                  req += CmtSystem::file_separator ();
                  req += "mgr";
                  req += CmtSystem::file_separator ();
                  req += "requirements";

                  if (!CmtSystem::test_file (req))
                    {
                      req = name;
                      req += CmtSystem::file_separator ();
                      req += "cmt";
                      req += CmtSystem::file_separator ();
                      req += "requirements";

                      if (!CmtSystem::test_file (req)) continue;
                    }

                  cmt_string& new_v = alternate_versions.add ();
                  new_v = name;
                  cmt_string& new_p = alternate_paths.add ();
                  new_p = from_path;

                  found = true;
                }
            }

          if (!found)
            {
              if (CmtSystem::test_file ("cmt/requirements"))
                {
                  CmtSystem::cd ("cmt");
                  style = no_version_style;
                  return (1);
                }
            }
        }

      if (Cmt::get_debug ())
        {
          cout << "  ... end of version scan" << endl;
        }

        //
        //  We have now the list of possible alternate versions. However
        // we return that the expected package/version was not found (yet).
        //

      return (0);
    }

  if (Cmt::get_debug ())
    {
      cout << "Use::reach_package-7>" << endl;
    }

  //cerr << "  ... version " << version << " exists" << endl;

  // Now we have met the exact specified version!
  if (!CmtSystem::test_file ("cmt/requirements"))
    {
      if (!CmtSystem::test_file ("mgr/requirements"))
        {
          return (0);
        }
      else
        {
          CmtSystem::cd ("mgr");
          style = mgr_style;
        }
    }
  else
    {
      CmtSystem::cd ("cmt");
      style = cmt_style;
    }

  if (Cmt::get_debug ())
    {
      cout << "Use::reach_package-8>" << endl;
    }

  return (1);
}

class UseProjectAction : public IProjectAction
{
public:
  UseProjectAction (Use* use) : m_use (use), m_found (false)
  {
  }

  bool found () const
  {
    return (m_found);
  }

  bool run (const Project& project)
  {
    const cmt_string& path = project.get_cmtpath ();
      
    m_use->alternate_versions.clear ();
    m_use->alternate_paths.clear ();

    if (m_use->reach_package (path))
      {
	if (Cmt::get_debug ())
	  {
	    cout << "move_to4> " << path << endl;
	  }
	
	m_use->change_path (path);

	m_found = true;

	return (false);
      }
    else if (m_use->alternate_versions.size () > 0)
      {
	if (m_use->select_alternate ()) 
	  {
	    if (Cmt::get_debug ())
	      {
		cout << "move_to5> " << m_use->real_path << endl;
	      }

	    m_found = true;

	    return (false);
	  }
      }
    
    return (true);
  }

private:
  Use* m_use;
  bool m_found;
};

//----------------------------------------------------------
bool Use::move_to ()
{
  if (m_located)
    {
      //
      // The real path where this version/package can be found 
      // has already been resolved. We thus first go there.
      //

      if (Cmt::get_debug ())
        {
          cout << "move_to1> " << real_path << endl;
        }

      reach_package (real_path);

      return (true);
    }

  cmt_string expanded_path = path;

  //
  // Try here.
  //
  if (expanded_path == "")
    {
      if (reach_package (""))
        {
          if (Cmt::get_debug ())
            {
              cout << "move_to2> " << expanded_path << endl;
            }

          change_path (expanded_path);

          return (true);
        }
      else if (alternate_versions.size () > 0)
        {
          if (select_alternate ()) 
            {
              if (Cmt::get_debug ())
                {
                  cout << "move_to5> " << real_path << endl;
                }
              
              return (true);
            }
        }
    }
      
  //
  // If the path specified in this use is a true absolute path,
  // then we search the package from there first.
  //
  if (CmtSystem::absolute_path (expanded_path))
    {
      if (reach_package (expanded_path))
        {
          if (Cmt::get_debug ())
            {
              cout << "move_to3> " << expanded_path << endl;
            }

          change_path (expanded_path);

          return (true);
        }
      else if (alternate_versions.size () > 0)
        {
          if (select_alternate ()) 
            {
              if (Cmt::get_debug ())
                {
                  cout << "move_to5> " << real_path << endl;
                }
              
              return (true);
            }
        }
    }
      
  //
  // Second try is among the CMTPATHs
  //
      
  UseProjectAction pa (this);

  Project::broadcast (pa);

  if (pa.found ()) return (true);

  return (false);
}

//----------------------------------------------------------
bool Use::select_alternate ()
{
  int i;

  int v0 = 0;
  int r0 = 0;
  int p0 = 0;

  int v = 0;
  int r = 0;
  int p = 0;

  int selected_index = -1;

  for (i = 0; i < alternate_versions.size (); i++)
    {
      cmt_string& name = alternate_versions[i];

        /*
      if (CmtSystem::getenv ("CMTTESTUSEWILDCARDS") != "")
        {
          cout << "select_alternate[" << this << "]> package " << get_package_name () << 
              " sv=" << specified_version << 
              " v=" << version << 
              " av[" << i << "]=" << name << endl;
        }
        */

      if (i == 0)
        {
          CmtSystem::is_version_directory (name, v0, r0, p0);
          selected_index = 0;
        }
      else
        {
          CmtSystem::is_version_directory (name, v, r, p);

          if (v > v0)
            {
              selected_index = i;
              v0 = v;
              r0 = r;
              p0 = p;
            }
          else if (v == v0)
            {
              if (r > r0)
                {
                  selected_index = i;
                  r0 = r;
                  p0 = p;
                }
              else if (r == r0)
                {
                  if (p > p0)
                    {
                      selected_index = i;
                      p0 = p;
                    }
                }
            }
        }
    }

  if (selected_index >= 0)
    {
      if (CmtSystem::cd (alternate_paths[selected_index]))
        {
          version = alternate_versions[selected_index];
          if (reach_package (alternate_paths[selected_index]))
            {
                /*
              if (CmtSystem::getenv ("CMTTESTUSEWILDCARDS") != "")
                {
                  cout << "select_alternate2> package " << get_package_name () << 
                      " sv=" << specified_version << 
                      " v=" << version << endl;
                }
                */

              if (Cmt::get_debug ())
                {
                  cout << "select_alternate> " << alternate_paths[selected_index] << endl;
                }

              change_path (alternate_paths[selected_index]);
              return (true);
            }
        }
    }

  return (false);
}

//----------------------------------------------------------
bool Use::need_new (const cmt_string& path,
                    const cmt_string& package,
                    const cmt_string& version,
                    Use** old_use,
		    Use* context_use)
{
  bool new_request = add_request (path, package, version);

  Use& current_use = Use::current ();

  if (package == current_use.get_package_name ())
    {
      if (Cmt::get_debug ())
	{
	  cout << "  ... recursive use to current package" << endl;
	}

      if (old_use != 0) *old_use = &current_use;
      return (false);
    }

  Package* p = Package::add (package);

  UsePtrVector& uses = p->get_uses ();

  bool do_need_new = true;
  Use* found = 0;
  Use* registered = 0;

  int req_v = -1;
  int req_r = -1;
  int req_p = -1;

  CmtSystem::is_version_directory (version, req_v, req_r, req_p);

  if (Cmt::get_debug ())
    {
      cout << "need_new> p=" << package << " v=" << version << " v=" << req_v << " r=" << req_r << " p=" << req_p << endl;
    }

  bool has_wild_card = (req_v == -1) || (req_r == -1) || (req_p == -1);

  int new_v = -1;
  int new_r = -1;
  int new_p = -1;

  int use_index;

  if (old_use != 0) *old_use = 0;

  // Acquire the registered use.
  for (use_index = 0; use_index < uses.size (); use_index++)
    {
      Use& use = (*uses[use_index]);

      if (use.m_index < 0) continue;

      registered = &use;

      break;
    }

  // Make a first try with the registered use if it exists.

  cmt_string decision;

  if (registered != 0)
    {
      Use& use = (*registered);

      found = &use;

      CmtSystem::is_version_directory (use.specified_version, new_v, new_r, new_p);

      bool use_has_wild_card = (new_v == -1) || (new_r == -1) || (new_p == -1);

      if (has_wild_card && !use_has_wild_card)
	{
	  if (Cmt::get_debug ())
	    {
	      cout << "  ... wildcarded request loses against existing explicit" << endl;
	    }
	  
	  do_need_new = false; // We don't need a new one
	}
      else
	{
	  // here either !has_wild_card or use_has_wild_card

	  if ((version == use.specified_version) &&
	      (path == use.specified_path))
	    {
	      if (Cmt::get_debug ())
		{
		  cout << " ... exactly same version and path!" << endl;
		}
	      
	      do_need_new = false; // We don't need a new one
	    }
	  else if (version != use.specified_version)
	    {
	      if (Cmt::get_debug ())
		{
		  cout << "requested explicit wins against existing wildcarded"
		       << endl;
		}

	      // The registered use loses against the request
	    }
	  else
	    {
	      if (Cmt::get_debug ())
		{
		  cout << "only paths differ, consider the new one."
		       << endl;
		}

	      // The registered use loses against the request
	    }
	}


      //if (new_request && !Cmt::get_quiet () && (Cmt::get_action () == action_show_uses))
      if (new_request && !Cmt::get_quiet ())
	{
	  if ((new_v != -1) && (req_v != -1) && (new_v != req_v))
	    {
              cout << "# Required version " << version
		   << " of package " << package;

	      if (context_use != 0)
		{
		  cout << " [from " << context_use->get_package_name () << "]";
		}

	      cout << " incompatible with selected version " << use.version;

	      if (use.version != use.specified_version)
		{
		  cout << " (" << use.specified_version << ")" ;
		}

	      cout << endl;
	    }
	}

      if (Cmt::get_debug ())
	{
	  cout << "# " << package << " " << version;

	  if (context_use != 0)
	    {
	      cout << " [from " << context_use->get_package_name () << "]";
	    }

	  if (do_need_new)
	    {
	      cout << " > ";
	    }
	  else
	    {
	      cout << " < ";
	    }

	  cout << use.version;
	  if (use.version != use.specified_version)
	    {
	      cout << " (" << use.specified_version << ")" ;
	    }

	  cout << " (r) ";

	  cout << endl;
	}
    }

  if (do_need_new)
    {
      // Now try unregistered uses, since the registered one is not appropriate.
      // to see is any other existing use could match

      for (use_index = 0; use_index < uses.size (); use_index++)
	{
	  Use& use = (*uses[use_index]);
	  
	  if (use.m_index >= 0) continue;
	  
	  // Always save the latest found.
	  
	  found = &use;

	  CmtSystem::is_version_directory (use.specified_version, new_v, new_r, new_p);

	  bool use_has_wild_card = (new_v == -1) || (new_r == -1) || (new_p == -1);

	  if (has_wild_card && !use_has_wild_card)
	    {
	      if (Cmt::get_debug ())
		{
		  cout << "  ... requested wildcarded loses against existing explicit" << endl;
		}
	      
	      do_need_new = false; // We don't need a new one
	    }
	  else
	    {
	      // here either !has_wild_card or use_has_wild_card
	  
	      if ((version == use.specified_version) &&
		  (path == use.specified_path))
		{
		  if (Cmt::get_debug ())
		    {
		      cout << " ... exactly same version and path!" << endl;
		    }
	      
		  do_need_new = false; // We don't need a new one
		}
	      else if (version != use.specified_version)
		{
		  if (Cmt::get_debug ())
		    {
		      cout << "requested explicit wins against existing wildcarded"
			   << endl;
		    }

		  // This use loses against the request
		}
	      else
		{
		  if (Cmt::get_debug ())
		    {
		      cout << "only paths differ, consider the new one."
			   << endl;
		    }

		  // This use loses against the request
		}
	    }

	  //if (new_request && !Cmt::get_quiet () && (Cmt::get_action () == action_show_uses))
	  if (new_request && !Cmt::get_quiet ())
	    {
	      if ((new_v != -1) && (req_v != -1) && (new_v != req_v))
		{
		  cout << "# Required version " << version
		       << " of package " << package;

		  if (context_use != 0)
		    {
		      cout << " [from " << context_use->get_package_name () << "]";
		    }
	      
		  cout << " incompatible with selected version " << use.version;

		  if (use.version != use.specified_version)
		    {
		      cout << " (" << use.specified_version << ")" ;
		    }

		  cout << endl;
		}
	    }

	  if (Cmt::get_debug ())
	    {
	      cout << "# " << package << " " << version;
	      
	      if (context_use != 0)
		{
		  cout << " [from " << context_use->get_package_name () << "]";
		}
	      
	      if (do_need_new)
		{
		  cout << " > ";
		}
	      else
		{
		  cout << " < ";
		}
	      
	      if ((new_v != -1) && (req_v != -1) && (new_v != req_v))
		{
		  cout << "(incompatible) ";
		}

	      cout << use.version;
	      if (use.version != use.specified_version)
		{
		  cout << " (" << use.specified_version << ")" ;
		}
	      cout << endl;
	    }
	}
    }

  if (old_use != 0)
    {
      if (registered != 0) *old_use = registered;
      else *old_use = found;
    }

  return (do_need_new);
}

//----------------------------------------------------------
//
//  Here the version which is provided here is the specified version.
// It may contain wild cards or it may be simply empty.
//
//----------------------------------------------------------
Use* Use::create (const cmt_string& path,
                  const cmt_string& package,
                  const cmt_string& version,
                  const cmt_string& version_alias,
                  const cmt_string& path_alias)
{
  Package* p = 0;

  p = Package::add (package);

  UsePtrVector& vector = p->get_uses ();

  // We first look in the database.
  for (int use_index = 0; use_index < vector.size (); use_index++)
    {
      Use* use = vector[use_index];

      if ((use->specified_version == version) && 
	  (use->specified_path == path)) return (use);
    }

  // We now really create a new Use entry.

  static UseVector& instances = get_instances ();

  Use& use_object = instances.add ();

  use_object.set (package, version, path, version_alias, path_alias);

  return (&use_object);
}

//----------------------------------------------------------
//  Add a use request into the database.
//
//  o If a use request already exist in the database,
//    check the version compatibility
//
//----------------------------------------------------------
Use* Use::add (const cmt_string& path,
               const cmt_string& package_name,
               const cmt_string& version,
               const cmt_string& version_alias,
               const cmt_string& path_alias,
               const cmt_string& native_version,
               Use* context_use,
               State specified_auto_imports)
{
  bool do_need_new = false;

  Use* old_use = 0;
  Use* use = 0;

  do_need_new = need_new (path, package_name, version, &old_use, context_use);
  // cout <<"native_version: "<<native_version<<" "<<version<<endl;
    

  if (Cmt::get_debug ())
    {
      if (old_use != 0)
	{
	  cout << "add> old_use " << old_use->get_package_name () <<
	    " " << old_use->version <<
	    " " << old_use->path <<
	    endl;
	}
    }

  if (do_need_new)
    {
      use = create (path, package_name, version, version_alias, path_alias);
    }
  else
    {
      // Since we don't need a new Use, it means that old_use exists !
      use = old_use;
      old_use = 0;
    }

  if (package_name == CmtSystem::get_home_package ())
    {
      return (use);
    }

  if (package_name == CmtSystem::get_user_context_package ())
    {
      return (use);
    }

  if (package_name == CmtSystem::get_project_package ())
    {
      return (use);
    }

  cmt_string here = CmtSystem::pwd ();

    //
    // Store the specified sub_uses. Some of them may become discarded
    // later on.
    //
  if (context_use != 0)
    {
      context_use->sub_uses.push_back (use);

      context_use->sub_use_scopes.push_back (context_use->get_current_scope ());

      context_use->sub_use_auto_imports.push_back (specified_auto_imports);

      cmt_string& request = context_use->requests.add ();

      request = package_name;
      request += " ";
      request += version;
      request += " ";
      request += path;


      if (Cmt::get_debug ())
        {
          cout << "Use::add context(" << context_use->get_package_name () << ") "
               << "[u:" << package_name
               << " s:" << context_use->get_current_scope ()
               << " ai:" << specified_auto_imports
               << "]" << endl;
        }
    }

  if (use == &(Use::current ())) 
    {
      return (use);
    }

  /*
   *   Now we have a Use object. If it is a new one, we have to
   *    1) understand if it exists physically
   *    2) it is better than the old ones.
   *
   *   Here, we may have :
   *    1) old_use = 0 
   *         there was no Use object before for this package
   *         the existing one is fine
   *
   *    2) old_use != 0 
   *         we have created a new Use (which has to be validated)
   */
  bool found = use->move_to ();

  if (Cmt::get_debug ())
    {
      cout << "add> use [" << use << "] " << use->get_package_name () 
           << " " << use->version
           << " " << use->path
           << " found=" << found
           << " done=" << use->done
           << endl;

      show_packages ();
    }
      
  if (!found)
    {
      if (!Cmt::get_quiet ())
        {
          cerr << "#CMT> Warning: package " << use->get_package_name () <<
            " " << use->version << " " << use->path << 
            " not found";

	  if (context_use != 0)
	    {
	      cerr << " (requested by " << context_use->get_package_name () << ")";
	    }

	  cerr << endl;
        }

      //CmtError::set (CmtError::package_not_found, use->get_package_name ());
      use = 0;
    }

  if ((old_use != 0) && (use != old_use))
    {
      if (Cmt::get_debug ())
	{
	  cout << "There was another version of this Use. \n"
	       << "But a new one was created due to some criteria. \n"
	       << "Now we are going to apply the version strategy to make the final selection"
	       << endl;
	}

      /*
       *    There was another version of this Use. 
       *    But a new one was created due to some criteria.
       *    Now we are going to apply the version strategy to make the final selection.
       */

      if (Cmt::get_debug ())
        {
	  cout << "select? [" << use << "] vs old_use[" << old_use << "] "
	       << old_use->get_package_name ()
	       << " " << old_use->version
	       << " " << old_use->path
	       << endl;
        }

      if (!found)
        {
          /*
           *  This new Use does not correspond to any physical package.
           *  let's simply discard it (and go back to old_use)
           */

          if (use != 0) use->discard ();
          use = old_use;
          found = use->move_to ();
        }
      else
        {
            //
            //  This new version is different from the old one
            // thus we have to choose
            //
	  static BestFitSelector selector;
          Use* selected_use = selector.operate (old_use, use);

	  if (Cmt::get_debug ())
	    {
	      cout << "after operate old=" << old_use << "(" << old_use->m_index << ")"
		   << " use=" << use << "(" << use->m_index << ")"
		   << " => selected=" << selected_use
		   << endl;
	    }

	    //
            // Some situations managed by selector.operate happen
            // to fail discarding the rejected Use.
            //
          if (use != selected_use) 
            {
	      use = use->set_selected_version (selected_use);
            }
          

          /*
           *   current directory is moved to the selected one
           */
          found = use->move_to ();
        }
    }

    //
    // The following statement is no longer considered as useful.
    // It is commented. But we should clarify why it was really needed!
    //
    //use->undiscard ();

  if (found)
    {
      static UsePtrVector& uses = get_ordered_uses ();

      bool registered = false;
      const Use& cu = Use::current ();

      Package* package = Package::find (package_name);

      //
      // A pointer to this new object is also added or replaced.
      //
      if ((use != &cu) && (package == cu.get_package ()))
        {
            // This is a recursive call to the current package!!
          registered = true;
          use->done = true;
	  use->m_index = -1;
        }
      else
        {
	  //
	  // Now sort out the registration in ordered uses
	  //
	  //  cases:
	  //
	  //   use is not yet registered (m_index < 0)
	  //     o if another use of the same package is registered,
	  //       it must be substituted with the new one
	  //     o otherwise create a new entry at the end (it will be sorted
	  //       afterwards)
	  //
	  //   use is already registered (m_index >= 0)
	  //     o if another use of the same package is registered,
	  //       it must be discarded, but this may mean that
	  //       two versions are simultaneously registered (bug?)
	  //     o otherwise we keep it in place
	  //

	  UsePtrVector& puses = package->get_uses ();

	  if (use->m_index >= 0) registered = true;

	  /*
	    We look for all existing uses in that package.
	   */
	  for (int i = 0; i < puses.size(); i++)
	    {
	      Use* u = puses[i];
              
	      if (Cmt::get_debug ())
		{
		  cout << "add> check registering between " << use
		       << ":" << use->get_package_name ()
		       << "(" << use->m_index << ")"
		       << " and " << u
		       << ":" << u->get_package_name () 
		       << "(" << u->m_index << ")"
		       << endl;
		}

	      if ((u != use) && (u->m_index >= 0))
		{
		  // Another use of that package is already registered

		  if (Cmt::get_debug ())
		    {
		      cout << "add> Another use of that package is already registered " 
			   << " use(" << use->m_index << ")";
		      if (use->m_index >= 0)
			{
			  cout << "[" << (uses[use->m_index])->get_package_name () << "]";
			}
		      cout << " u(" << u->m_index << ")";
		      if (u->m_index >= 0)
			{
			  cout << "[" << (uses[u->m_index])->get_package_name () << "]";
			}
		      cout << endl;
		    }

		  if (use->m_index < 0)
		    {
		      /*
			the newly selected use (use) should replace the one which was registered (u)
		      */

		      u->set_selected_version (use);

		      registered = true;
		    }
		  else
		    {
		      // bug??
		      if (Cmt::get_debug ())
			{
			  cout << "Duplicate entry in ordered uses" << endl;
			}
		    }
		}
	    }

	  if (Cmt::get_debug ())
	    {
	      cout << "add> registering completed" << endl;
	      show_packages ();
	    }
        }
      
      if (!registered) 
	{
	  if (Cmt::get_debug ())
	    {
	      cout << "add> We install use at the end the ordered list of uses" << endl;
	    }

	  uses.push_back (use);
	  use->m_index = uses.size () - 1;
	}

      if (Cmt::get_debug ())
	{
	  for (int use_index = 0; use_index < uses.size (); use_index++)
	    {
	      Use* u = (Use*) uses[use_index];
	      cout << "  use[" << use_index << "] p=(" << u << ")" << u->get_package_name () 
		   << " v=" << u->version
		   << " discarded=" << u->discarded
		   << " selected=" << u->selected 
		   << " done=" << u->done
		   << " index=" << u->m_index
		   << endl;
	    }
	  
	}
          
      if (!use->done && Cmt::get_recursive ())
        {
          use->done = true;

	  if (Cmt::get_debug ())
	    {
	      cout << "Parsing requirements file at " << CmtSystem::pwd () << endl;
	    }

          SyntaxParser::parse_requirements ("requirements", use);
        }
      else
	{
	  if (Cmt::get_debug ())
	    {
	      cout << "Requirements file at " << CmtSystem::pwd () << " already parsed" << endl;
	    }
	}
    }

  CmtSystem::cd (here);

  return (use);
}

//----------------------------------------------------------
void Use::discard ()
{
  discarded = true;
}

//----------------------------------------------------------
void Use::undiscard ()
{
  discarded = false;
}

//----------------------------------------------------------
void Use::select ()
{
  selected = true;
}

//----------------------------------------------------------
void Use::unselect ()
{
  selected = false;
}

//----------------------------------------------------------
bool Use::is_selected ()
{
  return (selected);
}

/**
 *    Check if the package makes use of the specified package/version
 *
 *    The seach only considers the first level of uses (thus it is
 *   NOT recursive)
 */
bool Use::is_client (const cmt_string& used_package,
                     const cmt_string& used_version)
{
  // A package is client of itself

  Package* package = Package::find (used_package);

  if ((get_package () == package) &&
      (version == used_version)) return (true);

  if (discarded) return (false);

  int i;

  for (i = 0; i < sub_uses.size (); i++)
    {
      Use* use = sub_uses[i];
      if (use == 0) continue;

      if ((use->get_package () == package) &&
          (use->version == used_version)) return (true);

      /*
        if (use->is_client (used_package, used_version))
        {
        return (true);
        }
      */
    }

  return (false);
}

//----------------------------------------------------------
void Use::apply_global_patterns ()
{
  int i;

  Pattern::PatternVector& vector = Pattern::patterns ();

  for (i = 0; i < vector.size (); i++)
    {
      Pattern& p = vector[i];

      if (p.global)
        {
          p.apply (this);
        }
    }
}

//----------------------------------------------------------
void Use::set_include_path (const cmt_string& new_path)
{
  include_path = new_path;
}

/**
 *   Compute the full real path of a found package.
 *   Takes the structuring style into account
 *
 *    Result is used to fill in the referenced string
 *    Result does NOT include the trailing file_separator
 */
void Use::get_full_path (cmt_string& s) const
{
  if (real_path == "") s = CmtSystem::pwd ();
  else s = real_path;

  s += CmtSystem::file_separator ();
  s += get_package_name ();
  
  if (style != no_version_style)
    {
      s += CmtSystem::file_separator ();
      s += version;
    }
}

/**
 *   Compute the full real path of a found package.
 *   Takes the structuring style into account
 */
cmt_string Use::get_full_path () const
{
  cmt_string result;

  get_full_path (result);

  return (result);
}

/**
 *   Considering a given path, try and reduce the part of it 
 *  that corresponds to the full path of this package into its
 *  normal form ${<PACKAGE>ROOT}
 *   The argument is modified if the pattern is exactly found.
 */
void Use::reduce_path (cmt_string& s) const
{
  cmt_string pattern;
  get_full_path (pattern);
  pattern += CmtSystem::file_separator ();
  
  cmt_string replacement = "${";
  replacement += prefix;
  replacement += "ROOT}";
  replacement += CmtSystem::file_separator ();

  s.replace (pattern, replacement);
}

//----------------------------------------------------------
void Use::get_cmtpath_and_offset (cmt_string& cmtpath, cmt_string& offset) const
{
  if (get_package_name () == CmtSystem::get_project_package ())
    {
      offset = "";
      CmtSystem::dirname (path, cmtpath);
      return;
    }
  else if (get_package_name () == CmtSystem::get_home_package ())
    {
      offset = "";
      cmtpath = path;
      return;
    }
  else if (get_package_name () == CmtSystem::get_user_context_package ())
    {
      offset = "";
      cmtpath = path;
      return;
    }

  cmtpath = "";
  offset = "";

  cmtpath = Project::find_in_cmt_paths (real_path);

  if (cmtpath != "")
    {
      CmtSystem::compress_path (cmtpath);

      static const cmt_string empty_string;
      static const cmt_string fs = CmtSystem::file_separator ();
      
      offset = real_path;
      offset.replace (cmtpath, empty_string);

      /**
	 try to remove this current CMTPATH entry from path.  This
	 has a meaning when the specified path already contains an
	 absolute path.
      */
      
      if (offset[0] == CmtSystem::file_separator ())
	{
	  // Just in case there is a part left after removing the cmtpath entry
		
	  offset.replace (fs, empty_string);
	}
    }
}

//------------------------------------------------------------
bool Use::get_strategy (const cmt_string& name) const
{
  Project* p = Project::find_by_cmtpath (real_path);

  bool strategy;

  if (p == 0)
    {
      static const Project::ProjectVector& projects = Project::projects ();

      if (projects.size () == 0) strategy = StrategyMgr::get_default_strategy (name);
      else
        {
          p = &(projects[0]);
          strategy = p->get_strategy (name);
        }
    }
  else
    {
      strategy = p->get_strategy (name);
    }

  return (strategy);
}

//----------------------------------------------------------
void Use::fill_includes_macro (cmt_string& buffer) const
{
  if (include_path == "")
    {
      buffer += "$(ppcmd)\"$(";
      buffer += get_package_name ();
      buffer += "_root)";
      buffer += CmtSystem::file_separator ();
      buffer += "src\" ";
    }
  else if (include_path != "none")
    {
      buffer += "$(ppcmd)\"";
      buffer += include_path;
      buffer += "\" ";
    }
  
  for (int i = 0; i < includes.size (); i++)
    {
      Include& incl = includes[i];

      if (incl.name == "") continue;
      
      buffer += "$(ppcmd)\"";
      buffer += incl.name;
      buffer += "\" ";
    }
}

//----------------------------------------------------------
void Use::fill_macro (cmt_string& buffer, const cmt_string& suffix) const
{
  buffer += " $(";
  buffer += get_package_name ();
  buffer += "_";
  buffer += suffix;
  buffer += ") ";
}

//----------------------------------------------------------
void Use::fill_standard_macros (cmt_string& buffer) const
{
  static cmt_string fs = CmtSystem::file_separator ();

  buffer = "";

  cmt_string package_name = get_package_name ();

  buffer += "macro ";
  buffer += package_name;
  buffer += "_tag";
  buffer += " \"$(tag)\"";
  buffer += "\n";

  if (located ())
    {
      buffer += "macro ";
      buffer += prefix;
      buffer += "ROOT";
      buffer += " \"";
      buffer += get_full_path ();
      buffer += "\"";
      buffer += "\n";

      buffer += "macro ";
      buffer += package_name;
      buffer += "_root";
      buffer += " \"";
      buffer += real_path;
      buffer += fs;
      buffer += package_name;
      if (style != no_version_style)
	{
	  buffer += fs;
	  buffer += version;
	}
      buffer += "\"";
      buffer += "\n";
    }

  buffer += "macro ";
  buffer += prefix;
  buffer += "VERSION";
  buffer += " \"";
  buffer += version;
  buffer += "\"";
  buffer += "\n";

  cmt_string cmtpath = "";
  cmt_string offset = "";

  get_cmtpath_and_offset (cmtpath, offset);

  buffer += "macro ";
  buffer += package_name;
  buffer += "_cmtpath";
  buffer += " \"";
  buffer += cmtpath;
  buffer += "\"";
  buffer += "\n";

  buffer += "macro ";
  buffer += package_name;
  buffer += "_offset";
  buffer += " \"";
  buffer += offset;
  buffer += "\"";
  buffer += "\n";
  
  Project* p = Project::find_by_cmtpath (cmtpath);

  buffer += "macro ";
  buffer += package_name;
  buffer += "_project";
  buffer += " \"";
  if (p != 0)
    {
      buffer += p->get_name ();
    }
  buffer += "\"";
  buffer += "\n";

  buffer += "macro ";
  buffer += package_name;
  buffer += "_project_release";
  buffer += " \"";
  if (p != 0)
    {
      buffer += p->get_release ();
    }
  buffer += "\"";
}


//----------------------------------------------------------
static bool same_dirs (const cmt_string& d1, const cmt_string& d2)
{
  bool result = false;

  cmt_string here = CmtSystem::pwd ();

  cmt_string h1;
  if (CmtSystem::cd (d1)) h1 = CmtSystem::pwd ();

  CmtSystem::cd (here);

  cmt_string h2;
  if (CmtSystem::cd (d2)) h2 = CmtSystem::pwd ();

  result = (h1 == h2);

  CmtSystem::cd (here);

  return (result);
}

//----------------------------------------------------------
static bool install_library (const cmt_string& source_library_name, 
			     const cmt_string& dest_library_name, 
			     const cmt_string& cmtinstallarea, 
			     const cmt_string& tag, 
			     const cmt_string& symlinkcmd)
{
  cmt_string libname = source_library_name;
  cmt_string name = dest_library_name;
  cmt_string s;
  Use& current_use = Use::current ();

  Symbol::expand (name);
  Symbol::expand (libname);

  if (cmtinstallarea != "")
    {
      s = cmtinstallarea;
      s += CmtSystem::file_separator ();
      s += tag;
      s += CmtSystem::file_separator ();
      s += "lib";
      s += CmtSystem::file_separator ();
      s += name;
      
      // Now creating the reference file

      cmt_string ref_file = s;
      ref_file += ".cmtref";

      cmt_string text = libname;
      text += "\n";

      Symbol::expand (ref_file);
      Symbol::expand (text);

      if (!Cmt::get_quiet ()) cout << "   Creating the reference file " << ref_file << endl;
      text.write (ref_file);
    }
  else
    {
      s = "../$(";
      s += current_use.get_package_name ();
      s += "_tag)/";
      s += name;
    }

  Symbol::expand (s);

  cmt_string source;
  cmt_string dest;

  CmtSystem::dirname (libname, source);
  CmtSystem::dirname (s, dest);

  if (same_dirs (source, dest))
    {
      return (false);
    }

  //if (!Cmt::get_quiet ()) 
  cout << "   Symlinking " << libname << " to " << s << endl;

  if (symlinkcmd == "")
    {
      if (!CmtSystem::create_symlink (libname, s))
	{
	  cerr << "#CMT> Cannot create a symbolic link to " << libname << endl;
	  return (false);
	}
    }
  else
    {
      cmt_string cmd = symlinkcmd;
      cmd += " ";
      cmd += libname;
      cmd += " ";
      cmd += s;
      
      int status = CmtSystem::execute (cmd);
      
      if (status != 0)
	{
	  cerr << "#CMT> Cannot create a symbolic link to " << libname << endl;
	  return (false);
	}
    }

  return (true);
}


/**
   Centralize the construction of library links for this Use.
*/
void Use::build_library_links (const cmt_string& cmtinstallarea, 
			       const cmt_string& tag, 
			       const cmt_string& shlibsuffix, 
			       const cmt_string& symlinkcmd) const
{
  if (!located ())
    {
      if (!Cmt::get_quiet ())
	{
	  cout << "# package " << get_package_name () <<
	    " " << version << " " << path << 
	    " not found" <<
	    endl;
	}
      return;
    }

  cmt_string s;

  s = get_package_name ();
  s += "_libraries";

  Symbol* libraries_macro = Symbol::find (s);

  if (libraries_macro == 0) return;

  cmt_string libraries = libraries_macro->build_macro_value ();
  Symbol::expand (libraries);
  static CmtSystem::cmt_string_vector values;

  CmtSystem::split (libraries, " \t", values);

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

      static cmt_string libname;
      static cmt_string name;

      // Is it a simple name or a complete path?

      libname = library;
      Symbol::expand (libname);

      bool is_absolute = false;

      is_absolute = CmtSystem::absolute_path (libname);

      if (is_absolute)
	{
	  /**
	   *   We assume here that "library" contains a complete path.
	   *   (including the complete syntax libxxx.so)
	   */

	  cmt_string suffix;
	  CmtSystem::basename (library, name);
	}
      else
	{
	  /**
	   *   Here we expect that only the base name of the library
	   *   is given : ie it should not contain the "lib" prefix, 
	   *   nor the suffix .so, nor any path prefix.
	   *    This of course should generally correspond to a constituent name.
	   */
	  
	  libname = "${";
	  libname += prefix;
	  libname += "ROOT}/${";
	  libname += get_package_name ();
	  libname += "_tag}/";
	  libname += "$(library_prefix)";
	  libname += library;
	  libname += "$(library_suffix)";
	  libname += ".";
	  libname += shlibsuffix;

	  name = "$(library_prefix)";
	  name += library;
	  name += "$(library_suffix)";
	  name += ".";
	  name += shlibsuffix;
	}

      if (!install_library (libname, name, cmtinstallarea, tag, symlinkcmd))
	{
	  continue;
	}

#ifdef __APPLE__
      if (!is_absolute)
	{
	  libname = "${";
	  libname += prefix;
	  libname += "ROOT}/${";
	  libname += get_package_name ();
	  libname += "_tag}/";
	  libname += library;
	  libname += ".bundle";

	  name = library;
	  name += ".bundle";
	  
	  if (!install_library (libname, name, cmtinstallarea, tag, symlinkcmd))
	    {
	      continue;
	    }
	}
#endif
    }
}

/**
 *   This function tries to get the replacement of a Use when it has
 *  been discarded by a better match to version constraints.
 */
Use* Use::get_selected_version ()
{
    //cout << "get_selected_version for package " << get_package_name () << endl;

  if (!discarded) return (this);

  Package* p = m_package;

  Use::UsePtrVector& uses = p->get_uses ();

  bool dbg = Cmt::get_debug ();

  int size = uses.size ();

  for (int i = 0; i < size; i++)
    {
      Use* u = uses[i];
      if (u == 0) continue;
      if (u->discarded) continue;
      if (dbg)
	{
	  cout << "Use::get_selected_version> for package " << get_package_name ()
	       << "  got a version " << u << endl;
	}
      return (u);
    }

  return (0);
}

/**
   Switching from one use to another one, (better wrt
   various criteria). 

   When switching, m_index and auto_imports are
   transfered from the un-selected to the newly selected.
 */
Use* Use::set_selected_version (Use* selected_use)
{
  if (this == selected_use) return (this);

  static UsePtrVector& uses = get_ordered_uses ();

  if (m_index >= 0)
    {
      // This discarded package was registered. We have to substitute
      // it with the new one.

      selected_use->m_index = m_index;
      selected_use->auto_imports = auto_imports;

      selected_use->undiscard ();

      m_index = -1;

      uses[selected_use->m_index] = selected_use;
    }

  discard ();

  return (selected_use);
}

void Use::set_auto_imports (State new_state)
{
  if (Cmt::get_debug ())
    {
      cout << "Use::set_auto_imports>(" << get_package_name () << ") " 
           << auto_imports << " -> " << new_state << endl;
    }

  if (auto_imports == new_state) return;
  
  State old_state = auto_imports;
  
  auto_imports = new_state;

    // We propagate only when we switch from Off to On

  if ((old_state == Off) && (new_state == On))
    {
      cmt_string s;
      static const cmt_string state_text[] = {"Unspecified", "Off", "On"};

      if (Cmt::get_debug ())
        {
          s = "Use::set_auto_imports>(";
          s += get_package_name ();
          s += ") ";

          cout << s << endl;
        }

      for (int i = 0; i < sub_uses.size (); i++)
        {
          Use* u = sub_uses[i];
          State state = sub_use_auto_imports[i];
          
          if (Cmt::get_debug ())
            {
              s += " ";
              s += u->get_package_name ();
              s += "(";
              s += state_text[state];
              s += ")";
            }

          if (state == Unspecified)
            {
              u->set_auto_imports (On);
            }
        }
          
      if (Cmt::get_debug ())
        {
          cout << s << endl;
        }
    }
}

void Use::set_native_version (bool state)
{
  m_has_native_version = state;
}

bool Use::has_native_version () const
{
  return (m_has_native_version);
}

Package* Use::get_package () const
{
  return (m_package);
}

const cmt_string& Use::get_package_name () const
{
  static const cmt_string null = "";

  Package* p = m_package;
  if (p == 0) return (null);

  return (p->get_name ());
}

void Use::set_package_name (const cmt_string& name)
{
  Package* p = Package::add (name);

  m_package = p;
  p->add_use (this);
}

int Use::get_index () const
{
  return (m_index);
}

//----------------------------------------------------------
bool Use::get_all_clients (const cmt_string& to_name)
{
  Use::UsePtrVector& uses = Use::get_ordered_uses ();

  Use* use = Use::find (to_name);

  if (use == 0)
    {
      cerr << "#CMT> No access to " << to_name << endl;
      return (false);
    }

  cmt_map <cmt_string, Use*> all_clients;
  cmt_map <cmt_string, Use*> all_clients_ok;

  const cmt_string& name = get_package_name ();

  Use* me = this;

  all_clients.add (name, me);
  all_clients_ok.add (name, me);

  bool status = get_all_clients (use, to_name, all_clients, all_clients_ok); 

  return (status);
}

//----------------------------------------------------------
bool Use::get_all_clients (Use* to, 
			   const cmt_string& result, 
			   cmt_map <cmt_string, Use*>& all_clients, 
			   cmt_map <cmt_string, Use*>& all_clients_ok)
{
  if (this == to)
    {
      cout << result << endl;
      return (true);
    }

  const cmt_string& to_name = to->get_package_name ();
  const cmt_string& to_version = to->version;

  //cout << "gac> from " << get_package_name () << " to " << to_name << " -> " << result << endl;

  if (all_clients.has (to_name))
    {
      if (all_clients_ok.has (to_name))
	{
	  cout << "   ..." << result << endl;
	  return (true);
	}
      else
	{
	  return (false);
	}
    }

  all_clients.add (to_name, to);

  bool status = false;

  Use::UsePtrVector& uses = Use::get_ordered_uses ();
  Use* use = 0;

  for (int n = 0; n < uses.size (); ++n)
    {
      use = uses[n];

      if (use->discarded) continue;

      if (!use->located ()) continue;

      if (use == to) continue;

      if (use->is_client (to_name, to_version))
	{
	  const cmt_string& n = use->get_package_name ();

	  cmt_string r;

	  if ((use->initial_scope != ScopeUnspecified) &&
	      (use->initial_scope != ScopePublic)) 
	    {
	      r += "(private)";
	    }
	  
	  if (use->auto_imports == Off) r += "(no_auto_imports)";

	  r += n;
	  r += ".";
	  r += result;

	  //cout << "gac> " << n << " is client of " << to_name << endl;

	  if (get_all_clients (use, r, all_clients, all_clients_ok))
	    {
	      all_clients_ok.add (n, use);
	      status = true;
	    }
	}
    }

  use = this;

  if (use->is_client (to_name, to_version))
    {
      const cmt_string& n = use->get_package_name ();
      
      cmt_string r;

      if ((use->initial_scope != ScopeUnspecified) &&
	  (use->initial_scope != ScopePublic)) 
	{
	  r += "(private)";
	}
      
      if (use->auto_imports == Off) r += "(no_auto_imports)";

      r += n;
      r += ".";
      r += result;
      
      //cout << "gac> " << n << " is client of " << to_name << endl;
      
      if (get_all_clients (use, r, all_clients, all_clients_ok))
	{
	  all_clients_ok.add (n, use);
	  status = true;
	}
    }

  return (status);
}

/**
   Let's consider two packages in the use graph "this" and "to"
   This function finds all packages that are reached in between
   following all possible paths between "this" and "to"

   Result s accumulated into "list"
 */
bool Use::get_paths (Use* to, UsePtrVector& list)
{
  bool is_in_path = false;
  bool cycle = false;
  bool already_in_path = false;
  bool dbg = Cmt::get_debug ();

  static int level = 0;

  int size = 0;

  if (level == 0)
    {
      unselect_all ();
      selected = false;
    }

  if (selected) 
    {
      /**
	 If this use is already in the list, we don't duplicate the entry.
	 (protection duplicate paths in the graph)
      */

      size = list.size ();

      for (int m = 0; m < size; m++)
	{
	  Use* u = list[m];
	  if (u == this)
	    {
	      if (dbg)
		{
		  for (int lll = 0; lll < level; lll++) cout << "  ";
		  cout << "  Use::get_paths." << level << "> try1.2 sub="
		       << get_package_name () << " already_in_path " << endl;
		}
	      
	      return (true);
	    }
	}

      return (false);
    }

  selected = true;

  if (dbg)
    {
      for (int lll = 0; lll < level; lll++) cout << "  ";
      cout << "Use::get_paths." << level << ">" << get_package_name ()
	   << " to=" << to->get_package_name () 
	   << " list=[";

      for (int m = 0; m < list.size (); m++)
        {
          Use* u = list[m];
	  cout << u->get_package_name () << " ";
        }

      cout << "]" << endl;
    }

  /**
     Now "this" is a candidate new entry in the list
   */

  // First figure out whether 'to' is used by 'this'.

  if (this->get_package_name () == to->get_package_name ())
    {
      // We've reached the goal (for the first time)
      is_in_path = true;
    }
  else
    {
      /**
	 Let's scan sub_uses now
       */
      size = sub_uses.size ();

      if (dbg)
	{
	  for (int lll = 0; lll < level; lll++) cout << "  ";
	  cout << "  Use::get_paths." << level << "> size=" << size << endl;
	}

      for (int n = 0; n < size; n++)
        {
          Use* use = sub_uses[n];

          if (use == 0) continue;

	  if (dbg)
	    {
	      for (int lll = 0; lll < level; lll++) cout << "  ";
	      cout << "  Use::get_paths." << level << "> try1 sub="
		   << use->get_package_name () << "(" << use << ") " << use->discarded << endl;
	    }

          if (use->discarded)
            {
              Use* u;

              u = use->get_selected_version ();
              if (u == 0) 
		{
		  /**
		     No way to find a valid use for this used package.
		     Is that a bug?
		     Anyway we don't pursue on that branch.
		   */
		  continue;
		}

              use = u;
            }

	  if (dbg)
	    {
	      for (int lll = 0; lll < level; lll++) cout << "  ";
	      cout << "  Use::get_paths." << level << "> try2 sub="
		   << use->get_package_name () << " " << use->discarded << endl;
	    }

          level++;
          bool r = use->get_paths (to, list);
          level--;

          if (r)
            {
              is_in_path = true;
            }
        }
    }

  if (is_in_path)
    {
      if (dbg)
	{
	  for (int lll = 0; lll < level; lll++) cout << "  ";
	  cout << "Use::get_paths." << level << "> push " << get_package_name () << endl;
	}
      list.push_back (this);
    }

  return (is_in_path);
}

//----------------------------------------------------------
bool Use::located () const
{
  return (m_located);
}

//----------------------------------------------------------
void Use::show_sub_uses (const cmt_string& request, bool skip_discarded)
{
  Use* current = &(Use::current ());
  int n;
  Use* use;
  static int level = 0;

  if (skip_discarded && discarded) return;

  if (level > 0)
    {
      cout << "# ";
      for (n = 0; n < (level-1); n++) cout << "  ";

      if (request == "")
	{
	  cout << "use " << get_package_name () << " " << specified_version;

	  if (this == current) 
	    {
	      cout << " (current)";
	    }
	  else
	    {
	      if (specified_path != "") cout << " " << specified_path;
	    }
	}
      else
	{
	  cout << "use " << request;
	}

      if (version_alias != "")
        {
          cout << " | " << version_alias << " " << path_alias;
        }

      if (initial_scope == ScopeUnspecified) cout << " (unspecified)";
      else if (initial_scope != ScopePublic) cout << " (private)";

      if (auto_imports == Off) cout << " (no_auto_imports)";

      if (style == no_version_style) cout << " (no_version_directory)";

      if (m_has_native_version)
        {
          cmt_string n = get_package_name ();
          n += "_native_version";

          Symbol* s = Symbol::find (n);
          if (s != 0)
            {
              cmt_string value = s->resolve_macro_value ();
              cout << " (native_version=" << value << ")";
            }
        }

      cout << endl;
    }

  if (selected) return;
  selected = true;

  level++;
  for (n = 0; n < sub_uses.size (); n++)
    {
      use = sub_uses[n];
      if (use == 0) continue;

      const cmt_string& request = requests[n];

      ScopeType saved_scope = use->initial_scope;
      State saved_state = use->auto_imports;

      use->initial_scope = sub_use_scopes[n];
      use->auto_imports = sub_use_auto_imports[n];

      use->show_sub_uses (request, skip_discarded);

      use->initial_scope = saved_scope;
      use->auto_imports = saved_state;
    }
  level--;
}

//----------------------------------------------------------
Use& Use::current ()
{
  static UseVector& instances = get_instances ();
  static Use* current_use = 0;

  if ((current_use == 0) || (instances.size () == 0))
    {
      Use& use_object = instances.add ();
      current_use = &use_object;
    }

  return (*current_use);
}

//----------------------------------------------------------
const Use& Use::const_current ()
{
  const Use& use = Use::current ();

  return (use);
}

//----------------------------------------------------------
Use::UseVector& Use::get_instances ()
{
  static Database& db = Database::instance ();
  static UseVector& instances = db.all_uses ();

  return (instances);
}

//----------------------------------------------------------
Use::UsePtrVector& Use::get_ordered_uses ()
{
  static Database& db = Database::instance ();
  static UsePtrVector& uses = db.uses ();

  return (uses);
}

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

//----------------------------------------------------------
//
//  Check if the specified version is better than the
//  current one.
//
//----------------------------------------------------------
Use* BestFitSelector::operate (Use* ref_use, Use* new_use)
{
  Use* selected = ref_use;

  int ref_v = -1;
  int ref_r = -1;
  int ref_p = -1;
  cmt_string ref_pp;

  int new_v = -1;
  int new_r = -1;
  int new_p = -1;
  cmt_string new_pp;

  int alias_v = -1;
  int alias_r = -1;
  int alias_p = -1;
  cmt_string alias_pp;

  enum { no_alias, new_has_alias, ref_has_alias } has_alias = no_alias;

  // First analyze specified versions
  cmt_string ref_version = ref_use->specified_version;
  cmt_string new_version = new_use->specified_version;

  CmtSystem::is_version_directory (ref_version, ref_v, ref_r, ref_p);
  ref_pp = ref_use->path;

  CmtSystem::is_version_directory (new_version, new_v, new_r, new_p);
  new_pp = new_use->path;

  if (new_use->version_alias != "")
    {
      has_alias = new_has_alias;
      CmtSystem::is_version_directory (new_use->version_alias, 
                                       alias_v, alias_r, alias_p);
      alias_pp = new_use->path_alias;
    }
  else if (ref_use->version_alias != "")
    {
      has_alias = ref_has_alias;
      CmtSystem::is_version_directory (ref_use->version_alias, 
                                       alias_v, alias_r, alias_p);
      alias_pp = ref_use->path_alias;
    }

  ref_use->undiscard ();
  new_use->undiscard ();

  if (new_v != ref_v)
    {
      if (has_alias != no_alias)
        {
          if (has_alias == new_has_alias)
            {
              new_v = alias_v;
              new_r = alias_r;
              new_p = alias_p;
              new_pp = alias_pp;
            }
          else if (has_alias == ref_has_alias)
            {
              ref_v = alias_v;
              ref_r = alias_r;
              ref_p = alias_p;
              ref_pp = alias_pp;
            }
        }
    }

  bool ref_v_wildcarded = ((ref_v) == -1);
  bool ref_r_wildcarded = ((ref_r) == -1);
  bool ref_p_wildcarded = ((ref_p) == -1);

  bool ref_v_explicit = !ref_v_wildcarded;
  bool ref_r_explicit = !ref_r_wildcarded;
  bool ref_p_explicit = !ref_p_wildcarded;

  bool new_v_wildcarded = ((new_v) == -1);
  bool new_r_wildcarded = ((new_r) == -1);
  bool new_p_wildcarded = ((new_p) == -1);

  bool new_v_explicit = !new_v_wildcarded;
  bool new_r_explicit = !new_r_wildcarded;
  bool new_p_explicit = !new_p_wildcarded;

  bool verbose = (CmtSystem::getenv ("CMTVERBOSE") != "");

  cmt_string ref_vc = ref_v_wildcarded ? "wildcarded" : "explicit";
  cmt_string ref_rc = ref_r_wildcarded ? "wildcarded" : "explicit";
  cmt_string ref_pc = ref_p_wildcarded ? "wildcarded" : "explicit";
  
  cmt_string new_vc = new_v_wildcarded ? "wildcarded" : "explicit";
  cmt_string new_rc = new_r_wildcarded ? "wildcarded" : "explicit";
  cmt_string new_pc = new_p_wildcarded ? "wildcarded" : "explicit";

  // Now compute effective version identifiers
  CmtSystem::is_version_directory (ref_use->version, ref_v, ref_r, ref_p);
  CmtSystem::is_version_directory (new_use->version, new_v, new_r, new_p);

  cmt_string new_selected_version = new_use->version;

  if (new_v_explicit && ref_v_explicit && (new_v != ref_v))
    {
      /*
      if (verbose && !Cmt::get_quiet ())
	{
	  cerr << "# Required explicit version " << new_version
	       << " of package " << ref_use->get_package_name () 
	       << " incompatible with selected explicit version " << ref_version
	       << endl;
	}

      CmtError::set (CmtError::version_conflict, "BestFitSelector::operate> ");

      if (ref_use != new_use) new_use->discard ();
      */

      if (new_v > ref_v)
	{
	  if (verbose && !Cmt::get_quiet ())
	    {
	      cerr << "# Select " << new_vc << " version " << new_version 
		   << " of package " << ref_use->get_package_name () 
		   << " instead of existing " << ref_vc << " " << ref_version 
		   << endl;
	    } 
	  
	  if (ref_use != new_use) ref_use->discard ();
	  selected = new_use;
	  selected->done = false; // Will read the new requirements
	}
    }
  else if (new_v_wildcarded || ref_v_wildcarded)
    {
      // at least one of ref or new is wildcarded

      //
      // we plan to discard new_use, but if it was specified as explicit 
      // and ref_use was wildcarded then new_use will win !!
      //
      // So then we'll have to understand where are the wild
      // cards... If they are on v or r, then we consider them.
      // 
      //

      if (ref_v_wildcarded && new_v_explicit)
	{
	  if ((ref_use->real_path != new_use->real_path) ||
	      (ref_use->version != new_use->version))
	    {
	      if (ref_use != new_use) ref_use->discard ();
	      selected = new_use;
	      selected->done = false; // Will read the new requirements

	      if (verbose && !Cmt::get_quiet ())
		{
		  cerr << "# Select explicit version " << new_version 
		       << "(" << new_use->version << ")" 
		       << " of package " << ref_use->get_package_name () 
		       << " instead of existing wildcarded " << ref_version 
		       << "(" << ref_use->version << ")" 
		       << endl;
		}
	    }
	  else
	    {
	      if (ref_use != new_use) new_use->discard ();
	      //ref_use->version = new_selected_version;
	    }
	}
      else
	{
	  // ref is explicit or new is wildcarded

	  /*
	  if (verbose && !Cmt::get_quiet ())
	    {
	      cerr << "# keep " << ref_vc << " version " << ref_version
		   << " of package " << ref_use->get_package_name () 
		   << " (ignore " << new_vc << " version " << new_version << ")" 
		   << endl;
	    }
	  */

	  if (ref_use != new_use) new_use->discard ();
	}
    }
  else if (new_r_wildcarded || ref_r_wildcarded || (new_r < ref_r))
    {
      //
      // we plan to discard new_use, but if it was specified as explicit 
      // and ref_use was wildcarded then new_use will win !!
      //
      // So then we'll have to understand where are the wild
      // cards... If they are on v or r, then we consider them.
      // 
      //

      if (ref_r_wildcarded && new_r_explicit)
	{
	  // ref has wild card and new has not => new wins

	  if ((ref_use->real_path != new_use->real_path) ||
	      (ref_use->version != new_use->version))
	    {
	      if (ref_use != new_use) ref_use->discard ();
	      selected = new_use;
	      selected->done = false; // Will read the new requirements

	      if (verbose && !Cmt::get_quiet ())
		{
		  cerr << "# Select explicit release " << new_version
		       << " of package " << ref_use->get_package_name ()
		       << " instead of existing wildcarded " << ref_version
		       << endl;
		} 
	    }
	  else
	    {
	      // Just adapt version with new one.

	      if (ref_use != new_use) new_use->discard ();
	      //ref_use->version = new_selected_version;
	    }
	}
      else
	{
	  /*
	  if (verbose &&!Cmt::get_quiet ())
	    {
	      cerr << "# keep " << ref_rc << " release " << ref_version 
		   << " of package " << ref_use->get_package_name () 
		   << " (ignore " << new_rc << " release " << new_version << ")" 
		   << endl;
	    }
	  */

	  if (ref_use != new_use) new_use->discard ();
	}
    }
  else if (new_r > ref_r)
    {
      if (verbose && !Cmt::get_quiet ())
	{
	  cerr << "# Select " << new_rc << " release " << new_version 
	       << " of package " << ref_use->get_package_name () 
	       << " instead of existing " << ref_rc << " " << ref_version 
	       << endl;
        } 

      if (ref_use != new_use) ref_use->discard ();
      selected = new_use;
      selected->done = false; // Will read the new requirements
    }
  else if (new_p_wildcarded || ref_p_wildcarded || (new_p < ref_p))
    {
      //
      // we plan to discard new_use, but if it was specified as explicit 
      // and ref_use was wildcarded then new_use will win !!
      //

      if (ref_p_wildcarded && new_p_explicit)
	{
	  if ((ref_use->real_path != new_use->real_path) ||
	      (ref_use->version != new_use->version))
	    {
	      if (ref_use != new_use) ref_use->discard ();
	      selected = new_use;
	      selected->done = false; // Will read the new requirements

	      if (verbose && !Cmt::get_quiet ())
		{
		  cerr << "# Select explicit patch " << new_version 
		       << " of package " << ref_use->get_package_name () 
		       << " instead of existing wildcarded " << ref_version 
		       << endl;
		}
	    }
	  else
	    {
	      if (ref_use != new_use) new_use->discard ();
	      ref_use->version = new_selected_version;
	    }
	}
      else
	{
	  /*
	  if (verbose && !Cmt::get_quiet ())
	    {
	      cerr << "# keep " << ref_pc << " patch " << ref_version 
		   << " [" << ref_use->specified_version << "]" 
		   << " of package " << ref_use->get_package_name () 
		   << " (ignore " << new_pc << " version " << new_version << ")" 
		   << " [" << new_use->specified_version << "]" 
		   << endl;
	    }
	  */

	  if (ref_use != new_use) new_use->discard ();
	}
    }
  else if (new_p > ref_p)
    {
      if (verbose && !Cmt::get_quiet ())
        {
	  cerr << "# Select " << new_pc << " patch " << new_version 
	       << " of package " << ref_use->get_package_name () 
	       << " instead of existing " << ref_pc << " " << ref_version 
	       << endl;
        }

      if (ref_use != new_use) ref_use->discard ();
      selected = new_use;
      selected->done = false; // Will read the new requirements
    }
  else if (new_pp != ref_pp) // same version-r-p but from different path
    {
      if (ref_use != new_use) ref_use->discard ();
      selected = new_use;
      selected->done = false; // Will read the new requirements
    }

  return (selected);
}






Package* Package::find (const cmt_string& name)
{
  static PackageMap& PackageMap = package_map ();

  Package* result = 0;

  result = PackageMap.find (name);

  return (result);
}

Package* Package::add (const cmt_string& name)
{
  static PackageVector& Packages = packages ();
  static PackageMap& PackageMap = package_map ();

  {
    Package* package;

    package = find (name);
    if (package != 0) return (package);
  }

  Package& package = Packages.add ();
  PackageMap.add (name, package);

  package.m_name = name;

  if (name == "CMT")
    {
      package.m_is_cmt = true;
    }
  else if (name == "methods")
    {
      package.m_is_cmt = true;
    }

  if (Cmt::get_debug ())
    {
      cout << "Package::add (" << name << ")" << endl;
    }

  return (&package);
}

Package::PackageVector& Package::packages ()
{
  static Database& db = Database::instance ();
  static PackageVector& Packages = db.packages ();

  return (Packages);
}

Package::PackageMap& Package::package_map ()
{
  static Database& db = Database::instance ();
  static PackageMap& PackageMap = db.package_map ();

  return (PackageMap);
}

void Package::clear_all ()
{
  static PackageVector& Packages = packages ();
  static PackageMap& PackageMap = package_map ();

  PackageMap.clear ();
  Packages.clear ();
}

Package::Package () : m_is_cmt (false)
{
  if (Cmt::get_debug ())
    {
      cout << "Package::Package" << endl;
    }
}

Package::~Package ()
{
  m_name = "";
}

const cmt_string& Package::get_name () const
{
  return (m_name);
}

void Package::add_use (Use* use)
{
  for (int i = 0; i < m_uses.size (); i++)
    {
      Use* u = m_uses[i];
      if (u == use) return;
    }

  m_uses.push_back (use);
}

void Package::remove_use (Use* use)
{
  if (Cmt::get_debug ())
    {
      cout << "Package::remove_use (" << use->get_package_name () << ")" << endl;
      cout << "  name=" << m_name
	   << " uses=" << m_uses.size () << endl;
    }

  Use::UsePtrVector temp;

  temp = m_uses;

  m_uses.clear ();

  for (int i = 0; i < temp.size (); i++)
    {
      Use* u = temp[i];
      if (u != use)
	{
	  m_uses.push_back (u);
	}
    }
}

Use::UsePtrVector& Package::get_uses ()
{
  return (m_uses);
}

bool Package::is_cmt ()
{
  return (m_is_cmt);
}

static void show_packages ()
{
  Package::PackageVector& vector = Package::packages ();

  int i;
  int j;

  cout << "### Packages: ";
  for (i = 0; i < vector.size (); i++)
    {
      Package& p = vector[i];
      cout << p.get_name () << "[";
      Use::UsePtrVector& uses = p.get_uses ();
      for (j = 0; j < uses.size (); j++)
	{
	  Use* u = uses[j];
	  cout << u << ",";
	}

      cout << "] ";
    }
  cout << endl;

  {
    static Use::UsePtrVector& uses = Use::get_ordered_uses ();

    cout << "### Uses: ";
    for (i = 0; i < uses.size (); i++)
      {
	Use* u = uses[i];
	cout << "[" << u << "]" << u->get_package_name () << " ";
      }
  }

  cout << endl;
}

