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

#include "cmt_fragment.h"
#include "cmt_use.h"
#include "cmt_symbol.h"
#include "cmt_system.h"
#include "cmt_database.h"

/*----------------------------------------------------------*/
/*                                                          */
/*  Operations on Variables                                 */
/*                                                          */
/*----------------------------------------------------------*/

//----------------------------------------------------------
Variable* Variable::find (VariableVector& vector, 
                          const cmt_string& name)
{
  for (int i = 0; i < vector.size (); i++)
    {
      Variable& v = vector[i];

      if (v.name == name) return (&v);
    }

  return (0);
}

//----------------------------------------------------------
Variable::Variable ()
{
}

//----------------------------------------------------------
Variable::Variable (const cmt_string& n) : name (n)
{
  m_macro_braces = "${";
  m_macro_braces += name;
  m_macro_braces += "}";

  m_macro_pars = "$(";
  m_macro_pars += name;
  m_macro_pars += ")";
}

//----------------------------------------------------------
const cmt_string& Variable::macro_braces () const
{
  return (m_macro_braces);
}

//----------------------------------------------------------
const cmt_string& Variable::macro_pars () const
{
  return (m_macro_pars);
}

//----------------------------------------------------------
void Variable::set (const cmt_string& new_name,
                    const cmt_string& new_value)
{
  name = new_name;
  value = new_value;

  m_macro_braces = "${";
  m_macro_braces += name;
  m_macro_braces += "}";

  m_macro_pars = "$(";
  m_macro_pars += name;
  m_macro_pars += ")";
}

//----------------------------------------------------------
Variable& Variable::operator = (const Variable& other)
{
  value = other.value;
  return (*this);
}

//----------------------------------------------------------
Variable& Variable::operator = (const cmt_string& v)
{
  value = v;
  return (*this);
}

//----------------------------------------------------------
void Variable::operator += (const cmt_string& v)
{
  value += v;
}

//----------------------------------------------------------
cmt_string Variable::operator + (const cmt_string& v) const
{
  return (value + v);
}

//----------------------------------------------------------
Variable::operator const cmt_string& () const
{
  return (value);
}

//----------------------------------------------------------
bool Variable::operator == (const cmt_string& v) const
{
  return ((value == v));
}

//----------------------------------------------------------
bool Variable::operator != (const cmt_string& v) const
{
  return ((value != v));
}

/*----------------------------------------------------------*/
/*                                                          */
/*  Operations on Fragments                                 */
/*                                                          */
/*----------------------------------------------------------*/

/*----------------------------------------------------------*/
void Fragment::show (const cmt_string& name)
{
  Fragment* fragment = Fragment::find (name);
  if (fragment == 0)
    {
      cout << "Fragment " << name << " not found" << endl;
    }
  else
    {
      fragment->print ();
    }
}

/*----------------------------------------------------------*/
void Fragment::show_all ()
{
  static FragmentVector& Fragments = fragments ();

  int number;

  for (number = 0; number < Fragments.size (); number++)
    {
      Fragment& fragment = Fragments[number];

      fragment.print ();
    }
}

/*----------------------------------------------------------*/
void Fragment::action (const CmtSystem::cmt_string_vector& words,
                       Use* use)
{
  if (words.size () > 1)
    {
      cmt_string suffix;
      cmt_string header;
      cmt_string trailer;
      bool need_dependencies = false;

      cmt_string name = words[1];

      for (int i = 2; i < words.size (); i++)
        {
          const cmt_string& word = words[i];

          if (word.find ("-suffix=") != cmt_string::npos)
            {
              suffix = word;
              suffix.replace ("-suffix=", "");
            }
          else if (word.find ("-dependencies") != cmt_string::npos)
            {
              need_dependencies = true;
            }
          else if (word.find ("-header=") != cmt_string::npos)
            {
              header = word;
              header.replace ("-header=", "");

              if (find (header) == 0)
                {
                  add (header, "", "", "", false, use);
                }
            }
          else if (word.find ("-trailer=") != cmt_string::npos)
            {
              trailer = word;
              trailer.replace ("-trailer=", "");

              if (find (trailer) == 0)
                {
                  add (trailer, "", "", "", false, use);
                }
            }
        }

      add (name, suffix, header, trailer, need_dependencies, use);
    }
}

/*----------------------------------------------------------*/
Fragment* Fragment::find (const cmt_string& name)
{
  static FragmentVector& Fragments = fragments ();

  int fragment_index;

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

  for (fragment_index = 0;
       fragment_index < Fragments.size ();
       fragment_index++)
    {
      Fragment& fragment = Fragments[fragment_index];

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

  return (0);
}

/*----------------------------------------------------------*/
void Fragment::add (const cmt_string& name,
                    const cmt_string& suffix,
                    const cmt_string& header,
                    const cmt_string& trailer,
                    bool need_dependencies,
                    Use* use)
{
  static FragmentVector& Fragments = fragments ();

  {
    Fragment* fragment;

    if (name == "") return;

    fragment = find (name);
    if (fragment != 0)
      {
        if (suffix != "")
          {
            fragment->suffix = suffix;
          }

        if (header != "")
          {
            fragment->header = header;
          }

        if (trailer != "")
          {
            fragment->trailer = trailer;
          }

        fragment->need_dependencies = need_dependencies;

        fragment->use = use;
        return;
      }
  }

  Fragment& fragment = Fragments.add ();

  fragment.name              = name;
  fragment.suffix            = suffix;
  fragment.header            = header;
  fragment.trailer           = trailer;
  fragment.need_dependencies = need_dependencies;
  fragment.use               = use;
}

/*----------------------------------------------------------*/
void Fragment::clear_all ()
{
  static FragmentVector& Fragments = fragments ();

  for (int i = 0; i < Fragments.size (); i++)
    {
      Fragment& f = Fragments[i];
      f.clear ();
    }

  Fragments.clear ();
}

/*----------------------------------------------------------*/
Fragment::FragmentVector& Fragment::fragments ()
{
  static Database& db = Database::instance ();
  static FragmentVector& Fragments = db.fragments ();

  return (Fragments);
}

/*----------------------------------------------------------*/
Fragment::Fragment ()
{
  use = 0;
}

/*----------------------------------------------------------*/
Fragment::Fragment (const cmt_string& fragment_name)
{
  name = fragment_name;
  use = 0;
  path = "";
}

/*----------------------------------------------------------*/
Fragment::~Fragment ()
{
  use = 0;
}

/*----------------------------------------------------------*/
void Fragment::clear ()
{
  name    = "";
  suffix  = "";
  header  = "";
  trailer = "";
  need_dependencies = false;
  path = "";
  use  = 0;
}

/*----------------------------------------------------------*/
int Fragment::print ()
{
  int result = 1;

  if (name == "") return (0);
  
  locate ();

  if (use == 0)
    {
      Use& u = Use::current();
      use = &u;
    }
  
  cmt_string the_path = path;

  use->reduce_path (the_path);

  cout << the_path;

  if (suffix != "")
    {
      cout << "->" << suffix;
    }

  cout << endl;

  return (result);
}

/*----------------------------------------------------------*/
bool Fragment::locate ()
{
  cmt_string root_path;

  if (use == 0)
    {
      // Assume CMT
      use = Use::find ("CMT");
    }

  use->get_full_path (root_path);

  if (path != "") return (true);

  // First try <root>/fragments/<name> or <root>/fragments/nmake/<name>

  path = root_path;
  path += CmtSystem::file_separator ();
  path += "fragments";
  path += CmtSystem::file_separator ();
  
  if (Cmt::build_nmake ())
    {
      path += "nmake";
      path += CmtSystem::file_separator ();
    }
  
  path += name;

  if (CmtSystem::test_file (path)) return (true);

  // Then try <root>/fragments/<name> for both Win and Unix

  path = root_path;
  path += CmtSystem::file_separator ();
  path += "fragments";
  path += CmtSystem::file_separator ();
  path += name;

  if (CmtSystem::test_file (path)) return (true);

  // Then try <root>/cmt/fragments/<name> or <root>/cmt/fragments/nmake/<name>

  root_path += CmtSystem::file_separator ();

  if (use->style == mgr_style) root_path += "mgr";
  else root_path += "cmt";
  
  root_path += CmtSystem::file_separator ();
  root_path += "fragments";
  root_path += CmtSystem::file_separator ();
  
  path = root_path;
  
  if (Cmt::build_nmake ())
    {
      path += "nmake";
      path += CmtSystem::file_separator ();
    }
  
  path += name;
  
  if (CmtSystem::test_file (path)) return (true);

  // Then try <root>/cmt/fragments/<name> for both Win and Unix

  path = root_path;
  
  path += name;

  if (CmtSystem::test_file (path)) return (true);

  return (false);
}

//--------------------------------------------------
bool Fragment::copy (FILE* out,
                     const cmt_string& name,
                     int variables, ...)
{
  va_list ids;

  Fragment* fragment = Fragment::find (name);
  if (fragment == 0) return (false);

  va_start (ids, variables);
  bool result = fragment->copy (out, variables, ids);
  va_end (ids);

  return (result);
}

//--------------------------------------------------
bool Fragment::copy (cmt_string& out,
                     const cmt_string& name,
                     int variables, ...)
{
  va_list ids;

  Fragment* fragment = Fragment::find (name);
  if (fragment == 0) return (false);

  va_start (ids, variables);
  bool result = fragment->copy (out, variables, ids);
  va_end (ids);

  return (result);
}

//--------------------------------------------------
bool Fragment::copy (FILE* out, int variables, ...)
{
  va_list ids;

  va_start (ids, variables);
  bool result = copy (out, variables, ids);
  va_end (ids);

  return (result);
}

//--------------------------------------------------
bool Fragment::copy (cmt_string& out, int variables, ...)
{
  va_list ids;

  va_start (ids, variables);
  bool result = copy (out, variables, ids);
  va_end (ids);

  return (result);
}

//--------------------------------------------------
bool Fragment::copy (FILE* out, int variables, va_list ids)
{
  static cmt_string cline;

  bool result = copy (cline, variables, ids);
  if (result)
    {
      cline.write (out);
      return (true);
    }
  else
    {
      return (false);
    }
}

//--------------------------------------------------
bool Fragment::copy (cmt_string& out, int variables, va_list ids)
{
  int i;

  if (!locate ()) return (false);

  out.read (path);

  Variable* var = 0;
  for (i = 0; i < variables; i++)
    {
      var = va_arg (ids, Variable*);
      out.replace_all (var->macro_braces (), var->value);
      out.replace_all (var->macro_pars (), var->value);
    }

  return (true);
}

//--------------------------------------------------
bool Fragment::wincopy (FILE* out, int variables, va_list ids)
{
  static cmt_string cline;

  bool result = wincopy (cline, variables, ids);

  if (result)
    {
      cline.write (out);
      return (true);
    }
  else
    {
      return (false);
    }
}

//--------------------------------------------------
bool Fragment::wincopy (cmt_string& out, int variables, va_list ids)
{
  int i;

  if (!locate ()) return (false);

  out.read (path);

  Variable* var = 0;
  for (i = 0; i < variables; i++)
    {
      var = va_arg (ids, Variable*);
      out.replace_all (var->macro_braces (), var->value);
      out.replace_all (var->macro_pars (), var->value);
    }

  cmt_string pattern;
  cmt_string macro_name;
  char end_pattern;

  int start = 0;

  for (;;)
    {
      //
      // Try and substitute all ${xxx} or $(xxx) patterns
      // using symbol values.
      //
      int par;
      int brace;
      int begin;

      par = out.find (start, "$(");
      brace = out.find (start, "${");

      if (par == cmt_string::npos)
        {
	  // No parentheses. Look for brace
          if (brace == cmt_string::npos)
            {
	      // No pattern, finish the scan.
              break;
            }

	  // Brace found
          end_pattern = '}';
          begin = brace;
        }
      else
        {
	  // Parenthese found. Look for closest from {par, brace}
          if ((brace == cmt_string::npos) ||
              (brace > par))
            {
              end_pattern = ')';
              begin = par;
            }
          else
            {
              end_pattern = '}';
              begin = brace;
            }
        }

      // Skip the pattern intro.
      start = begin + 2;

      int end;
      end = out.find (start, end_pattern);
      if (end == cmt_string::npos)
        {
	  // The pattern is a fake one (no ending!)
          break;
        }

      // This should never happen...
      if (end < begin) break;

      // Extract the complete pattern
      out.substr (begin, end - begin + 1, pattern);

      // Then only the macro name
      out.substr (begin + 2, end - begin - 2, macro_name);

      if (macro_name == "CFG")
        {
	  // This is a Windows reserved keyword...
          start = end + 1;
        }
      else
        {
          Symbol* macro = Symbol::find (macro_name);
          if (macro != 0)
            {
	      // Macro found
              cmt_string value = macro->resolve_macro_value ();
	      //cout << "resolve_macro_value2> value=" << value << endl;
              out.replace_all (pattern, value);

	      // The substitution will restart from the same place
	      // allowing for recursive replacements
              start = begin;
            }
          else
            {
	      // Macro not found. Look for env. variable
              cmt_string value = CmtSystem::getenv (macro_name);
	      //cout << "resolve_macro_value3> " << macro_name << "=" << value << endl;
              out.replace_all (pattern, value);

	      // The substitution will restart from the same place
	      // allowing for recursive replacements
              start = begin;
            }
        }
    }

  // Uniformly install CR-LF doublets.

  out.replace_all ("\r\n", "\n");
  out.replace_all ("\n", "\r\n");

  return (true);
}








//--------------------------------------------------
bool Fragment::copy (FILE* out,
                     const cmt_string& name,
                     const Variable::VariableVector& vector, 
                     int variables, ...)
{
  va_list ids;

  Fragment* fragment = Fragment::find (name);
  if (fragment == 0) return (false);

  va_start (ids, variables);
  bool result = fragment->copy (out, vector, variables, ids);
  va_end (ids);

  return (result);
}

//--------------------------------------------------
bool Fragment::copy (cmt_string& out,
                     const cmt_string& name,
                     const Variable::VariableVector& vector, 
                     int variables, ...)
{
  va_list ids;

  Fragment* fragment = Fragment::find (name);
  if (fragment == 0) return (false);

  va_start (ids, variables);
  bool result = fragment->copy (out, vector, variables, ids);
  va_end (ids);

  return (result);
}

//--------------------------------------------------
bool Fragment::copy (FILE* out, const Variable::VariableVector& vector, int variables, ...)
{
  va_list ids;

  va_start (ids, variables);
  bool result = copy (out, vector, variables, ids);
  va_end (ids);

  return (result);
}

//--------------------------------------------------
bool Fragment::copy (cmt_string& out, const Variable::VariableVector& vector, int variables, ...)
{
  va_list ids;

  va_start (ids, variables);
  bool result = copy (out, vector, variables, ids);
  va_end (ids);

  return (result);
}

//--------------------------------------------------
bool Fragment::copy (FILE* out, const Variable::VariableVector& vector, int variables, va_list ids)
{
  static cmt_string cline;

  bool result = copy (cline, vector, variables, ids);
  if (result)
    {
      cline.write (out);
      return (true);
    }
  else
    {
      return (false);
    }
}

//--------------------------------------------------
bool Fragment::copy (cmt_string& out, const Variable::VariableVector& vector, int variables, va_list ids)
{
  int i;

  if (!locate ()) return (false);

  out.read (path);

  Variable* var = 0;

  for (i = 0; i < vector.size (); i++)
    {
      var = &(vector[i]);
      out.replace_all (var->macro_braces (), var->value);
      out.replace_all (var->macro_pars (), var->value);
    }

  for (i = 0; i < variables; i++)
    {
      var = va_arg (ids, Variable*);
      out.replace_all (var->macro_braces (), var->value);
      out.replace_all (var->macro_pars (), var->value);
    }

  return (true);
}

//--------------------------------------------------
bool Fragment::wincopy (FILE* out, const Variable::VariableVector& vector, int variables, va_list ids)
{
  static cmt_string cline;

  bool result = wincopy (cline, vector, variables, ids);
  if (result)
    {
      cline.write (out);
      return (true);
    }
  else
    {
      return (false);
    }
}

//--------------------------------------------------
bool Fragment::wincopy (cmt_string& out, const Variable::VariableVector& vector, 
                        int variables, va_list ids)
{
  int i;

  if (!locate ()) return (false);

  out.read (path);

  Variable* var = 0;

  for (i = 0; i < vector.size (); i++)
    {
      var = &(vector[i]);
      out.replace_all (var->macro_braces (), var->value);
      out.replace_all (var->macro_pars (), var->value);
    }

  for (i = 0; i < variables; i++)
    {
      var = va_arg (ids, Variable*);
      out.replace_all (var->macro_braces (), var->value);
      out.replace_all (var->macro_pars (), var->value);
    }

  cmt_string pattern;
  cmt_string macro_name;
  char end_pattern;

  int start = 0;

  for (;;)
    {
      //
      // Try and substitute all ${xxx} or $(xxx) patterns
      // using symbol values.
      //
      int par;
      int brace;
      int begin;

      par = out.find (start, "$(");
      brace = out.find (start, "${");

      if (par == cmt_string::npos)
        {
	  // No parentheses. Look for brace
          if (brace == cmt_string::npos)
            {
	      // No pattern, finish the scan.
              break;
            }

	  // Brace found
          end_pattern = '}';
          begin = brace;
        }
      else
        {
	  // Parenthese found. Look for closest from {par, brace}
          if ((brace == cmt_string::npos) ||
              (brace > par))
            {
              end_pattern = ')';
              begin = par;
            }
          else
            {
              end_pattern = '}';
              begin = brace;
            }
        }

      // Skip the pattern intro.
      start = begin + 2;

      int end;
      end = out.find (start, end_pattern);
      if (end == cmt_string::npos)
        {
	  // The pattern is a fake one (no ending!)
          break;
        }

      // This should never happen...
      if (end < begin) break;

      // Extract the complete pattern
      out.substr (begin, end - begin + 1, pattern);

      // Then only the macro name
      out.substr (begin + 2, end - begin - 2, macro_name);

      if (macro_name == "CFG")
        {
	  // This is a Windows reserved keyword...
          start = end + 1;
        }
      else
        {
          Symbol* macro = Symbol::find (macro_name);
          if (macro != 0)
            {
	      // Macro found
              cmt_string value = macro->resolve_macro_value ();
	      //cout << "resolve_macro_value2> value=" << value << endl;
              out.replace_all (pattern, value);

	      // The substitution will restart from the same place
	      // allowing for recursive replacements
              start = begin;
            }
          else
            {
	      // Macro not found. Look for env. variable
              cmt_string value = CmtSystem::getenv (macro_name);
	      //cout << "resolve_macro_value3> " << macro_name << "=" << value << endl;
              out.replace_all (pattern, value);

	      // The substitution will restart from the same place
	      // allowing for recursive replacements
              start = begin;
            }
        }
    }

  // Uniformly install CR-LF doublets.

  out.replace_all ("\r\n", "\n");
  out.replace_all ("\n", "\r\n");

  return (true);
}

//--------------------------------------------------
FragmentHandle::FragmentHandle ()
{
  _fragment = 0;
  _initialized = false;
}

//--------------------------------------------------
FragmentHandle::FragmentHandle (const cmt_string name) : _name(name)
{
  _fragment = 0;
  _initialized = false;
}

//--------------------------------------------------
FragmentHandle& FragmentHandle::operator = (const FragmentHandle& other)
{
  _name = other._name;
  _fragment = 0;
  _initialized = false;

  return (*this);
}

//--------------------------------------------------
void FragmentHandle::reset ()
{
  _fragment = 0;
  _initialized = false;
}

//--------------------------------------------------
void FragmentHandle::set (const cmt_string name)
{
  _name = name;
  _fragment = 0;
  _initialized = false;
}

//--------------------------------------------------
cmt_string& FragmentHandle::name ()
{
  static cmt_string null_string;

  if (!setup ()) return (null_string);

  return (_fragment->name);
}

//--------------------------------------------------
cmt_string& FragmentHandle::suffix ()
{
  static cmt_string null_string;

  if (!setup ()) return (null_string);

  return (_fragment->suffix);
}

//--------------------------------------------------
cmt_string& FragmentHandle::header ()
{
  static cmt_string null_string;

  if (!setup ()) return (null_string);

  return (_fragment->header);
}

//--------------------------------------------------
cmt_string& FragmentHandle::trailer ()
{
  static cmt_string null_string;

  if (!setup ()) return (null_string);

  return (_fragment->trailer);
}

//--------------------------------------------------
bool FragmentHandle::need_dependencies ()
{
  if (!setup ()) return (false);

  return (_fragment->need_dependencies);
}

//--------------------------------------------------
bool FragmentHandle::copy (FILE* out, int variables, ...)
{
  if (!setup ()) return (false);

  va_list ids;

  va_start (ids, variables);
  bool result = _fragment->copy (out, variables, ids);
  va_end (ids);

  if (!result)
    {
      cout << "#CMT> Fragment " << _name << " not found" << endl;
      _fragment = 0;
    }

  return (result);
}

//--------------------------------------------------
bool FragmentHandle::copy (cmt_string& out, int variables, ...)
{
  if (!setup ()) return (false);

  va_list ids;

  va_start (ids, variables);
  bool result = _fragment->copy (out, variables, ids);
  va_end (ids);

  if (!result)
    {
      cout << "#CMT> Fragment " << _name << " not found" << endl;
      _fragment = 0;
    }

  return (result);
}

//--------------------------------------------------
bool FragmentHandle::wincopy (FILE* out, int variables, ...)
{
  if (!setup ()) return (false);

  va_list ids;

  va_start (ids, variables);
  bool result = _fragment->wincopy (out, variables, ids);
  va_end (ids);

  if (!result)
    {
      cout << "#CMT> Fragment " << _name << " not found" << endl;
      _fragment = 0;
    }

  return (result);
}

//--------------------------------------------------
bool FragmentHandle::wincopy (cmt_string& out, int variables, ...)
{
  if (!setup ()) return (false);

  va_list ids;

  va_start (ids, variables);
  bool result = _fragment->wincopy (out, variables, ids);
  va_end (ids);

  if (!result)
    {
      cout << "#CMT> Fragment " << _name << " not found" << endl;
      _fragment = 0;
    }

  return (result);
}




//--------------------------------------------------
bool FragmentHandle::copy (FILE* out, const Variable::VariableVector& vector, int variables, ...)
{
  if (!setup ()) return (false);

  va_list ids;

  va_start (ids, variables);
  bool result = _fragment->copy (out, vector, variables, ids);
  va_end (ids);

  if (!result)
    {
      cout << "#CMT> Fragment " << _name << " not found" << endl;
      _fragment = 0;
    }

  return (result);
}

//--------------------------------------------------
bool FragmentHandle::copy (cmt_string& out, const Variable::VariableVector& vector, int variables, ...)
{
  if (!setup ()) return (false);

  va_list ids;

  va_start (ids, variables);
  bool result = _fragment->copy (out, vector, variables, ids);
  va_end (ids);

  if (!result)
    {
      cout << "#CMT> Fragment " << _name << " not found" << endl;
      _fragment = 0;
    }

  return (result);
}

//--------------------------------------------------
bool FragmentHandle::wincopy (FILE* out, const Variable::VariableVector& vector, int variables, ...)
{
  if (!setup ()) return (false);

  va_list ids;

  va_start (ids, variables);
  bool result = _fragment->wincopy (out, vector, variables, ids);
  va_end (ids);

  if (!result)
    {
      cout << "#CMT> Fragment " << _name << " not found" << endl;
      _fragment = 0;
    }

  return (result);
}

//--------------------------------------------------
bool FragmentHandle::wincopy (cmt_string& out, 
                              const Variable::VariableVector& vector, 
                              int variables, ...)
{
  if (!setup ()) return (false);

  va_list ids;

  va_start (ids, variables);
  bool result = _fragment->wincopy (out, vector, variables, ids);
  va_end (ids);

  if (!result)
    {
      cout << "#CMT> Fragment " << _name << " not found" << endl;
      _fragment = 0;
    }

  return (result);
}

//--------------------------------------------------
bool FragmentHandle::setup ()
{
  if (!_initialized)
    {
      _initialized = true;

      _fragment = Fragment::find (_name);
      if (_fragment == 0)
        {
          cout << "#CMT> Fragment " << _name << " not found" << endl;
        }
    }

  if (_fragment == 0)
    {
      return (false);
    }
  else
    {
      return (true);
    }
}
