/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file LICENSE.rst or https://cmake.org/licensing for details.  */
#include "RegexExplorer.h"

RegexExplorer::RegexExplorer(QWidget* p)
  : QDialog(p)
  , m_matched(false)
{
  this->setupUi(this);

  for (int i = 1; i < cmsys::RegularExpressionMatch::NSUBEXP; ++i) {
    matchNumber->addItem(QString("Match %1").arg(QString::number(i)),
                         QVariant(i));
  }
  matchNumber->setCurrentIndex(0);
}

void RegexExplorer::setStatusColor(QWidget* widget, bool successful)
{
  QColor color = successful ? QColor(0, 127, 0) : Qt::red;

  QPalette palette = widget->palette();
  palette.setColor(QPalette::WindowText, color);
  widget->setPalette(palette);
}

void RegexExplorer::on_regularExpression_textChanged(QString const& text)
{
#ifdef QT_NO_STL
  m_regex = text.toAscii().constData();
#else
  m_regex = text.toStdString();
#endif

  bool validExpression =
    stripEscapes(m_regex) && m_regexParser.compile(m_regex);
  if (!validExpression) {
    m_regexParser.set_invalid();
  }

  setStatusColor(labelRegexValid, validExpression);

  on_inputText_textChanged();
}

void RegexExplorer::on_inputText_textChanged()
{
  if (m_regexParser.is_valid()) {
    QString plainText = inputText->toPlainText();
#ifdef QT_NO_STL
    m_text = plainText.toAscii().constData();
#else
    m_text = plainText.toStdString();
#endif
    m_matched = m_regexParser.find(m_text);
  } else {
    m_matched = false;
  }

  setStatusColor(labelRegexMatch, m_matched);

  if (!m_matched) {
    clearMatch();
    return;
  }

  std::string matchingText;

  if (matchAll->isChecked()) {
    char const* p = m_text.c_str();
    while (m_regexParser.find(p)) {
      std::string::size_type l = m_regexParser.start();
      std::string::size_type r = m_regexParser.end();
      if (r - l == 0) {
        // matched empty string
        clearMatch();
        return;
      }
      if (!matchingText.empty()) {
        matchingText += ";";
      }
      matchingText += std::string(p + l, r - l);
      p += r;
    }
  } else {
    matchingText = m_regexParser.match(0);
  }

#ifdef QT_NO_STL
  QString matchText = matchingText.c_str();
#else
  QString matchText = QString::fromStdString(matchingText);
#endif
  match0->setPlainText(matchText);

  on_matchNumber_currentIndexChanged(matchNumber->currentIndex());
}

void RegexExplorer::on_matchNumber_currentIndexChanged(int index)
{
  if (!m_matched) {
    return;
  }

  QVariant itemData = matchNumber->itemData(index);
  int idx = itemData.toInt();

  if (idx < 1 || idx >= cmsys::RegularExpressionMatch::NSUBEXP) {
    return;
  }

#ifdef QT_NO_STL
  QString match = m_regexParser.match(idx).c_str();
#else
  QString match = QString::fromStdString(m_regexParser.match(idx));
#endif
  matchN->setPlainText(match);
}

void RegexExplorer::on_matchAll_toggled(bool checked)
{
  Q_UNUSED(checked);

  on_inputText_textChanged();
}

void RegexExplorer::clearMatch()
{
  m_matched = false;
  match0->clear();
  matchN->clear();
}

bool RegexExplorer::stripEscapes(std::string& source)
{
  char const* in = source.c_str();

  std::string result;
  result.reserve(source.size());

  for (char inc = *in; inc != '\0'; inc = *++in) {
    if (inc == '\\') {
      char nextc = in[1];
      if (nextc == 't') {
        result.append(1, '\t');
        in++;
      } else if (nextc == 'n') {
        result.append(1, '\n');
        in++;
      } else if (isalnum(nextc) || nextc == '\0') {
        return false;
      } else {
        result.append(1, nextc);
        in++;
      }
    } else {
      result.append(1, inc);
    }
  }

  source = result;
  return true;
}
