#include <map>

using namespace std;

#include "regexp.h"
#include "message.h"
#include "utils.h"

// default constructor
Regexp::Regexp (const string & regexp, const int options) 
    :  program_ (NULL), extra_ (NULL)
  { 
  count_ = 0;
  erroroffset_ = 0;
  compileCount_ = 0;
  executeCount_ = 0;
  Compile (regexp, options); 
  };   // constructor

// copy constructor
Regexp::Regexp (const Regexp & rhs) 
  : program_ (NULL), extra_ (NULL)  // cannot be uninitialised for operator=
  {
  *this = rhs;  // call operator=   (below)
  };  // end of copy constructor
  
// operator=  (assign one regexp to another)
const Regexp & Regexp::operator= (const Regexp & rhs)
  {
  if (this != &rhs)
    {
    offsets_.assign (ISEQ (rhs.offsets_));
    regexp_ = rhs.regexp_;
    target_ = rhs.target_;

    // old program and extra not needed as we are assigning over them
    if (program_) 
      {
      free (program_); 
      program_ = NULL;
      }
    if (extra_) 
      {
      free (extra_); 
      extra_ = NULL;
      }

  // copy program, if we have one
    if (rhs.program_)
      {
      size_t iSize;
      pcre_fullinfo (rhs.program_, rhs.extra_, PCRE_INFO_SIZE, &iSize);
      program_ = (real_pcre *) malloc (iSize);
      if (program_)
        memcpy (program_, rhs.program_, iSize);
      }  // end of copying program

    // re-create extra stuff if we have any
    if (rhs.extra_)
      {
      // re-study it will be easier than working out what the size is
      // however, working out the size can probably be done.
      // It would probably be :

      /*
       size_t iSize = sizeof(pcre_extra) + sizeof(pcre_study_data);
       extra_ = (pcre_extra *) malloc (iSize);
       if (extra_)
         memcpy (extra_, rhs.extra_, iSize);


      NB - the field extra_->study_data probably needs to be recomputed after 
           the malloc. Namely:

      extra_->study_data = (pcre_study_data *)((char *)extra_ + sizeof(pcre_extra));

       */

      const char *error;
      extra_ =  pcre_study (program_, 0, &error);        
      }  // end of copying extra stuff

    count_ = rhs.count_;
    erroroffset_ = rhs.erroroffset_;
    compileCount_ = rhs.compileCount_;

    } // end of not self-assignment
  return *this;
  };  // end of operator= 
  
// destructor  
Regexp::~Regexp () 
  { 
  if (program_) 
    free (program_); 
  if (extra_) 
    free (extra_); 
  };  // end of destructor
  
void Regexp::Compile (const string & regexp, const int options)
  {
const char *error;

  compileCount_++;

  // get rid of earlier program, if any
  if (program_) 
    {
    free (program_); 
    program_ = NULL;
    }

  if (extra_) 
    {
    free (extra_);
    extra_ = NULL;
    }

  // no regexp, must fail match
  if (regexp.empty ())
    return;
  
  program_ = pcre_compile(regexp.c_str (), options, &error, &erroroffset_, NULL);

  if (!program_)
    THROW_ERROR ("Failed: %1 at offset %2", error << erroroffset_);

  erroroffset_ = 0;

  // study it for speed purposes
  extra_ =  pcre_study(program_, 0, &error);        

  if (error)
    {
    free (program_); 
    program_ = NULL;
    THROW_ERROR ("Regexp study failed: %1", error);
    }

  }   // end of Regexp::Compile 

bool Regexp::Execute (const string & target,
                       const int offset,
                       const int options)
  {
int count;
static int offsets [MAX_PCRE_WILDCARDS * 3];  // hopefully we won't recurse and crash ;)

  executeCount_++;

  // exit if no regexp program to process (possibly because of previous error)
  if (program_ == NULL)
    return false;

  count = pcre_exec (program_, extra_, target.c_str (), target.size (),
                    offset, options, offsets, NUMITEMS (offsets));

  if (count == PCRE_ERROR_NOMATCH)
    return false;  // no match

  // free program as an indicator that we can't keep trying to do this one
  if (count <= 0)
    {
    free (program_);
    program_ = NULL;
    }

  if (count <= 0)
    {
    switch (count)
      {

      case PCRE_ERROR_NOMATCH:
        break;    // no match is OK

      case 0:
        throw runtime_error  ("Too many substrings in regular expression");

      case PCRE_ERROR_NULL        :
        throw runtime_error  ("pcre_exec error: null passed");

      case PCRE_ERROR_BADOPTION   :
        throw runtime_error  ("pcre_exec error: bad option bit");

      case PCRE_ERROR_BADMAGIC    :
        throw runtime_error  ("pcre_exec error: bad magic number in program");

      case PCRE_ERROR_UNKNOWN_NODE:
        throw runtime_error  ("pcre_exec error: unknown node");

      case PCRE_ERROR_NOMEMORY    :
        throw runtime_error  ("pcre_exec error: not enough memory");

      case PCRE_ERROR_NOSUBSTRING :
        throw runtime_error  ("pcre_exec error: no substring");

      case PCRE_ERROR_MATCHLIMIT  :
        throw runtime_error  ("pcre_exec error: recursion hit match limit");

      default:
       THROW_ERROR ("Unexpected error %1 in pcre_exec", count);

      } // end of switch on iCount
    } // end of some sort of error


  // if, and only if, we match we will save the matching string, the count
  // and offsets, so we can extract the wildcards later on

  target_ = target;  // for extracting wildcards
  count_ = count;    // ditto
  offsets_.clear ();
  // only need first 2/3 of offsets
  copy (offsets, &offsets [count * 2], back_inserter (offsets_));

  return true; // match
  }   // end of Regexp::Execute 

 // returns a numbered wildcard
const string Regexp::GetWildcard (const int iNumber) const
  {
  if (iNumber >= 0 && iNumber < count_)
    return string (
    &target_.c_str () [offsets_ [iNumber * 2]],
    offsets_ [(iNumber * 2) + 1] - offsets_ [iNumber * 2]).c_str ();
  else
    return "";
  };  // end of egexp::GetWildcard 

// returns a named wildcard
const string Regexp::GetWildcard (const string & sName) const
  {
  int iNumber;
  if (IsNumber (sName))
    iNumber = atoi (sName.c_str ());
  else
    {
    if (program_ == NULL)
      iNumber = PCRE_ERROR_NOSUBSTRING;
    else
      iNumber = pcre_get_stringnumber (program_, sName.c_str ());
    }
  return GetWildcard (iNumber);
  } // end of Regexp::GetWildcard 
  
void Regexp::BuildResultMap (StringMap & results)
  {
  results.clear ();
  
  // can't extract data if no regexp parsed, or no recent match
  if (program_ == NULL || count_ <= 0)
     return;
  
  int i;
    
  // first each wildcard by position
  for (i = 0; i < count_; i++)
    results [MAKE_STRING (i)] = GetWildcard (i);
  
  // see this web page for example code: http://chpcre.sourceforge.net/pcredemo.txt
  
  int namecount;
 
  // get count of named substrings
  pcre_fullinfo(program_, extra_, PCRE_INFO_NAMECOUNT, &namecount); 
  
  // no named substrings?
  if (namecount <= 0)
    return;
    
  /* Before we can access the substrings, we must extract the table for
  translating names to numbers, and the size of each entry in the table. */

  unsigned char *name_table;
 
  pcre_fullinfo(program_, extra_, PCRE_INFO_NAMETABLE, &name_table); 

  int name_entry_size;
  
  pcre_fullinfo(program_, extra_, PCRE_INFO_NAMEENTRYSIZE, &name_entry_size); 
  
/* Now we can scan the table and, for each entry, print the number, the name,
  and the substring itself. */

  unsigned char *tabptr;
  
  tabptr = name_table;
  for (i = 0; i < namecount; i++)
    {
    int n = (tabptr[0] << 8) | tabptr[1];
    const unsigned char * name = tabptr + 2;
    results [reinterpret_cast <const char *> (name)] = GetWildcard (n);
    tabptr += name_entry_size;
    }
    
  } // end of Regexp::BuildResultMap 

