/*
   SPDX-FileCopyrightText: 2017-2021 Volker Krause <vkrause@kde.org>

   SPDX-License-Identifier: LGPL-2.0-or-later
*/

#include "field.h"
#include "pass.h"
#include "pass_p.h"

#include <QGuiApplication>
#include <QJsonObject>

using namespace KPkPass;
using namespace Qt::Literals;

namespace KPkPass
{
class FieldPrivate
{
public:
    const Pass *pass = nullptr;
    QJsonObject obj;
};
}

Field::Field()
    : d(new FieldPrivate)
{
}

Field::Field(const Field &) = default;
Field::Field(Field &&) = default;
Field::~Field() = default;
Field &Field::operator=(const Field &) = default;

Field::Field(const QJsonObject &obj, const Pass *pass)
    : d(new FieldPrivate)
{
    d->pass = pass;
    d->obj = obj;
}

QString Field::key() const
{
    return d->obj.value(QLatin1StringView("key")).toString();
}

QString Field::label() const
{
    if (d->pass) {
        return d->pass->d->message(d->obj.value(QLatin1StringView("label")).toString());
    }
    return {};
}

QVariant Field::value() const
{
    if (!d->pass) {
        return {};
    }
    auto v = d->obj.value("attributedValue"_L1);
    if (v.isUndefined()) {
        v = d->obj.value("value"_L1);
    }

    if (v.isString()) {
        const auto s = d->pass->d->message(v.toString());
        const auto dt = QDateTime::fromString(s, Qt::ISODate);
        if (dt.isValid()) {
            return dt;
        }
        return s;
    }

    if (v.isDouble()) {
        return v.toDouble();
    }

    return {};
}

constexpr inline const auto PKDateStyleNone = "PKDateStyleNone"_L1;
constexpr inline const auto PKDateStyleShort = "PKDateStyleShort"_L1;
constexpr inline const auto PKDateStyleMedium = "PKDateStyleMedium"_L1;
constexpr inline const auto PKDateStyleLong = "PKDateStyleLong"_L1;
constexpr inline const auto PKDateStyleFull = "PKDateStyleFull"_L1;

QString Field::valueDisplayString() const
{
    const auto v = value();
    // see
    // https://developer.apple.com/library/archive/documentation/UserExperience/Reference/PassKit_Bundle/Chapters/FieldDictionary.html#//apple_ref/doc/uid/TP40012026-CH4-SW6
    // however, real-world data doesn't strictly follow that, so we have to guess a bit here...
    if (v.typeId() == QMetaType::QDateTime) {
        const auto dt = v.toDateTime();
        const auto dateStyle = d->obj.value("dateStyle"_L1);
        const auto timeStyle = d->obj.value("timeStyle"_L1);

        auto fmt = QLocale::ShortFormat;
        if (dateStyle == PKDateStyleLong || dateStyle == PKDateStyleFull) {
            fmt = QLocale::LongFormat;
        }
        // time only
        if (timeStyle == PKDateStyleNone || (timeStyle.isUndefined() && !dateStyle.isUndefined())) {
            return QLocale().toString(dt.date(), fmt);
        }
        // date only
        if (dateStyle == PKDateStyleNone || (dateStyle.isUndefined() && !timeStyle.isUndefined())) {
            return QLocale().toString(dt.time(), QLocale::ShortFormat);
        }

        return QLocale().toString(dt, fmt);
    }
    if (v.typeId() == QMetaType::Double) {
        const auto f = v.toDouble();
        if (const auto currency = currencyCode(); !currency.isEmpty()) {
            return QLocale().toCurrencyString(f, currency);
        }

        // TODO respect number formatting options
        if (double i; std::modf(f, &i) == 0) {
            return QString::number(static_cast<qint64>(f));
        }
        return QString::number(v.toDouble(), 'f');
    }

    return v.toString().trimmed();
}

QString Field::changeMessage() const
{
    if (!d->pass) {
        return {};
    }
    auto msg = d->pass->d->message(d->obj.value(QLatin1StringView("changeMessage")).toString());
    msg.replace(QLatin1StringView("%@"), valueDisplayString());
    return msg;
}

Qt::Alignment Field::textAlignment() const
{
    const auto alignStr = d->obj.value(QLatin1StringView("textAlignment")).toString();
    if (alignStr == QLatin1StringView("PKTextAlignmentLeft")) {
        return Qt::AlignLeft;
    } else if (alignStr == QLatin1StringView("PKTextAlignmentCenter")) {
        return Qt::AlignHCenter;
    } else if (alignStr == QLatin1StringView("PKTextAlignmentRight")) {
        return Qt::AlignRight;
    }
    return QGuiApplication::layoutDirection() == Qt::LeftToRight ? Qt::AlignLeft : Qt::AlignRight;
}

QString Field::currencyCode() const
{
    return d->obj.value("currencyCode"_L1).toString();
}

int Field::row() const
{
    return d->obj.value("row"_L1).toInt(0);
}

bool Field::isRichText() const
{
    const auto v = value();
    if (v.typeId() != QMetaType::QString) {
        return false;
    }
    const auto text = v.toString();
    auto idx = text.indexOf('<'_L1);
    if (idx >= 0 && idx < text.size() - 2) {
        return text[idx + 1].isLetter() || text[idx + 1] == '/'_L1;
    }
    idx = text.indexOf('&'_L1);
    for (auto i = idx; idx >= 0 && idx - i < 5; ++i) {
        if (text[i] == ';'_L1) {
            return true;
        }
    }
    return false;
}

#include "moc_field.cpp"
