
#include "cmt_model.h"
#include "cmt_fragment.h"
#include "cmt_symbol.h"

/**
 *   Filters out all predefined XML constructs.
 */
void CmtModel::filter (cmt_string& text)
{
  text.replace_all ("<cmt:tab/>", "\t");
  text.replace_all ("<cmt:cr/>",  "\r");
  text.replace_all ("<cmt:lf/>",  "\n");
}

void CmtModel::display (cmt_string& text)
{
  text.replace_all ("&lt;", "<");
  text.replace_all ("&gt;", ">");
  Symbol::expand (text);
  cout << text;
}

void CmtModel::expand (const cmt_string& input_text)
{
  int openmarker;
  int closemarker;

  CmtSystem::cmt_string_vector subargs;

  cmt_string text = input_text;
  cmt_string remaining;

  filter (text);

  for (;;)
    {
      /**
       *   Look for the next < ... /> pattern
       *   If not found, then simply dump the text as it is.
       */
      
      openmarker = text.find ("<");
      if (openmarker == cmt_string::npos) break;
      
      closemarker = text.find (openmarker, "/>");
      if (closemarker == cmt_string::npos) break;

      /**
       *  Extract the command from the pattern
       */
      cmt_string command;
      text.substr (openmarker + 1, closemarker - openmarker - 1, command);
          
      /**
       *   Get what is left from the original text beyond the pattern 
       */
      text.substr (closemarker + 2, remaining);

      /**
       *   Cut the original text up to the pattern start
       */
      text.erase (openmarker);
      
      /**
       *   Now display it
       */
      display (text);
          
      /**
       * Convert the extracted command into words
       */
      CmtSystem::split (command, " ", subargs);
          
      /**
       *  Recursively expand it
       */
      expand (subargs);
      
      /**
       *  The next iteration will operate on the remaining text
       */
      text = remaining;
    }
  
  /**
   *   Display what is left after extracting the last pattern
   */
  display (text);
}

/**
 *   Expands a model file
 *   Arguments are :
 *     model-name   : name of a model file
 *     var=value    : variable value to be expanded 
 *                    (will take the form ${var} )
 */
void CmtModel::expand (const CmtSystem::cmt_string_vector& arguments)
{
  int i;

  Variable::VariableVector variables;

  /**
   *   We start by decoding all [variable=value] pairs from the arguments
   *   A vector of Variables is filled from them. 
   */
  for (i = 0; i < arguments.size (); i++)
    {
      const cmt_string& arg = arguments[i];

      if (arg.find ("=") != cmt_string::npos)
        {
          cmt_string name;
          cmt_string value;
          int pos = arg.find ("=");

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

          Variable* v = Variable::find (variables, name);
          if (v == 0)
            {
              v = &(variables.add ());
              v->set (name);
            }

          (*v) = value;
        }
    }

  /**
   *   Then model names are extracted.
   *   Each model may contain a set of <...> patterns.
   *   The expected syntax for each of them is :
   *     <model-name variable-name=value ...>
   *
   *   Therefore the current expand function is recursively restarted.
   *
   *   Text around patterns is displayed after replacements of all
   *   variable values detected by ${variable-name} patterns
   */
  cmt_string text;

  for (i = 0; i < arguments.size (); i++)
    {
      const cmt_string& arg = arguments[i];

      if (arg.find ("=") == cmt_string::npos)
        {
          FragmentHandle fragment (arg);
          fragment.copy (text, variables, 0);

          filter (text);

          int openmarker;
          int closemarker;

          CmtSystem::cmt_string_vector subargs;

          cmt_string remaining;

          for (;;)
            {
              /**
               *   Look for the next <...> pattern
               *   If not found, then simply dump the text as it is.
               */

              openmarker = text.find ("<");
              if (openmarker == cmt_string::npos) break;

              closemarker = text.find (openmarker, "/>");
              if (closemarker == cmt_string::npos)
                {
                  /**
                   *  The opening < was in the text
                   */

                  /**
                   *   Get what is left from the original text beyond the pattern 
                   */
                  text.substr (openmarker + 1, remaining);
                  text.erase (openmarker + 1);
                  
                  /**
                   *   Now display it
                   */
                  display (text);
                }
              else
                {
                  /**
                   *  Extract the command from the pattern
                   */
                  cmt_string command;
                  text.substr (openmarker + 1, closemarker - openmarker - 1, command);

                  /**
                   *   Get what is left from the original text beyond the pattern 
                   */
                  text.substr (closemarker + 2, remaining);
                  text.erase (openmarker);
                  
                  /**
                   *   Now display it
                   */
                  display (text);

                  /**
                   * Convert the extracted command into words
                   */
                  CmtSystem::split (command, " ", subargs);
                  
                  /**
                   *  Recursively expand it
                   */
                  expand (subargs);
                }

              /**
               *  The next iteration will operate on the remaining text
               */
              text = remaining;
            }

          /**
           *   Display what is left after extracting the last pattern
           */
          display (text);
        }
    }
}

