/*
Copyright 2011 Ilia Kats <ilia-kats@gmx.net>, based on work by Paul Marchouk <pmarchouk@gmail.com>

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License or (at your option) version 3 or any later version
accepted by the membership of KDE e.V. (or its successor approved
by the membership of KDE e.V.), which shall act as a proxy
defined in Section 14 of version 3 of the license.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "simpleipv6addressvalidator.h"

#include <QStringList>
#include <QVector>

SimpleIpV6AddressValidator::SimpleIpV6AddressValidator(AddressStyle style, QObject *parent)
    : QValidator(parent)
    , m_addressStyle(style)
{
    switch (style) {
        case Base:
            m_validator.setRegularExpression(QRegularExpression(QLatin1String("([0-9a-fA-F]{1,4}|:)+")));
            break;
        case WithCidr:
            m_validator.setRegularExpression(QRegularExpression(QLatin1String("([0-9a-fA-F]{1,4}|:){2,15}/[0-9]{1,3}")));
            break;
        case WithPort:
            m_validator.setRegularExpression(QRegularExpression(QLatin1String("\\[([0-9a-fA-F]{1,4}|:)+\\]:[0-9]{1,5}")));
    }
}

SimpleIpV6AddressValidator::~SimpleIpV6AddressValidator()
{
}

QValidator::State SimpleIpV6AddressValidator::validate(QString &address, int &pos) const
{
    if (QValidator::Invalid == checkWithInputMask(address, pos)) {
        return QValidator::Invalid;
    }

    return checkTetradsRanges(address);
}

QValidator::State SimpleIpV6AddressValidator::checkWithInputMask(QString &value, int &pos) const
{
    return  m_validator.validate(value, pos);
}

QValidator::State SimpleIpV6AddressValidator::checkTetradsRanges(QString &value) const
{
    QStringList addrParts;
    QStringList cidrParts;
    QStringList portParts;
    bool foundBracket = false;
    QValidator::State result = QValidator::Acceptable;

    switch (m_addressStyle) {
    case Base:
        addrParts = value.split(QLatin1Char(':'));
        break;

    case WithCidr:
        cidrParts = value.split(QLatin1Char('/'));
        addrParts = cidrParts[0].split(QLatin1Char(':'));
        break;

    case WithPort:
      if (value.isEmpty())
          return QValidator::Intermediate;
      if (value[0] != '[') {
          return QValidator::Invalid;
      } else {
          // Input: "[1:2:3:4:5:6:7:8]:123"
          // bracketParts: "[1:2:3:4:5:6:7:8" , ":123"
          // addrParts: "" , "1:2:3:4:5:6:7:8"
          // portParts: "", "123"
          QStringList bracketParts = value.split(QLatin1Char(']'));
          if (bracketParts.size() < 2)
              portParts = QStringList();
          else {
              foundBracket = true;
              if (!bracketParts[1].isEmpty() && bracketParts[1][0] != ':')
                  return QValidator::Invalid;
              else
                  portParts = bracketParts[1].split(QLatin1Char(':'));
          }
          addrParts = bracketParts[0].split(QLatin1Char('['))[1].split(QLatin1Char(':'));
      }
    }

    int number = addrParts.size();
    // There is no case where can be more than 8 colons (9 parts)
    // and only one unusual case where there are 8 colons (1:2:3:4:5:6:7::)
    if (number > 9)
        return QValidator::Invalid;
    else if (number == 9 && (!addrParts[7].isEmpty() || !addrParts[8].isEmpty()))
        return QValidator::Invalid;

    // lets check address parts
    bool emptypresent = false;
    int i = 1;
    for (QString part : addrParts) {
        if (part.isEmpty() && i < number) {
            // There is only one case where you can have 3 empty parts
            // and that is when you have the string: "::" which is valid
            // and useful and of course it can also be extended to ::123 for
            // instance. Anywhere other than the beginning though, having 3 empty
            // parts indicates either a run of 3 colons ("1:::6")" or two sets of
            // 2 colons ("1:2::3:4::") which are always invalid
            if (emptypresent && i != 2) {
                // qCDebug(PLASMA_NM) << "part.isEmpty()";
                return QValidator::Invalid;
            } else {
                // If this is an empty part then set it to zero to not fail
                // the next test
                part.setNum(0,16);
                emptypresent = true;
            }
        }
        i++;

        bool ok;
        if (part.toInt(&ok, 16) > 65535) {
            return QValidator::Invalid;
        }
    }

    // A special case: a single colon needs to be  Intermediate not Acceptable
    if (number == 2 && addrParts[0].isEmpty() && addrParts[1].isEmpty())
        result = QValidator::Intermediate;

    // Another special case: a single colon followed by something (i.e. ":123"
    // is invalid
    else if (number > 1 && addrParts[0].isEmpty() && !addrParts[1].isEmpty())
        result =  QValidator::Invalid;

    // If we don't have 8 parts yet and none of them are empty we aren't done yet
    else if (number < 8 && !emptypresent)
        result = QValidator::Intermediate;

    // If we have 8 parts but the last one is empty we aren't done yet
    else if (number == 8 && addrParts[7].isEmpty())
        result =  QValidator::Intermediate;

    if (m_addressStyle == WithCidr) {
        int cidrSize = cidrParts.size();

        // If we have a '/' and the basic address portion is not
        // yet complete (i.e. Intermediate) then the whole thing  is Invalid
        if (cidrSize == 2 && result == QValidator::Intermediate)
            return QValidator::Invalid;

        if (cidrSize == 1 || (cidrSize == 2 && cidrParts[1].isEmpty()))
            return QValidator::Intermediate;

        int cidrValue = cidrParts[1].toInt();
        if (cidrValue > 128)
            return QValidator::Invalid;
    } else if (m_addressStyle == WithPort) {
        int portSize = portParts.size();

        // If we have a ']' and the basic address portion is not
        // yet complete (i.e. Intermediate) then the whole thing  is Invalid
        if (foundBracket && result == QValidator::Intermediate)
            return QValidator::Invalid;

        if (portSize < 2 || (portSize == 2 && portParts[1].isEmpty())) {
            return QValidator::Intermediate;
        } else {
            int portValue = portParts[1].toInt();
            if (portValue > 65535)
                return QValidator::Invalid;
        }
    }
    return result;
}
