#include "publicsuffix.h"

#include "publicsuffix_data.h"

#include <QUrl>

#include <algorithm>

namespace {

QString normalizedDomain(QString domain) {
  domain = domain.trimmed();

  if (domain.startsWith(QLatin1Char('.'))) {
    domain.remove(0, 1);
  }

  while (domain.endsWith(QLatin1Char('.'))) {
    domain.chop(1);
  }

  if (domain.isEmpty()) {
    return QString();
  }

  const QByteArray ace_domain = QUrl::toAce(domain);

  if (!ace_domain.isEmpty()) {
    return QString::fromLatin1(ace_domain).toLower();
  }

  return domain.toLower();
}

QStringList domainLabels(const QString& domain) {
  return normalizedDomain(domain).split(QLatin1Char('.'), Qt::SkipEmptyParts);
}

bool containsRule(const QStringList& rules, const QString& rule) {
  return std::binary_search(rules.cbegin(), rules.cend(), rule);
}

QString joinedLabels(const QStringList& labels, int first_label) {
  QStringList suffix_labels;

  for (int i = first_label; i < labels.size(); ++i) {
    suffix_labels << labels.at(i);
  }

  return suffix_labels.join(QLatin1Char('.'));
}

} // namespace

namespace PublicSuffix {

bool isEffectiveTld(const QString& domain) {
  const QString normalized_domain = normalizedDomain(domain);

  if (normalized_domain.isEmpty()) {
    return false;
  }

  if (containsRule(Data::exceptionRules(), normalized_domain)) {
    return false;
  }

  if (containsRule(Data::exactRules(), normalized_domain)) {
    return true;
  }

  const int first_dot = normalized_domain.indexOf(QLatin1Char('.'));

  if (first_dot < 0) {
    return false;
  }

  const QString wildcard_rule = QLatin1String("*.") + normalized_domain.mid(first_dot + 1);

  return containsRule(Data::wildcardRules(), wildcard_rule);
}

QString topLevelDomain(const QString& domain) {
  const QStringList labels = domainLabels(domain);

  if (labels.isEmpty()) {
    return QString();
  }

  QString public_suffix;

  for (int i = labels.size() - 1; i >= 0; --i) {
    const QString suffix = joinedLabels(labels, i);

    if (isEffectiveTld(suffix)) {
      public_suffix = QLatin1Char('.') + suffix;
    }
  }

  return public_suffix;
}

QString registrableDomain(const QString& domain) {
  const QStringList labels = domainLabels(domain);
  const QString public_suffix = topLevelDomain(domain);

  if (labels.isEmpty() || public_suffix.isEmpty()) {
    return normalizedDomain(domain);
  }

  const QString suffix = public_suffix.mid(1);
  const QStringList suffix_labels = suffix.split(QLatin1Char('.'), Qt::SkipEmptyParts);
  const int registrable_label_count = suffix_labels.size() + 1;

  if (labels.size() <= suffix_labels.size()) {
    return normalizedDomain(domain);
  }

  return joinedLabels(labels, labels.size() - registrable_label_count);
}

QString listVersion() {
  return Data::listVersion();
}

QString listCommit() {
  return Data::listCommit();
}

} // namespace PublicSuffix
