#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"

static Use* CurrentUse = 0;

//----------------------------------------------------------
class VersionSelector
{
public:
  static VersionSelector& instance ();

  virtual Use* operate (Use* ref_use, Use* new_use)
  {
    return (ref_use);
  }
};

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

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

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

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

class KeepAllSelector : public VersionSelector
{
public:
  Use* operate (Use* ref_use, Use* new_use);
};
//----------------------------------------------------------

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

//----------------------------------------------------------
void Use::select_clients (const cmt_string& package,
                          const cmt_string& version)
{
  static UsePtrVector& Uses = 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_all (const cmt_string& prefix, bool skip_discarded)
{
  static UsePtrVector& Uses = uses ();

  Use* use;
  int number;

  unselect_all ();

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

  if (Uses.size () > 0)
    {
      if (!Cmt::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->real_path == "")
            {
              if (!Cmt::quiet)
                {
                  cout << "# package " << use->package <<
                      " " << use->version << " " << use->path << 
                      " not found" <<
                      endl;
                }
              CmtError::set (CmtError::package_not_found, use->package);
            }
          else
            {
              cout << prefix << use->package <<
                " " << use->version <<
                " " << use->real_path <<
                endl;
            }
        }

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

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

class use_action_iterator
{
public:

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

  void set (const cmt_string& w)
      {
        if (w == "-auto_imports")
          {
            auto_imports = request_auto_imports;
          }
        else if (w == "-no_auto_imports")
          {
            auto_imports = no_auto_imports;
          }
        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;
                  state = finished;
                  break;
                case need_version_alias:
                  version_alias = w;
                  state = need_path_alias;
                  break;
                case need_path_alias:
                  path_alias = w;
                  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);

        return (true);
      }

  Use* get_use (Use* use)
      {
        static Use::UsePtrVector& Uses = Use::uses ();

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

        if (Cmt::debug)
          {
            int i;

            cout << "use::action1> current=" << use->package <<
                " package=" << package << " ";

            for (i = 0; i < Uses.size (); i++)
              {
                Use* use = Uses[i];
                cout << use->package << " ";
              }
            cout << endl;
          }

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

        if ((use != 0) && 
            (use->package != cu.package) &&
            (Cmt::scope == ScopePrivate))
          {
            return (0);
          }


          // Here the version may contain wild cards
        Use* new_use = Use::add (path, package, version, 
                                 version_alias, path_alias, use);

        switch (auto_imports)
          {
            case keep_auto_imports:
              break;
            case request_auto_imports:
              new_use->auto_imports = true;
              break;
            case no_auto_imports:
              new_use->auto_imports = false;
              break;
          }

        Use::reorder (new_use, use);

        if (Cmt::debug)
          {
            int i;
            
            cout << "use::action2> current=" << use->package <<
                " package=" << package << " ";
            for (i = 0; i < Uses.size (); i++)
              {
                Use* use = Uses[i];
                cout << use->package << " ";
              }
            cout << endl;
          }

        return (new_use);
      }

private:

  enum
  {
    need_package,
    need_version,
    need_path,
    need_version_alias,
    need_path_alias,
    finished
  } state;
  
  enum
  {
    keep_auto_imports,
    request_auto_imports,
    no_auto_imports
  } auto_imports;

  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* use)
{
  Use* new_use;

  //
  // complete syntax : "use <package> <version> <path>"
  // 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 (use);
  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)
{
  static UsePtrVector& Uses = uses ();
  static UseVector& AllUses = all_uses ();

  int use_index;

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

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

      if (use.package == package)
        {
            // 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);
}

//----------------------------------------------------------
//
//  Move use to the end
//
//----------------------------------------------------------
void Use::move (Use* use1)
{
  static UsePtrVector& Uses = uses ();

  int use_index;
  Use* use;
  int found = 0;

  if (Uses.size () == 0) return;
  if (use1 == 0) return;

  //
  // On se positionne sur le pointeur.
  //
  for (use_index = 0; use_index < Uses.size (); use_index++)
    {
      use = Uses[use_index];

      if (use == use1)
        {
          found = 1;
          break;
        }
    }

  if (!found) return;

  //
  // On deplace tous les pointeurs d'une case en arriere
  //
  for (use_index++;
       use_index < Uses.size ();
       use_index++)
    {
      Uses[use_index - 1] = Uses[use_index];
    }

  //
  // use1 est donc replace en derniere position
  //
  {
    Uses[Uses.size () - 1] = use1;
  }
}

//----------------------------------------------------------
void Use::reorder (Use* use1, Use* use2)
{
  static UsePtrVector& Uses = uses ();

  int use_index;
  int index1 = -1;
  int index2 = -1;
  Use* use;

  if (Uses.size () == 0) return;
  if (use1 == use2) return;

  //
  // First locate the two use objects into the Uses vector.
  //   -> index1 and index 2
  //
  for (use_index = 0; use_index < Uses.size (); use_index++)
    {
      use = (Use*) Uses[use_index];

      if (use == use1) index1 = use_index;
      if (use == use2) index2 = use_index;
    }

  if (Cmt::debug)
    {
      cout << "Use::reorder> 1=" << index1 << " 2=" << index2 << endl;
    }

  //
  // Both objects must be installed in Uses before acting.
  //
  if (index1 == -1) return;
  if (index2 == -1) return;

  if (index2 < index1)
    {
      //
      // 2 is already before 1 so job is finished
      //
      return;
    }
  else
    {
      //
      // before : <aaa 1 bbbb 2 ccc>
      //
      //  o move "1 bbbb" by one place to the right
      // thus : <aaa 1 1 bbbb ccc>
      //
      //  o move "2" to [1]
      //
      // after  : <aaa 2 1 bbbb ccc>
      //

      use = use2;

      for (use_index = index2 - 1; use_index >= index1; use_index--)
        {
          Uses[use_index + 1] = Uses[use_index];
        }

      Uses[index1] = use;
    }
}

//----------------------------------------------------------
void Use::clear_all ()
{
  static UsePtrVector& Uses = uses ();
  static UseVector& AllUses = all_uses ();

  int use_index;

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

  Uses.clear ();
  AllUses.clear ();
  CurrentUse = 0;
}

//----------------------------------------------------------
void Use::unselect_all ()
{
  static UsePtrVector& Uses = 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->unselect ();
        }
    }
}

//----------------------------------------------------------
void Use::undiscard_all ()
{
  static UsePtrVector& Uses = 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 = 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];
      
      if (use->package == "CMT") continue;
      if (use->package == "methods") continue;
      if (use->discarded) continue;
      if (!use->auto_imports) continue;
      
      use->fill_macro (buffer, suffix);
    }
  
  buffer += "\"";
}

//----------------------------------------------------------
Use::Use ()
{
  done = false;
  discarded = false;
  auto_imports = true;

  clear ();
}

//----------------------------------------------------------
Use::Use (const cmt_string& new_package,
          const cmt_string& new_version,
          const cmt_string& new_path)
{
  auto_imports = true;
  set (new_package, new_version, new_path);
}

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

//----------------------------------------------------------
void Use::clear ()
{
  specified_path = "";
  path      = "";
  package   = "";
  version   = "";
  author    = "";
  manager   = "";
  real_path = "";

  prefix    = "";
  style     = mgr_style;
  cmt_scope = Cmt::scope;
  scope     = Cmt::scope;
  done      = false;
  discarded = false;
  selected  = false;
  auto_imports = true;

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

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

  version_alias = "";
  path_alias    = "";
}

//----------------------------------------------------------
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           = new_package;
  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;
  cmt_scope         = Cmt::scope;
  scope             = Cmt::scope;
  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 ("\\", "/");
    }
}

//----------------------------------------------------------
int Use::reach_package (const cmt_string& from_path)
{
  //cerr << "Use::reach_package> (" << package << " " << 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);

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

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

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

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

  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;
          
          for (i = 0; i < versions.size (); i++)
            {
              const cmt_string& vers = versions[i];
              
              if (Cmt::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;
                }
            }
        }

      //cerr << "  ... 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);
    }

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

  return (1);
}

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

      if (Cmt::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::debug)
            {
              cout << "move_to2> " << expanded_path << endl;
            }

          change_path (expanded_path);
          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::debug)
            {
              cout << "move_to3> " << expanded_path << endl;
            }

          change_path (expanded_path);
          return (true);
        }
    }
      
  //
  // Second try is among the CMTPATHs
  //
      
  static const CmtSystem::cmt_string_vector& search_path = Cmt::cmt_path;
  int path_index = 0;
      
  for (path_index = 0; path_index < search_path.size (); path_index++)
    {
      const cmt_string& next_path = search_path[path_index];
      
      if (reach_package (next_path))
        {
          if (Cmt::debug)
            {
              cout << "move_to4> " << next_path << endl;
            }

          change_path (next_path);
          return (true);
        }
    }
  
  if (select_alternate ()) 
    {
      if (Cmt::debug)
        {
          cout << "move_to5> " << real_path << endl;
        }

      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 " << package << 
              " 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 " << package << 
                      " sv=" << specified_version << 
                      " v=" << version << endl;
                }
                */

              if (Cmt::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* use;

  // find any possible existing version for that package
  use = find (package);
  if (old_use != 0) *old_use = use;

  if (use == 0)
    {
      // No version was not found at all
      return (true);
    }

  //cerr << "Use::need_new> (" << package << ") requested version=" << version << " vs existing=" << use->version << " (specified as " << use->specified_version << ")" << endl;

  // There was a version. We want to install a priority to the
  // explicit specifications over wildcarded ones.
  //  So we say that :
  //
  //    - if the existing spec. was specified as explicit, and the new
  //      request is wildcarded, then the new one is discarded.
  //
  //    - if the existing spec. was specified as wildcarded and the new 
  //      request is explicit, then the new one wins.
  //

  if ((version.find ("*") != cmt_string::npos) &&
      (use->specified_version.find ("*") == cmt_string::npos))
    {
      //cerr << "  ... wild card loses against existing specific" << endl;
      return (false);
    }

  if ((version.find ("*") == cmt_string::npos) &&
      (use->specified_version.find ("*") != cmt_string::npos))
    {
      //cerr << "  ... specific should win against existing wildcarded" << endl;
      return (true);
    }

  if (version == use->specified_version)
    {
      if (path == use->specified_path)
        {
            // exactly same version and path!
          return (false);
        }
      else
        {
            // same version but different path.
          return (true);
        }
    }

  // at least one version has been found (different from what is requested), 
  // can we find the exact match ?

  use = find (package, version, path);

  if (use == 0)
    {
      // The required version was not found
      return (true);
    }

  if (path != use->specified_path)
    {
      // The required version was found but on another path
      return (true);
    }

  // The required version was exactly found

  return (false);
}

//----------------------------------------------------------
//
//  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)
{
  static UseVector& AllUses = all_uses ();

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

      if (use.package == package)
        {
          if ((use.specified_version == version) && 
              (use.specified_path == path)) return (&use);
        }
    }

  // We now really create a new Use entry.

  Use& use_object = AllUses.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,
               const cmt_string& version,
               const cmt_string& version_alias,
               const cmt_string& path_alias,
               Use* context_use)
{
  static UsePtrVector& Uses = uses ();

  bool do_need_new = false;

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

  do_need_new = need_new (path, package, version, &old_use);

  /*
    if (old_use != 0)
    {
    cout << "add> old_use " << old_use->package <<
    " " << old_use->version <<
    " " << old_use->path <<
    endl;
    }
  */

  if (do_need_new)
    {
      use = create (path, package, 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 == CmtSystem::get_home_package ())
    {
      return (use);
    }

  if (package == CmtSystem::get_user_context_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);
    }

  bool found = use->move_to ();

  if (Cmt::debug)
    {
      cout << "add> use " << use->package <<
          " " << use->version <<
          " " << use->path <<
          " found=" << found <<
          endl;
    }
      
  if (old_use != 0)
    {
        /*
      if (CmtSystem::getenv ("CMTTESTUSEWILDCARDS") != "")
      {
      cout << "select? [" << use << "] vs old_use[" << old_use << "] " << old_use->package <<
      " " << old_use->version <<
      " " << old_use->path <<
      endl;
      }
        */

      if (use != old_use)
        {
            //
            //  This new version is different from the old one
            // thus we have to choose
            //
          VersionSelector& selector = VersionSelector::instance ();
          Use* selected_use = selector.operate (old_use, use);

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

  {
    bool registered = false;

      //
      // A pointer to this new object is also added or replaced.
      //
    for (int i = 0; i < Uses.size(); i++)
      {
        Use* u = Uses[i];
        
        if (u->package == package)
          {
            registered = true;
            Uses[i] = use;
            break;
          }
      }
    
    if (!registered) Uses.push_back (use);
  }

    //
    // 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)
    {
      if (!Cmt::quiet)
        {
          cout << "# package " << use->package <<
              " " << use->version << " " << use->path << 
              " not found" <<
              endl;
        }
      CmtError::set (CmtError::package_not_found, use->package);
    }
  
  if (found && !use->done && Cmt::recursive)
    {
      use->done = true;

        /*
      if (CmtSystem::getenv ("CMTTESTUSEWILDCARDS") != "")
        {
          for (int use_index = 0; use_index < Uses.size (); use_index++)
            {
              Use* u = (Use*) Uses[use_index];
              cout << "  use[" << use_index << "] p=" << u->package <<
                  " v=" << u->version <<
                  " discarded=" << u->discarded <<
                  " selected=" << u->selected <<
                  endl;
            }

          cout << "parsing at " << CmtSystem::pwd () << endl;
        }
        */

        /**
           Get the previous set of effective use statements
           that might have been obtained in a previous build.
           This is stored in uses.log. Ignored if file does not exist.
        */

      //Cmt::parse_requirements ("uses.log", use);

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

      Cmt::parse_requirements ("requirements", use);
    }

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

//----------------------------------------------------------
bool Use::is_client (const cmt_string& used_package,
                     const cmt_string& used_version)
{
  if ((package == used_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->package == used_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;
}

//----------------------------------------------------------
void Use::fill_includes_macro (cmt_string& buffer) const
{
  if (include_path == "")
    {
      buffer += "$(ppcmd)\"$(";
      buffer += package;
      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];
      
      buffer += "$(ppcmd)\"";
      buffer += incl.name;
      buffer += "\" ";
    }
}

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

/**
 *   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 ()
{
  static Use::UsePtrVector& Uses = uses ();

    //cout << "get_selected_version for package " << package << endl;

  if (!discarded) return (this);

  for (int i = 0; i < Uses.size (); i++)
    {
      Use* u = Uses[i];
      if (u == 0) continue;
      if (u->discarded) continue;
      if (u->package == package) 
        {
            //cout << "  got a version" << endl;
          return (u);
        }
    }

  return (0);
}

//----------------------------------------------------------
bool Use::get_paths (Use* to, UsePtrVector& list)
{
  bool found = false;
  bool cycle = false;

  static int level = 0;
  static UsePtrVector stack;

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

  for (int k = 0; k < stack.size (); k++)
    {
      Use* u = stack[k];
      if (u == this) return (false);
    }

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

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

  if (Cmt::debug)
    {
      cout << "Use::get_paths." << level << ">" << package << " list[" << list.size () << "]" << endl;
    }

  if (this == to)
    {
      found = true;
    }
  else
    {
      for (int n = 0; n < sub_uses.size (); n++)
        {
          Use* use = sub_uses[n];

          if (use == 0) continue;

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

              u = use->get_selected_version ();
              if (u == 0) continue;

              use = u;
            }

          cycle = false;
      
            // This use must not be already in the list (protection against cycles)

          for (int m = 0; m < list.size (); m++)
            {
              Use* u = list[m];
              if (u == use)
                {
                  cycle = true;
                  break;
                }
            }

          if (cycle) 
            {
              found = true;
              continue;
            }

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

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

  if (found)
    {
      cycle = false;

      for (int m = 0; m < list.size (); m++)
        {
          Use* u = list[m];
          if (u == this)
            {
              cycle = true;
              break;
            }
        }

      if (!cycle)
        {
          list.push_back (this);
        }
    }

  return (found);  
}

//----------------------------------------------------------
void Use::show_sub_uses (bool skip_discarded)
{
  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 << "  ";
      cout << "use " << package << " " << specified_version;
      if (specified_path != "") cout << " " << specified_path;
      if (version_alias != "")
        {
          cout << " | " << version_alias << " " << path_alias;
        }
      if (scope == ScopeUnspecified) cout << " unspecified";
      else if (scope == ScopePublic) cout << " public";
      else cout << " private";
      cout << endl;
    }

  if (selected) return;
  selected = true;

  level++;
  for (n = 0; n < sub_uses.size (); n++)
    {
      use = sub_uses[n];
      if (use != 0) use->show_sub_uses (skip_discarded);
    }
  level--;
}

//----------------------------------------------------------
Use& Use::current ()
{
  static UseVector& AllUses = all_uses ();

  if (CurrentUse == 0)
    {
      Use& use_object = AllUses.add ();
      CurrentUse = &use_object;
    }

  return (*CurrentUse);
}

//----------------------------------------------------------
const Use& Use::const_current ()
{
  static UseVector& AllUses = all_uses ();

  if (CurrentUse == 0)
    {
      Use& use_object = AllUses.add ();
      CurrentUse = &use_object;
    }

  const Use& use = *CurrentUse;

  return (use);
}

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

  return (AllUses);
}

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

  return (Uses);
}

//----------------------------------------------------------
VersionSelector& VersionSelector::instance ()
{
  static BestFitSelector best_fit;
  static BestFitNoCheckSelector best_fit_no_check;
  static FirstChoiceSelector first_choice;
  static LastChoiceSelector last_choice;
  static KeepAllSelector keep_all;

  switch (Cmt::current_strategy)
    {
    case BestFit:
      return (best_fit);
    case BestFitNoCheck:
      return (best_fit_no_check);
    case FirstChoice:
      return (first_choice);
    case LastChoice:
      return (last_choice);
    case KeepAll:
      return (keep_all);
    default:
      return (best_fit);
    }
}

//----------------------------------------------------------
//
//  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 old_v = -1;
  int old_r = -1;
  int old_p = -1;
  cmt_string old_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;

  CmtSystem::is_version_directory (ref_use->version, old_v, old_r, old_p);
  old_pp = ref_use->path;

  CmtSystem::is_version_directory (new_use->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 != old_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)
            {
              old_v = alias_v;
              old_r = alias_r;
              old_p = alias_p;
              old_pp = alias_pp;
            }
        }
    }

  if (new_v != old_v)
    {
      if (!Cmt::quiet)
        cout << "# Required version " << new_use->version <<
          " of package " << ref_use->package <<
          " incompatible with selected version " << ref_use->version <<
          endl;

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

      if (ref_use != new_use) new_use->discard ();
    }
  else if (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.
      // 
      //


      bool new_is_wildcarded = false;
      bool ref_is_wildcarded = false;

      if (new_use->specified_version.find ("*") != cmt_string::npos)
	{
	  int nv = -1;
	  int nr = -1;
	  int np = -1;

	  CmtSystem::is_version_directory (new_use->specified_version, nv, nr, np);
	  if ((nv == -1) || (nr == -1)) new_is_wildcarded = true;
	}

      if (ref_use->specified_version.find ("*") != cmt_string::npos)
	{
	  int nv = -1;
	  int nr = -1;
	  int np = -1;

	  CmtSystem::is_version_directory (ref_use->specified_version, nv, nr, np);
	  if ((nv == -1) || (nr == -1)) new_is_wildcarded = true;
	}

      if (!ref_is_wildcarded && new_is_wildcarded)
	{
	  if (ref_use != new_use) ref_use->discard ();
	  selected = new_use;
	  selected->done = false; // Will read the new requirements
	}
      else
	{
	  if (!Cmt::quiet)
	    cout << "# keep release " << ref_use->version <<
	      " of package " << ref_use->package <<
	      " (ignore release " << new_use->version << ")" <<
	      endl;

	  if (ref_use != new_use) new_use->discard ();
	}
    }
  else if (new_r > old_r)
    {
      if (!Cmt::quiet)
        {
          cout << "# Select release " << new_use->version <<
            " of package " << ref_use->package <<
            " instead of existing " << ref_use->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 > old_p)
    {
      if (!Cmt::quiet)
        {
          cout << "# Select patch " << new_use->version <<
            " of package " << ref_use->package <<
            " instead of existing " << ref_use->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 != old_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);
}

//----------------------------------------------------------
//
//  Check if the specified version is better than the
//  current one. We don't check major ids incompatibilities
//
//----------------------------------------------------------
Use* BestFitNoCheckSelector::operate (Use* ref_use, Use* new_use)
{
  Use* selected = ref_use;

  int old_v = -1;
  int old_r = -1;
  int old_p = -1;
  cmt_string old_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;

  CmtSystem::is_version_directory (ref_use->version, old_v, old_r, old_p);
  old_pp = ref_use->path;

  CmtSystem::is_version_directory (new_use->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 != old_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)
            {
              old_v = alias_v;
              old_r = alias_r;
              old_p = alias_p;
              old_pp = alias_pp;
            }
        }
    }

  if (new_v < old_v)
    {
      if (!Cmt::quiet)
        {
          cout << "# Keep version " << ref_use->version <<
            " of package " << ref_use->package <<
            " (ignore version " << new_use->version << ")" <<
            endl;
        }

      if (ref_use != new_use) new_use->discard ();
    }
  else if (new_v > old_v)
    {
      if (!Cmt::quiet)
        {
          cout << "# Select version " << new_use->version <<
            " of package " << ref_use->package <<
            " instead of existing " << ref_use->version <<
            endl;
        }

      if (ref_use != new_use) ref_use->discard ();
      selected = new_use;
      selected->done = false; // Will read the new requirements
    }
  else if (new_r < old_r)
    {
      if (!Cmt::quiet)
        {
          cout << "# keep release " << ref_use->version <<
            " of package " << ref_use->package <<
            " (ignore release " << new_use->version << ")" <<
            endl;
        }

      if (ref_use != new_use) new_use->discard ();
    }
  else if (new_r > old_r)
    {
      if (!Cmt::quiet)
        {
          cout << "# Select release " << new_use->version <<
            " of package " << ref_use->package <<
            " instead of existing " << ref_use->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 > old_p)
    {
      if (!Cmt::quiet)
        {
          cout << "# Select patch " << new_use->version <<
            " of package " << ref_use->package <<
            " instead of existing " << ref_use->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 != old_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);
}

//----------------------------------------------------------
Use* FirstChoiceSelector::operate (Use* ref_use, Use* new_use)
{
  ref_use->undiscard ();
  new_use->undiscard ();

  new_use->done = false; // Will read the new requirements
  if (ref_use != new_use) new_use->discard ();
  return (ref_use);
}

//----------------------------------------------------------
Use* LastChoiceSelector::operate (Use* ref_use, Use* new_use)
{
  ref_use->undiscard ();
  new_use->undiscard ();

  new_use->done = false; // Will read the new requirements
  if (ref_use != new_use) ref_use->discard ();
  return (new_use);
}

//----------------------------------------------------------
Use* KeepAllSelector::operate (Use* ref_use, Use* new_use)
{
  ref_use->undiscard ();
  new_use->undiscard ();

  new_use->done = false; // Will read the new requirements
  return (new_use);
}
