+#include <algorithm>
+#include <array>
+#include <utility>
+
+#include <QCoreApplication>
+#include <QDateTime>
+#include <QTimeZone>
+#include <QDebug>
+#include <QTextCodec>
+#include <QVector>
+
+#include "quassel.h"
+
+// MIBenum values from http://www.iana.org/assignments/character-sets/character-sets.xml#table-character-sets-1
+static QList<int> utf8DetectionBlacklist = QList<int>() << 39 /* ISO-2022-JP */;
+
+QString nickFromMask(const QString& mask)
+{
+ return mask.left(mask.indexOf('!'));
+}
+
+QString userFromMask(const QString& mask)
+{
+ const int offset = mask.indexOf('!') + 1;
+ if (offset <= 0)
+ return {};
+ const int length = mask.indexOf('@', offset) - offset;
+ return mask.mid(offset, length >= 0 ? length : -1);
+}
+
+QString hostFromMask(const QString& mask)
+{
+ const int excl = mask.indexOf('!');
+ if (excl < 0)
+ return {};
+ const int offset = mask.indexOf('@', excl + 1) + 1;
+ return offset > 0 && offset < mask.size() ? mask.mid(offset) : QString{};
+}
+
+bool isChannelName(const QString& str)
+{
+ if (str.isEmpty())
+ return false;
+ static constexpr std::array<quint8, 4> prefixes{{'#', '&', '!', '+'}};
+ return std::any_of(prefixes.cbegin(), prefixes.cend(), [&str](quint8 c) { return c == str[0]; });
+}
+
+QString stripFormatCodes(QString message)
+{
+ static QRegExp regEx{"\x03(\\d\\d?(,\\d\\d?)?)?|\x04([\\da-fA-F]{6}(,[\\da-fA-F]{6})?)?|[\x02\x0f\x11\x12\x16\x1d\x1e\x1f]"};
+ return message.remove(regEx);
+}
+
+QString stripAcceleratorMarkers(const QString& label_)
+{
+ QString label = label_;
+ int p = 0;
+ forever
+ {
+ p = label.indexOf('&', p);
+ if (p < 0 || p + 1 >= label.length())
+ break;
+
+ if (label.at(p + 1).isLetterOrNumber() || label.at(p + 1) == '&')
+ label.remove(p, 1);
+
+ ++p;
+ }
+ return label;
+}
+
+QString decodeString(const QByteArray& input, QTextCodec* codec)
+{
+ if (codec && utf8DetectionBlacklist.contains(codec->mibEnum()))
+ return codec->toUnicode(input);
+
+ // First, we check if it's utf8. It is very improbable to encounter a string that looks like
+ // valid utf8, but in fact is not. This means that if the input string passes as valid utf8, it
+ // is safe to assume that it is.
+ // Q_ASSERT(sizeof(const char) == sizeof(quint8)); // In God we trust...
+ bool isUtf8 = true;
+ int cnt = 0;
+ for (uchar c : input) {
+ if (cnt) {
+ // We check a part of a multibyte char. These need to be of the form 10yyyyyy.
+ if ((c & 0xc0) != 0x80) {
+ isUtf8 = false;
+ break;
+ }
+ cnt--;
+ continue;
+ }
+ if ((c & 0x80) == 0x00)
+ continue; // 7 bit is always ok
+ if ((c & 0xf8) == 0xf0) {
+ cnt = 3;
+ continue;
+ } // 4-byte char 11110xxx 10yyyyyy 10zzzzzz 10vvvvvv
+ if ((c & 0xf0) == 0xe0) {
+ cnt = 2;
+ continue;
+ } // 3-byte char 1110xxxx 10yyyyyy 10zzzzzz
+ if ((c & 0xe0) == 0xc0) {
+ cnt = 1;
+ continue;
+ } // 2-byte char 110xxxxx 10yyyyyy
+ isUtf8 = false;
+ break; // 8 bit char, but not utf8!
+ }
+ if (isUtf8 && cnt == 0) {
+ QString s = QString::fromUtf8(input);
+ // qDebug() << "Detected utf8:" << s;
+ return s;
+ }
+ // QTextCodec *codec = QTextCodec::codecForName(encoding.toLatin1());
+ if (!codec)
+ return QString::fromLatin1(input);
+ return codec->toUnicode(input);
+}
+
+uint editingDistance(const QString& s1, const QString& s2)
+{
+ uint n = s1.size() + 1;
+ uint m = s2.size() + 1;
+ QVector<QVector<uint>> matrix(n, QVector<uint>(m, 0));
+
+ for (uint i = 0; i < n; i++)
+ matrix[i][0] = i;
+
+ for (uint i = 0; i < m; i++)
+ matrix[0][i] = i;
+
+ uint min;
+ for (uint i = 1; i < n; i++) {
+ for (uint j = 1; j < m; j++) {
+ uint deleteChar = matrix[i - 1][j] + 1;
+ uint insertChar = matrix[i][j - 1] + 1;
+
+ if (deleteChar < insertChar)
+ min = deleteChar;
+ else
+ min = insertChar;
+
+ if (s1[i - 1] == s2[j - 1]) {
+ uint inheritChar = matrix[i - 1][j - 1];
+ if (inheritChar < min)
+ min = inheritChar;
+ }
+
+ matrix[i][j] = min;
+ }
+ }
+ return matrix[n - 1][m - 1];