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

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

#include "cmt_constituent.h"
#include "cmt_generator.h"
#include "cmt_system.h"
#include "cmt_database.h"
#include "cmt_log.h"

/*----------------------------------------------------------*/
/*                                                          */
/*  Operations on Constituent                               */
/*                                                          */
/*----------------------------------------------------------*/

//----------------------------------------------------------
void Constituent::show (const cmt_string& name)
{
  Constituent* cptr = find (name);
  if (cptr == 0) return;

  const Constituent& constituent = *cptr;

  constituent.show ();
}

//----------------------------------------------------------
void Constituent::parse_all ()
{
  static ConstituentVector& Constituents = constituents ();

  int number;

  for (number = 0; number < Symbol::symbol_number (); number++)
    {
      Symbol& symbol = Symbol::symbol (number);

      if (symbol.type != Symbol::SymbolAction) continue;

      /*
      if (symbol.value_lists.size () < 1) continue;

      cmt_string value = symbol.build_macro_value ();

      if (value != "")
	{
	  add_for_action (symbol.name);
	}
      */
      add_for_action (symbol);
    }

  for (number = 0; number < Constituents.size (); number++)
    {
      Constituent& constituent = Constituents[number];

      constituent.parse ();
    }
}

//----------------------------------------------------------
void Constituent::show_all ()
{
  static ConstituentVector& Constituents = constituents ();

  int number;

  for (number = 0; number < Constituents.size (); number++)
    {
      const Constituent& constituent = Constituents[number];

      constituent.show ();
    }
}

//----------------------------------------------------------
void Constituent::show_names ()
{
  static ConstituentVector& Constituents = constituents ();

  int number;

  for (number = 0; number < Constituents.size (); number++)
    {
      Constituent& constituent = Constituents[number];
      cout << constituent.name << endl;
    }
}

//----------------------------------------------------------
Constituent* Constituent::find (const cmt_string& name)
{
  static ConstituentVector& Constituents = constituents ();

  int constituent_index;

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

  for (constituent_index = 0;
       constituent_index < Constituents.size ();
       constituent_index++)
    {
      Constituent& constituent = Constituents[constituent_index];

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

  return (0);
}

class constituents_action_iterator
{
public:
  typedef enum
    {
      ready,
      need_include
    } states;

  constituents_action_iterator (Constituent& c) : m_constituent (c)
  {
    m_state = ready;
  }

  void set (const cmt_string& w)
  {
    int equal;

    if (w == "") return;

    if (m_state == need_include)
      {
        m_state = ready;

        cmt_string& include = m_constituent.includes.add ();
        include = w;
      }

    if (w == "-OS9")
      {
        m_constituent.need_OS9 = true;
      }
    else if ((w == "-Windows") ||
             (w == "-windows"))
      {
        m_constituent.windows = true;
      }
    else if (w == "-no_share")
      {
        m_constituent.no_share = true;
      }
    else if (w == "-no_static")
      {
        m_constituent.no_static = true;
      }
    else if (w == "-prototypes")
      {
        m_constituent.need_prototypes = true;
      }
    else if (w == "-no_prototypes")
      {
        m_constituent.need_prototypes = false;
      }
    else if (w == "-check")
      {
        if (m_constituent.type == Application)
          {
	    m_constituent.need_check = true;
          }
      }
    else if (w == "-triggers")
      {
        if (m_constituent.type == Library)
          {
	    //m_constituent.build_triggers = true;
          }
      }
    else if (w == "-no_triggers")
      {
        if (m_constituent.type == Library)
          {
            m_constituent.build_triggers = false;
          }
      }
    else if (w == "-I")
      {
        m_state = need_include;
      }
    else if (w.substr (0, 3) == "-s=")
      {
        w.substr (3, m_subdir);
      }
    else if (w.substr (0, 3) == "-x=")
      {
        cmt_string& exclude = m_constituent.excludes.add ();
        w.substr (3, exclude);
        cmt_regexp& exp = m_constituent.exclude_exprs.add ();
	exp.set (exclude);
      }
    else if (w.substr (0, 3) == "-k=")
      {
        cmt_string& select = m_constituent.selects.add ();
        w.substr (3, select);
        cmt_regexp& exp = m_constituent.select_exprs.add ();
	exp.set (select);
      }
    else if (w.substr (0, 8) == "-import=")
      {        
	cmt_string text_imports;
	w.substr (8, text_imports);

	CmtSystem::cmt_string_vector imports;

	CmtSystem::split (text_imports, ',', imports);

	for (int i = 0; i < imports.size (); i++)
	  {
	    cmt_string& import = m_constituent.imports.add ();
	    import             = imports[i] ; 
	  }        
      }
    else if (w.substr (0, 7) == "-group=")
      {
        cmt_string group_name = "";

        w.substr (7, group_name);
        
	if (group_name != "")
	  {
	    m_constituent.group = Group::add (group_name);
	  }
      }
    else if (w.substr (0, 8) == "-suffix=")
      {
        w.substr (8, m_constituent.suffix);
      }
    else if (w == "-target_tag")
      {
	m_constituent.has_target_tag = true;
      }
    else if (w.substr (0, 1) == "-")
      {
	//if (!Cmt::get_quiet ())
	//{
	CmtMessage::warning ("bad option " + w + " in constituent " +
			     m_constituent.name);
	//	cerr << "#CMT> Warning: bad option " 
	//	     << w << " in constituent " << m_constituent.name << endl;
	//CmtError::set (CmtError::execution_error, cmd);
	//}
      }
    else if ((equal = w.find ("=")) != cmt_string::npos)
      {
        cmt_string variable_name;
        cmt_string variable_value;
        
        w.substr (0, equal, variable_name);
        w.substr (equal + 1, variable_value);
        
        Variable* v = Variable::find (m_constituent.variables, variable_name);
        if (v == 0)
          {
            v = &(m_constituent.variables.add ());
            v->set (variable_name);
          }

        (*v) = variable_value;
      }
    else
      {
        // We have a normal source module
 
        cmt_string& module = m_constituent.modules.add ();
        
        module.erase (0);
          
        //
        // The prefix explicitly provided in (w) has priority
        // over the currently specified (m_subdir) when it is an
        // absolute path
        //
        if (CmtSystem::absolute_path (w))
          {
            module += w;
          }
        else
          {
            cmt_string prefix;
            cmt_string name = w;

            CmtSystem::dirname (name, prefix);
            if (prefix == "../src") CmtSystem::basename (name, name);

            module += m_subdir;

            if (module != "")
              {
                module += CmtSystem::file_separator ();
              }

            module += name;
          }
      }
  }

  Constituent& m_constituent;
  cmt_string m_subdir;
  states m_state;
};

//----------------------------------------------------------
void Constituent::action (ConstituentType type,
                          const CmtSystem::cmt_string_vector& words)
{
  cmt_string generator;
  cmt_string name;
  Constituent* constituent;

  int i = 1;

  if (type == Document)
    {
      generator = words[i];
      if (generator == "") return;
      i++;
    }

  name = words[i];
  if (name == "") return;
  i++;

  constituent = add (type, name, generator);

  for (;i < words.size (); i++)
    {
      const cmt_string& w = words[i];
      cmt_string& parameter = constituent->parameters.add ();
      parameter = w;
    }
}

//----------------------------------------------------------
void Constituent::parse ()
{
  if (parameters.size () == 0) return;

  Constituent& me = *this;

  modules.clear ();

  constituents_action_iterator it (me);

  for (int i = 0; i < parameters.size (); i++)
    {
      const cmt_string& w = parameters[i];
      cmt_string ew = w;

      Symbol::expand (ew);

      CmtSystem::cmt_string_vector ws;

      //cerr << "Constituent " << name << " Analyzing module " << ew << endl;

//       if (ew.substr (0, 13) == "action_value=")
// 	{
//           it.set (ew);
// 	}
//       else
// 	{
	  CmtSystem::split (ew, " \t", ws);

	  for (int j = 0; j < ws.size (); ++j)
	    {
	      const cmt_string& w = ws[j];
          
	      //cerr << "Constituent " << name << " Setting module " << w << endl;
	      it.set (w);
	    }
// 	}
    }

  parameters.clear ();

  if (has_target_tag && (Document != type || group->name () != "cmt_actions"))
    {
      const Tag* tag = Tag::find ("target_" + name);
      if (!Tag::check_tag_used (tag) && !Symbol::check_tag_used (tag))
	{
	  // Keep associated tag target_<name>
	  // only if this tag is used somewhere
	  // in tag or symbol expression
	  has_target_tag = false;
	  CmtMessage::verbose ("discarded tag target_" + name + " for "
			       + (type == Application ? "application " :
				  (type == Library ? "library " : "document "))
			       + name);
	}
    }
}

//----------------------------------------------------------
Constituent* Constituent::add (ConstituentType type,
                               const cmt_string& name,
                               const cmt_string& generator)
{
  static ConstituentVector& Constituents = constituents ();

  {
    Constituent* constituent;

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

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

  Constituent& constituent = Constituents.add ();
  constituent.clear ();

  constituent.name      = name;
  constituent.generator = generator;
  constituent.type      = type;
  constituent.need_prototypes = Cmt::need_prototypes ();

  return (&constituent);
}

//----------------------------------------------------------
Constituent* Constituent::add_for_action (const Symbol& symbol)
{
  Constituent* constituent (0);
  if (symbol.value_lists.size () < 1) return constituent;

  const Tag* tag = Tag::find ("target_" + symbol.name);
  const bool target_tag_used
    (Tag::check_tag_used (tag) || Symbol::check_tag_used (tag));

  cmt_string value = symbol.build_macro_value ();
  if ( 0 == value.size () && !target_tag_used) return constituent;

  constituent = add (Document, symbol.name, "cmt_action_runner");

  constituent->group = Group::add ("cmt_actions");

  // Do NOT associate target_<name> by default
  // but only if this tag is used somewhere
  // in tag or symbol expression
  if (target_tag_used)
    constituent->has_target_tag = true;

  cmt_string& p1 = constituent->parameters.add ();
  p1 = "action_value=";
  Symbol::expand (value);
  // quote since split will later be applied
  // - in parse
  p1 += CmtSystem::quote (value, " \t");
//   p1 += "$(";
//   p1 += symbol.name;
//   p1 += ")";

  return (constituent);
}

//----------------------------------------------------------
void Constituent::clear_all ()
{
  static ConstituentVector& Constituents = constituents ();

  for (int i = 0; i < Constituents.size (); i++)
    {
      Constituent& c = Constituents[i];
      c.clear ();
    }
  Constituents.clear ();
}

//----------------------------------------------------------
Constituent::ConstituentVector& Constituent::constituents ()
{
  static Database& db = Database::instance ();
  static ConstituentVector& Constituents = db.constituents ();

  return (Constituents);
}

//----------------------------------------------------------
Constituent::Constituent ()
{
  clear ();
}

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

//----------------------------------------------------------
void Constituent::clear ()
{
  name      = "";
  generator = "";
  type = Document;
  group     = 0;
  modules.clear ();
  parameters.clear ();
  need_OS9        = false;
  windows         = false;
  no_static       = false;
  no_share        = false;
  need_prototypes = false;
  need_check      = false;
  build_triggers  = false;
  has_target_tag  = false;
  excludes.clear ();
  exclude_exprs.clear ();
  selects.clear ();
  select_exprs.clear ();
  includes.clear ();
  imports.clear ();
  variables.clear ();
}

//----------------------------------------------------------
void Constituent::build_all_makefiles (bool simulation)
{
  static ConstituentVector& Constituents = constituents ();

  int i;

  for (i = 0; i < Constituents.size (); i++)
    {
      Constituent& constituent = Constituents[i];

      constituent.build_makefile (simulation);
    }
}

//----------------------------------------------------------
void Constituent::build_all_msdev_files (bool simulation)
{
  static ConstituentVector& Constituents = constituents ();

  int i;

  Generator::build_msdev_workspace (Constituents);

  for (i = 0; i < Constituents.size (); i++)
    {
      Constituent& constituent = Constituents[i];

      constituent.build_msdev_file (simulation);
    }
}

// Visual Studio.Net Support                                     
//----------------------------------------------------------     
void Constituent::build_all_vsnet_files (bool simulation)        
{                                                                
  static ConstituentVector& Constituents = constituents ();      
                                                                 
  int i;                                                         
                                                                 
  Generator::build_vsnet_workspace (Constituents);               
                                                                 
  for (i = 0; i < Constituents.size (); i++)                     
    {                                                            
      Constituent& constituent = Constituents[i];                
                                                                 
      constituent.build_vsnet_file (simulation);                 
    }                                                            
}                                                                
                                                                 
//----------------------------------------------------------
void Constituent::build_makefile (bool simulation) const
{
  if (!simulation)
    {
      bool dependencies (false);
      Generator::build_constituent_makefile (*this, dependencies);
    }
}

//----------------------------------------------------------
void Constituent::build_msdev_file (bool simulation) const
{
  if (!simulation)
    {
      Generator::build_msdev (*this);
    }
}

// Visual Studio.net Support                                  
//----------------------------------------------------------  
void Constituent::build_vsnet_file (bool simulation) const    
{                                                             
  if (!simulation)                                            
    {                                                         
      Generator::build_vsnet (*this);                         
    }
}

//----------------------------------------------------------
void Constituent::show (ostream& out) const
//void Constituent::show () const
{
  int i;

  switch (type)
    {
    case Library:
      out << "library";
      break;
    case Application:
      out << "application";
      break;
    case Document:
      out << "document " << generator;
      break;
    }
  
  out << " " << name;
  
  if (group != 0)
    {
      out << " -group=" << group->name ();
    }
  
  if (suffix != 0)
    {
      out << " -suffix=" << suffix;
    }
  
  if ((type == Application) && need_check)
    {
      out << " -check";
    }
  
  if ((type == Library) && no_share)
    {
      out << " -no_share";
    }
  
  if ((type == Library) && no_static)
    {
      out << " -no_static";
    }
  
  if ((type == Library) && build_triggers)
    {
      out << " -triggers";
    }
  
  if (type == Application || type == Library)
    {
      if (need_prototypes)
	out << " -prototypes";
      else
	out << " -no_prototypes";
    }
  
  if (has_target_tag)
    {
      out << " -target_tag";
    }
  
  for (i = 0; i < (imports.size ()); i++)
    {
      const cmt_string& import_name = imports[i];
      
      out << " -import=" << import_name;
    }
  
  for (i = 0; i < (excludes.size ()); i++)
    {
      const cmt_string& exclude = excludes[i];
      
      out << " -x=" << exclude;
    }
  
  for (i = 0; i < (selects.size ()); i++)
    {
      const cmt_string& select = selects[i];
      
      out << " -k=" << select;
    }
  
  for (i = 0; i < (modules.size ()); i++)
    {
      const cmt_string& module_name = modules[i];
      
      out << " " << module_name;
    }
  
  for (i = 0; i < (variables.size ()); i++)
    {
      const Variable& v = variables[i];
      
      //      out << " " << v.name << "=" << v.value;
      // quote (up to) 2 times as split is applied 2 times
      // - before action
      // - in parse
      cmt_string qvalue (CmtSystem::quote (v.value, " \t"));
      out << " " << v.name << "="
	  << (qvalue == v.value ? v.value : CmtSystem::quote (qvalue, " \t"));
      //	  << CmtSystem::quote (CmtSystem::quote (v.value, " \t"), " \t");
    }
  
  out << endl;
}
