#pragma once

#include <string.h>
#include "pcre.h"

namespace PCRE
{

  class Match
  {
    int ovector[30];
    int matchCount;
    const pcre *re;
    const char *str;
    int strLen;
    int bufLen;
    char *buffer;

    friend class RegExp;

    Match(const pcre *re, const char *str)
      : re(re),
        str(str),
        strLen(0),
        bufLen(0),
        buffer(0)
    {
      for(const char *p = str; *p++; ++strLen);

      matchCount = pcre_exec(re, 0, str, strLen, 0, 0, ovector, sizeof(ovector) / sizeof(ovector[0]));

      if(matchCount > 0)
        buffer = (char*)pcre_malloc(bufLen = strLen + 10);
    }

  public:

    Match(const Match &m)
      : matchCount(m.matchCount),
        re(m.re),
        str(m.str),
        strLen(m.strLen),
        bufLen(m.bufLen),
        buffer(0)
    {
      for(size_t i = 0; i < sizeof(ovector) / sizeof(ovector[0]); ++i)
        ovector[i] = m.ovector[i];
      if(bufLen > 0)
        buffer = (char*)pcre_malloc(bufLen);
    }

    Match &operator=(const Match &m)
    {
      re = m.re;
      str = m.str;
      strLen = m.strLen;
      matchCount = m.matchCount;
      bufLen = m.bufLen;
      if(buffer)
        pcre_free(buffer);
      for(size_t i = 0; i < sizeof(ovector) / sizeof(ovector[0]); ++i)
        ovector[i] = m.ovector[i];
      if(bufLen > 0)
        buffer = (char*)pcre_malloc(bufLen);
      return *this;
    }

    operator bool()
    {
      return matchCount >= 0;
    }

    const char *operator[] (const char *name)
    {
      int res = pcre_copy_named_substring(re, str, ovector, matchCount, name, buffer, bufLen);
      return res >= 0 ? buffer : 0;
    }

    const char *operator[] (int idx)
    {
      int res = pcre_copy_substring(str, ovector, matchCount, idx, buffer, bufLen);
      return res >= 0 ? buffer : 0;
    }

    ~Match()
    {
      if(buffer)
        pcre_free(buffer);
    }
  };

  class RegExp
  {
    pcre *re;
    const char *lastError;
    int errorOffset;

    RegExp(const RegExp &);         // I'm lazy =)
    RegExp &operator=(const RegExp &);

  public:
    RegExp()
      : re(0),
        lastError(0),
        errorOffset(0)
    {
    }

    RegExp(const char *pattern)
      : re(0),
        lastError(0),
        errorOffset(0)
    {
      compile(pattern);
    }

    RegExp(const char *pattern, const char *options)
      : re(0),
        lastError(0),
        errorOffset(0)
    {
      compile(pattern, options);
    }

    ~RegExp()
    {
      if(re)
        pcre_free(re);
    }

    bool compile(const char *pattern)
    {
      RegExp r("^\\/(.*)\\/([ixms]*)$", "");

      if(Match m = r.match(pattern))
      {
        char options[9] = {};
        strncat(options, m[2], sizeof(options) - 1);
        return compile(m[1], options);
      }
      return false;
    }

    bool compile(const char *pattern, const char *options)
    {
      int nOptions = 0;
      for(const char *opt = options; opt && *opt; ++opt)
      {
        switch(*opt)
        {
          case 'i': nOptions |= PCRE_CASELESS;  break;
          case 'x': nOptions |= PCRE_EXTENDED;  break;
          case 'm': nOptions |= PCRE_MULTILINE; break;
          case 's': nOptions |= PCRE_DOTALL;  break;
        }
      }

      //const char *error = 0;
      //int errorOfs = 0;
      if(re)
        pcre_free(re);
      re = pcre_compile(pattern, nOptions, &lastError, &errorOffset, 0);

      return !!re;
    }

    bool isValid() const
    {
      return !!re;
    }

    const char *getLastError() const
    {
      return lastError;
    }

    int getErrorOffset() const
    {
      return errorOffset;
    }

    Match match(const char *str) const
    {
      return Match(re, str);
    }

  };


}
