#include "WedgeHeader.h"
#include <QList>
#include <QLocale>
namespace {
struct Tok { QString text; bool literal; };
QList<Tok> tokenize(const QString &fmt) {
QList<Tok> out;
int i = 0;
while (i < fmt.length()) {
if (fmt.mid(i, 6) == "otcDay") { out.append({"otcDay", false}); i += 6; continue; }
if (fmt.mid(i, 7) == "otcYear") { out.append({"otcYear", false}); i += 7; continue; }
const QChar c = fmt.at(i);
int j = i;
if (c.isLetter()) {
while (j < fmt.length() && fmt.at(j) == c) ++j;
out.append({fmt.mid(i, j - i), false});
} else {
while (j < fmt.length() && !fmt.at(j).isLetter()) ++j;
out.append({fmt.mid(i, j - i), true});
}
i = j;
}
return out;
}
bool isDayTok(const QString &t) {
return t.compare("dd", Qt::CaseInsensitive) == 0 || t.compare("d", Qt::CaseInsensitive) == 0;
}
QString litPattern(const QString &s) {
QString p;
int i = 0;
while (i < s.length()) {
int j = i;
if (s.at(i).isSpace()) {
while (j < s.length() && s.at(j).isSpace()) ++j;
p += "\\s+";
} else {
while (j < s.length() && !s.at(j).isSpace()) ++j;
p += QRegularExpression::escape(s.mid(i, j - i));
}
i = j;
}
return p;
}
QString tokValue(const QString &t, const QDate &d) {
const QLocale c = QLocale::c();
if (t == "otcDay") return QString::number(WedgeHeader::otcDayOf(d));
if (t == "otcYear") return QString("0%1").arg(WedgeHeader::otcYearOf(d) % 100, 2, 10, QChar('0'));
if (t == "EEEE") return c.toString(d, "dddd").toLower();
if (t == "EEE") return c.toString(d, "ddd").toLower();
if (t == "MMMM") return c.toString(d, "MMMM").toLower();
if (t == "MMM") return c.toString(d, "MMM").toLower();
if (t == "MM") return c.toString(d, "MM");
if (t == "M") return c.toString(d, "M");
if (t.compare("dd", Qt::CaseInsensitive) == 0) return c.toString(d, "dd");
if (t.compare("d", Qt::CaseInsensitive) == 0) return c.toString(d, "d");
if (t == "yyyy") return c.toString(d, "yyyy");
if (t == "yy") return c.toString(d, "yy");
return t;
}
QString tokPattern(const QString &t) {
if (t == "otcDay") return "(?<otc>\\d+)";
if (t == "otcYear") return "\\d{1,4}";
if (t.startsWith('E') || t == "MMM" || t == "MMMM") return "[a-zA-Z]{3,}";
if (t == "MM" || t == "M") return "\\d{1,2}";
if (isDayTok(t)) return "0?\\d{1,2}";
if (t == "yyyy") return "\\d{4}";
if (t == "yy") return "\\d{2}";
return QRegularExpression::escape(t);
}
}
QDate WedgeHeader::otcStartFor(const QDate &date) {
QDate start(date.year(), 3, 20);
if (date < start) start = start.addYears(-1);
return start;
}
qint64 WedgeHeader::otcDayOf(const QDate &date) {
return otcStartFor(date).daysTo(date) + 1;
}
int WedgeHeader::otcYearOf(const QDate &date) {
return otcStartFor(date).year();
}
QDate WedgeHeader::dateFromOtcDay(qint64 otcDay) {
return otcStartFor(QDate::currentDate()).addDays(otcDay - 1);
}
QString WedgeHeader::render(const QString &format, const QDate &date) {
QString out;
for (const Tok &t : tokenize(format))
out += t.literal ? t.text : tokValue(t.text, date);
return out;
}
QRegularExpression WedgeHeader::matchRegex(const QString &format) {
const QList<Tok> toks = tokenize(format);
QString p = "^";
for (int i = 0; i < toks.size(); ++i) {
const Tok &t = toks.at(i);
if (!t.literal) { p += tokPattern(t.text); continue; }
const QString lp = litPattern(t.text);
p += (i == toks.size() - 1) ? QString("(?:%1)?").arg(lp) : lp;
}
p += ".*";
return QRegularExpression(p, QRegularExpression::CaseInsensitiveOption
| QRegularExpression::MultilineOption);
}
QRegularExpression WedgeHeader::dateRegex(const QString &format, const QDate &date) {
const QList<Tok> toks = tokenize(format);
QString p = "^";
for (int i = 0; i < toks.size(); ++i) {
const Tok &t = toks.at(i);
if (t.literal) {
const QString lp = litPattern(t.text);
p += (i == toks.size() - 1) ? QString("(?:%1)?").arg(lp) : lp;
} else if (isDayTok(t.text)) {
p += "0?" + QRegularExpression::escape(QString::number(date.day()));
} else {
p += QRegularExpression::escape(tokValue(t.text, date));
}
}
p += ".*";
return QRegularExpression(p, QRegularExpression::CaseInsensitiveOption
| QRegularExpression::MultilineOption);
}