#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "cmt_pattern.h"
#include "cmt_use.h"
#include "cmt_database.h"
#include "cmt_error.h"

//----------------------------------------------------------
//
//  Operations on Patterns
//
//----------------------------------------------------------

//----------------------------------------------------------
void Pattern::action (const CmtSystem::cmt_string_vector& words, Use* use)
{
  bool global = false;
  int start_index;

  if (words.size () < 2) return;

    //
    // expected syntax is:
    //
    //  pattern [-global] pattern-name any-cmt-statement
    //
    // where any-cmt-statement may contain "templates"
    //
    //      <package>
    //      <PACKAGE>
    //      <version>
    //      <path>
    //

  cmt_string& option = words[1];

  if (option == "-global")
    {
      global = true;
      start_index = 2;
    }
  else
    {
      start_index = 1;
    }

  cmt_string& name = words[start_index];

  start_index++;

  add (name, words, start_index, global, use);

  if (CmtSystem::testenv ("CMTTESTPATTERN"))
    {
      cout << "Pattern::action> add " << name << endl;
    }
}

/**
 *
 *  Patterns are stored with two keys : <name> and <use>
 *
 *  thus search must done against these two keys.
 *
 */
Pattern* Pattern::find (const cmt_string& name)
{
  Use::UsePtrVector& uses = Use::uses ();

  Use* use = &(Use::current ());

  Pattern* p;

  p = find (name, use);
  if (p != 0) return (p);

  for (int i = 0; i < uses.size (); i++)
    {
      use = uses[i];
      p = find (name, use);
      if (p != 0) return (p);
    }

  return (0);
}

//----------------------------------------------------------
Pattern* Pattern::find (const cmt_string& name, Use* use)
{
  static PatternVector& Patterns = patterns ();

  int pattern_index;
  Pattern* pattern;

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

  for (pattern_index = 0; pattern_index < Patterns.size (); pattern_index++)
    {
      pattern = &(Patterns[pattern_index]);

      if ((pattern->name == name) && (pattern->use) == use)
        {
          return (pattern);
        }
    }

  return (0);
}

//----------------------------------------------------------
void Pattern::add (const cmt_string& name,
                   const CmtSystem::cmt_string_vector& words,
                   int start_index,
                   bool global,
                   Use* use)
{
  static PatternVector& Patterns = patterns ();

  Pattern* pattern;

  pattern = find (name, use);

  if (pattern == 0)
    {
        // No pattern for the pair <name, use> exist yet.
        // create one.
      Pattern& p = Patterns.add ();

      p.clear ();

      p.name = name;
      p.use  = use;

      pattern = &p;
    }
  else
    {
      pattern->clear ();
    }

    //
    // Install the cmt-statement as a vector of words.
    //
  for (int i = start_index; i < words.size (); i++)
    {
      cmt_string& s = pattern->words.add ();
      s = words[i];
    }

  pattern->global = global;
}

/**
 * Get the number of registered patterns
 */
int Pattern::pattern_number ()
{
  static PatternVector& Patterns = patterns ();

  return (Patterns.size ());
}

/**
 * Get the index'th pattern in the database
 */
Pattern& Pattern::pattern (int index)
{
  static PatternVector& Patterns = patterns ();

  return (Patterns[index]);
}

//----------------------------------------------------------
void Pattern::clear_all ()
{
  static PatternVector& Patterns = patterns ();

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

  Patterns.clear ();
}

//----------------------------------------------------------
Pattern::PatternVector& Pattern::patterns ()
{
  static Database& db = Database::instance ();
  static PatternVector& Patterns = db.patterns ();

  return (Patterns);
}

/**
 * Applies all global patterns to all uses
 */
void Pattern::apply_all_globals ()
{
  static PatternVector& Patterns = patterns ();

  int i;

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

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

/**
 * Applies all global patterns to a given Use
 */
void Pattern::apply_all_globals (Use* use)
{
  if (use->package == "CMT") return;

  static PatternVector& Patterns = patterns ();

  int i;

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

      if (p.global)
        {
          if (IgnorePattern::find (p.name, use) == 0) p.apply (use);
        }
    }
}

/**
 * this is the cmt show patterns command
 * It just shows the pattern declarations.
 */
void Pattern::show_all ()
{
  static PatternVector& Patterns = patterns ();

  int i;

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

      cout << "# " << p.use->package << " " << p.use->version << " defines ";
      if (p.global) cout << "global ";
      cout << "pattern " << p.name << " as [";

      for (int wi = 0; wi < p.words.size (); wi++)
        {
          const cmt_string& s = p.words[wi];
          if (wi > 0) cout << " ";
          cout << s;
        }

      cout << "]" << endl;
    }

  Use* use = &(Use::current ());
  for (i = 0; i < use->apply_patterns.size (); i++)
    {
      const ApplyPattern& apply_pattern = use->apply_patterns[i];

      cout << "# " << use->package
           << " applies pattern " << apply_pattern.name << " => ";
      apply_pattern.show ();
    }
}

/**
 * this is the cmt show applied_patterns command
 * It just shows the pattern applications.
 */
void Pattern::show_all_applied_patterns ()
{
  Use* use = &(Use::current ());
  for (int i = 0; i < use->apply_patterns.size (); i++)
    {
      const ApplyPattern& apply_pattern = use->apply_patterns[i];

      cout << "# " << use->package
           << " applies pattern " << apply_pattern.name << " => ";
      apply_pattern.show ();
    }
}

//----------------------------------------------------------
void Pattern::show_all_names ()
{
  static PatternVector& Patterns = patterns ();

  if (Patterns.size () > 0)
    {
      for (int i = 0; i < Patterns.size (); i++)
        {
          Pattern& p = Patterns[i];
          
          cout << p.name << " ";
        }

      cout << endl;
    }
}

/**
 *  This is the cmt show pattern <name> command
 * It shows both the pattern definition(s) and the places
 * where it is explicitly applied.
 */
void Pattern::show (const cmt_string& name)
{
  static PatternVector& Patterns = patterns ();

  int i;
  int j;

  bool found = false;

    // First show the definitions.
  for (i = 0; i < Patterns.size (); i++)
    {
      Pattern& p = Patterns[i];

      if (p.name == name)
        {
          found = true;

          cout << "# " << p.use->package << " " << p.use->version << " defines ";
          if (p.global) cout << "global ";
          cout << "pattern " << p.name << " as [";

          for (int wi = 0; wi < p.words.size (); wi++)
            {
              const cmt_string& s = p.words[wi];
              if (wi > 0) cout << " ";
              cout << s;
            }

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

  if (!found)
    {
      CmtError::set (CmtError::pattern_not_found, name);
      return;
    }

    //
    // Then show the packages which explicitly apply the pattern.
    //
  Use* use;
  ApplyPattern* apply_pattern = 0;

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

  for (i = 0; i < uses.size (); i++)
    {
      use = uses[i];
      for (j = 0; j < use->apply_patterns.size (); j++)
        {
          apply_pattern = &(use->apply_patterns[j]);
            
          if (apply_pattern->name == name)
            {
              cout << "# " << use->package << " " << use->version << " applies pattern " << name;
              cout << " => ";
              apply_pattern->show ();
            }
        }
    }

  use = &(Use::current ());
  for (j = 0; j < use->apply_patterns.size (); j++)
    {
      apply_pattern = &(use->apply_patterns[j]);
	
      if (apply_pattern->name == name)
	{
          cout << "# " << use->package << " " << use->version << " applies pattern " << name;
	  cout << " => ";
	  apply_pattern->show ();
	}
    }
}

//----------------------------------------------------------
Pattern::Pattern ()
{
}

//----------------------------------------------------------
Pattern::~Pattern ()

{
}

//----------------------------------------------------------
void Pattern::clear ()
{
  global = false;
  name = "";
  use = 0;
  words.clear ();
}

/**
 *   Applies a pattern to all uses except CMT itself.
 */
void Pattern::apply () const
{
  Use::UsePtrVector& uses = Use::uses ();

  Use* current = &(Use::current());

  Use::UsePtrVector list;

  if (current != use)
    {
      if (!current->get_paths (use, list)) return;
    }
  else
    {
      list.push_back (current);
    }

  for (int i = 0; i < list.size (); i++)
    {
      Use* u = list[i];
      if ((u->package != "CMT") && 
          (IgnorePattern::find (name, u) == 0)) apply (u);
    }
}

//----------------------------------------------------------
void Pattern::apply (Use* context_use) const
{
  static Template::TemplateVector dummy_templates;

  apply (context_use, dummy_templates);
}

/**
 *  Applies a pattern to one particular use.
 */
void Pattern::apply (Use* context_use, 
                     const Template::TemplateVector& templates) const
{
  cmt_string line;

  expand (context_use, templates, line);

  if (line != "")
    {
      Cmt::parse_requirements_text (line, "", context_use);
    }
}

//----------------------------------------------------------
void Pattern::expand (Use* context_use, 
                      const Template::TemplateVector& templates, 
                      cmt_string& line) const
{
  line = "";

    // First re-create the cmt statement as one line.
  for (int i = 0; i < words.size (); i++)
    {
      const cmt_string& w = words[i];

      if (i > 0) line += " ";

      if ((w == "\n") | (w == ";"))
        {
          line += "\n";
        }
      else
        {
          line += "\"";
          line += w;
          line += "\"";
        }
    }

  if (context_use == 0) context_use = &(Use::current ());

  if (line != "")
    {
        // Substitute templates from the cmt statement
      line.replace_all ("<package>", context_use->package.c_str ());
      line.replace_all ("<PACKAGE>", context_use->prefix.c_str ());
      line.replace_all ("<version>", context_use->version.c_str ());
      line.replace_all ("<path>",    context_use->real_path.c_str ());

      for (int j = 0; j < templates.size (); j++)
        {
          Template& t = templates[j];
          cmt_string s;
          s = "<";
          s += t.name;
          s += ">";
          line.replace_all (s, t.value);
        }

      for (;;)
        {
          int begin = line.find ("<");
          if (begin == cmt_string::npos) break;
          int end = line.find (begin, ">");
          if (end == cmt_string::npos) break;
          // Do not erase XML constructs
          if (line[end-1] == '/') break;
          line.erase (begin, end - begin + 1);
        }
    }
}

//----------------------------------------------------------
//
//  Operations on ApplyPatterns
//
//----------------------------------------------------------

//----------------------------------------------------------
void ApplyPattern::action (const CmtSystem::cmt_string_vector& words, Use* use)
{
    //
    // Expected syntax is
    //
    // apply_pattern <pattern-name> [ <template>=<value> ... ]
    //

  if (words.size () < 2) return;

  if (use == 0) use = &(Use::current());

  cmt_string& name = words[1];

  ApplyPattern* apply_pattern = add (name, use);

  for (int i = 2; i < words.size (); i++)
    {
      cmt_string s = words[i];
      int pos = s.find ("=");
      if (pos == cmt_string::npos) break;
      if (pos == 0) break;

      Template& t = apply_pattern->replacements.add ();

      s.substr (0, pos, t.name);
      s.substr (pos + 1, t.value);

      int size = t.value.size ();

      if (size >= 2)
        {
          if (((t.value[0] == '"') && (t.value[size - 1] == '"')) ||
              ((t.value[0] == '\'') && (t.value[size - 1] == '\'')))
            {
              t.value.erase (size - 1);
              t.value.erase (0, 1);
            }
        }
    }

  apply_pattern->apply ();
}

//----------------------------------------------------------
ApplyPattern* ApplyPattern::add (const cmt_string& name, Use* use)
{
  ApplyPattern& a = use->apply_patterns.add ();

  a.name = name;
  a.use  = use;

  return (&a);
}

//----------------------------------------------------------
ApplyPattern::ApplyPattern ()
{
}

//----------------------------------------------------------
ApplyPattern::~ApplyPattern ()
{
}

//----------------------------------------------------------
void ApplyPattern::show () const
{
  cmt_string line;

  line = "";
  Pattern* p = Pattern::find (name);
  if (p == 0) return;

  Use* u = use;
  if (u == 0) u = &(Use::current ());

  p->expand (u, replacements, line);

  if (line != "")
    {
      cout << "[" << line << "]" << endl;
    }
}

//----------------------------------------------------------
void ApplyPattern::apply () const
{
  cmt_string line;

  if (CmtSystem::testenv ("CMTTESTPATTERN"))
    {
      cout << "ApplyPattern::apply> " << name << endl;
    }

  line = "";
  Pattern* p = Pattern::find (name);
  if (p == 0) 
    {
      if (CmtSystem::testenv ("CMTTESTPATTERN"))
        {
          cout << "ApplyPattern::apply> " << name << " not found" << endl;
        }

      return;
    }

  if (p->global) return;
  
  Use* u = use;
  if (u == 0) u = &(Use::current ());

  p->apply (u, replacements);
}



//----------------------------------------------------------
/*                                                          */
/*  Operations on IgnorePatterns                            */
/*                                                          */
//----------------------------------------------------------

//----------------------------------------------------------
void IgnorePattern::action (const CmtSystem::cmt_string_vector& words, Use* use)
{
    //
    // Expected syntax is
    //
    // ignore_pattern <pattern-name>
    //

  if (words.size () < 2) return;

  if (use == 0) use = &(Use::current());

  cmt_string& name = words[1];

  add (name, use);
}

//----------------------------------------------------------
IgnorePattern* IgnorePattern::find (const cmt_string& name, Use* use)
{
  int ignore_pattern_index;

  if (use == 0) use = &(Use::current());

  if (use->ignore_patterns.size () == 0) return (0);

  for (ignore_pattern_index = 0;
       ignore_pattern_index < use->ignore_patterns.size ();
       ignore_pattern_index++)
    {
      IgnorePattern& ignore_pattern = use->ignore_patterns[ignore_pattern_index];

      if (ignore_pattern.name == name)
        {
          return (&ignore_pattern);
        }
    }

  return (0);
}

//----------------------------------------------------------
void IgnorePattern::add (const cmt_string& name, Use* use)
{
  IgnorePattern* ignore_pattern;

  ignore_pattern = find (name, use);

  if (ignore_pattern == 0)
    {
      IgnorePattern& a = use->ignore_patterns.add ();

      a.name = name;
      a.use  = use;
    }
}

//----------------------------------------------------------
IgnorePattern::IgnorePattern ()
{
}

//----------------------------------------------------------
IgnorePattern::~IgnorePattern ()
{
}

//----------------------------------------------------------
void IgnorePattern::show () const
{
}
