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

#include "cmt_database.h"
#include "cmt_cmtpath_pattern.h"
#include "cmt_syntax.h"

//----------------------------------------------------------
//
//  Operations on CmtPathPatterns
//
//----------------------------------------------------------

void CmtPathPattern::action (const CmtSystem::cmt_string_vector& words, Use* use, bool revert)
{
  if (words.size () < 1) return;

  //
  // expected syntax is:
  //
  //  cmtpath_pattern any-cmt-statement
  //
  // where any-cmt-statement may contain the "template"
  //
  //      <path>
  //      <project>
  //

  add (words, use, revert);

  if (Cmt::get_debug ())
    {
      cout << "CmtPathPattern::action> add " << endl;
    }
}

void CmtPathPattern::add (const CmtSystem::cmt_string_vector& words, Use* use, bool revert)
{
  static CmtPathPatternVector& CmtPathPatterns = patterns ();

  CmtPathPattern& p = CmtPathPatterns.add ();

  p.clear ();

  p.use  = use;
  p.revert  = revert;

  int first_word = 1;

  //
  // Install the cmt-statement as a vector of words.
  //
  for (int i = first_word; i < words.size (); i++)
    {
      bool need_quotes = (i > (first_word + 1));

      cmt_string& s = words[i];

      if (i > first_word) p.line += " ";

      if (s == ";") first_word = i+1;

      if ((s == "\n") | (s == ";"))
        {
          p.line += "\n  ";
        }
      else
        {
	  cmt_string sep = "\"";

	  if (s.find (sep) != cmt_string::npos)
	    {
	      sep = "\'";
	    }

	  if (!need_quotes) sep = "";

          p.line += sep;
          p.line += s;
          p.line += sep;
        }
    }
}

/**
 * Get the number of registered patterns
 */
int CmtPathPattern::pattern_number ()
{
  static CmtPathPatternVector& CmtPathPatterns = patterns ();

  return (CmtPathPatterns.size ());
}

/**
 * Get the index'th pattern in the database
 */
CmtPathPattern& CmtPathPattern::pattern (int index)
{
  static CmtPathPatternVector& CmtPathPatterns = patterns ();

  return (CmtPathPatterns[index]);
}

void CmtPathPattern::clear_all ()
{
  static CmtPathPatternVector& CmtPathPatterns = patterns ();

  for (int i = 0; i < CmtPathPatterns.size (); i++)
    {
      CmtPathPattern& p = CmtPathPatterns[i];
      p.clear ();
    }

  CmtPathPatterns.clear ();
}

CmtPathPattern::CmtPathPatternVector& CmtPathPattern::patterns ()
{
  static Database& db = Database::instance ();
  static CmtPathPatternVector& CmtPathPatterns = db.cmtpath_patterns ();

  return (CmtPathPatterns);
}

/**
 * Applies all patterns to all CMTPATH item
 */
void CmtPathPattern::apply_all ()
{
  static CmtPathPatternVector& CmtPathPatterns = patterns ();

  int i;

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

      p.apply ();
    }
}

/**
 * this is the cmt show cmtpath_patterns command
 * It just shows the cmtpath_pattern declarations.
 */
void CmtPathPattern::show_all ()
{
  static CmtPathPatternVector& CmtPathPatterns = patterns ();

  int i;

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

      cout << "# " << p.use->get_package_name () << " " << p.use->version 
	   << " adds a cmtpath_pattern as " << endl;
      cout << "  " << p.line << endl;
    }
}

//----------------------------------------------------------
CmtPathPattern::CmtPathPattern ()
{
}

//----------------------------------------------------------
CmtPathPattern::~CmtPathPattern ()

{
}

//----------------------------------------------------------
void CmtPathPattern::clear ()
{
  use = 0;
  line = "";
}

class CmtPathPatternProjectAction : public IProjectAction
{
public:
  CmtPathPatternProjectAction (const CmtPathPattern& pattern, Use& use) : 
    m_pattern (pattern), 
    m_current (use)
  {
  }

  bool run (const Project& project)
  {
    const cmt_string& pname = project.get_name ();
    const cmt_string& p = project.get_cmtpath ();
    const cmt_string& s = project.get_cmtpath_source ();

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

    m_pattern.expand (m_buffer, p, pname);

    if (Cmt::get_debug ())
      {
	cout << "CmtPathPattern::apply> text=[" << m_buffer << "]" << endl;
      }

    SyntaxParser::parse_requirements_text (m_buffer, "", &m_current);
    m_buffer = "";

    return (true);
  }

private:

  const CmtPathPattern& m_pattern;
  Use& m_current;
  cmt_string m_buffer;
};


/**
 *   Applies a pattern to all CMTPATH entries.
 */
void CmtPathPattern::apply () const
{
  if (Cmt::get_debug ())
    {
      cout << "CmtPathPattern::apply> cmtpath_pattern defined in " << use->get_package_name () << endl;
    }

  Use& current_use = Use::current ();

  bool is_constant = ((line.find ("<path>") == cmt_string::npos) &&
		      (line.find ("<project>") == cmt_string::npos));

  if (is_constant)
    {
      cmt_string buffer;

      expand (buffer, "", "");

      if (Cmt::get_debug ())
	{
	  cout << "CmtPathPattern::apply> text=[" << buffer << "]" << endl;
	}

      SyntaxParser::parse_requirements_text (buffer, "", &current_use);
      buffer = "";
    }
  else
    {
      CmtPathPatternProjectAction pa (*this, current_use);

      if (revert)
	{
	  Project::broadcast (pa);
	}
      else
	{
	  Project::reverse_broadcast (pa);
	}
    }
}

void CmtPathPattern::expand (cmt_string& replacement, 
			     const cmt_string& path, 
			     const cmt_string& project) const
{
  replacement = line;

  if (replacement != "")
    {
      // Substitute <path> template from the cmt statement
      replacement.replace_all ("<path>", path.c_str ());
      replacement.replace_all ("<project>", project.c_str ());
    }
}