// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
// SPDX-FileCopyrightText: 2024 Arjen Hiemstra <ahiemstra@heimr.nl>
//
// This file is automatically generated from property.cpp.j2.
// To regenerate, run `tools/propertygenerator/generate_properties.py`.

#include "SizeProperty.h"

#include <QRegularExpression>

#include "PropertiesTypes.h"
#include "QDataStreamExtras.h"

using namespace Union::Properties;
using namespace Qt::StringLiterals;

class Union::Properties::SizePropertyPrivate
{
public:
    std::optional<qreal> left;
    std::optional<qreal> right;
    std::optional<qreal> top;
    std::optional<qreal> bottom;
};

SizeProperty::SizeProperty()
    : d(std::make_unique<SizePropertyPrivate>())
{
}

SizeProperty::SizeProperty(const SizeProperty &other)
    : d(std::make_unique<SizePropertyPrivate>())
{
    d->left = other.d->left;
    d->right = other.d->right;
    d->top = other.d->top;
    d->bottom = other.d->bottom;
}

SizeProperty::SizeProperty(SizeProperty &&other)
    : d(std::move(other.d))
{
}

SizeProperty::~SizeProperty() = default;

SizeProperty &SizeProperty::operator=(const SizeProperty &other)
{
    if (this != &other) {
        d->left = other.d->left;
        d->right = other.d->right;
        d->top = other.d->top;
        d->bottom = other.d->bottom;
    }
    return *this;
}

SizeProperty &SizeProperty::operator=(SizeProperty &&other)
{
    std::swap(d, other.d);
    return *this;
}

std::optional<qreal> SizeProperty::left() const
{
    return d->left;
}

void SizeProperty::setLeft(const std::optional<qreal> &newValue)
{
    if (newValue == d->left) {
        return;
    }

    d->left = newValue;
}

std::optional<qreal> SizeProperty::right() const
{
    return d->right;
}

void SizeProperty::setRight(const std::optional<qreal> &newValue)
{
    if (newValue == d->right) {
        return;
    }

    d->right = newValue;
}

std::optional<qreal> SizeProperty::top() const
{
    return d->top;
}

void SizeProperty::setTop(const std::optional<qreal> &newValue)
{
    if (newValue == d->top) {
        return;
    }

    d->top = newValue;
}

std::optional<qreal> SizeProperty::bottom() const
{
    return d->bottom;
}

void SizeProperty::setBottom(const std::optional<qreal> &newValue)
{
    if (newValue == d->bottom) {
        return;
    }

    d->bottom = newValue;
}

bool SizeProperty::hasAnyValue() const
{
    if (d->left.has_value()) {
        return true;
    }
    if (d->right.has_value()) {
        return true;
    }
    if (d->top.has_value()) {
        return true;
    }
    if (d->bottom.has_value()) {
        return true;
    }
    return false;
}

bool SizeProperty::isEmpty() const
{
    if (!hasAnyValue()) {
        return true;
    }

    if (d->left.has_value() && d->left.value() != emptyValue<qreal>()) {
        return false;
    }
    if (d->right.has_value() && d->right.value() != emptyValue<qreal>()) {
        return false;
    }
    if (d->top.has_value() && d->top.value() != emptyValue<qreal>()) {
        return false;
    }
    if (d->bottom.has_value() && d->bottom.value() != emptyValue<qreal>()) {
        return false;
    }

    return true;
}

QString SizeProperty::toString(int indentation, ToStringFlags flags) const
{
    if (!hasAnyValue()) {
        return u"(empty)"_s;
    }

    const bool multiline = flags & ToStringFlag::MultiLine;
    const bool types = flags & ToStringFlag::Types;

    QString result;
    QTextStream out(&result);

    constexpr auto indent = [](int amount, bool multiline, bool first) {
        if (multiline) {
            return QByteArray(amount, ' ');
        } else if (!first) {
            return QByteArray(", ");
        } else {
            return QByteArray(" ");
        }
    };

    const QByteArray maybeNewLine = multiline ? "\n" : "";
    const QByteArray empty = "(empty)";

    if (types) {
        out << "SizeProperty(" << maybeNewLine;
    } else if (indentation > 0) {
        out << maybeNewLine;
    }

    out << indent(indentation, multiline, true) << "left: ";
    if (d->left) {
        out << d->left.value() << maybeNewLine;
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "right: ";
    if (d->right) {
        out << d->right.value() << maybeNewLine;
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "top: ";
    if (d->top) {
        out << d->top.value() << maybeNewLine;
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "bottom: ";
    if (d->bottom) {
        out << d->bottom.value() << maybeNewLine;
    } else {
        out << empty << maybeNewLine;
    }

    if (types) {
        out << indent(indentation - 2, multiline, true) << ")";
    }
    out << maybeNewLine;

    out.flush();

    return result;
}

void SizeProperty::resolveProperties(const SizeProperty *source, SizeProperty *destination)
{
    if (!source || !destination) {
        return;
    }

    if (!destination->d->left.has_value()) {
        destination->d->left = source->d->left;
    }
    if (!destination->d->right.has_value()) {
        destination->d->right = source->d->right;
    }
    if (!destination->d->top.has_value()) {
        destination->d->top = source->d->top;
    }
    if (!destination->d->bottom.has_value()) {
        destination->d->bottom = source->d->bottom;
    }
}

std::unique_ptr<SizeProperty> SizeProperty::empty()
{
    auto result = std::make_unique<SizeProperty>();
    result->d->left = emptyValue<qreal>();
    result->d->right = emptyValue<qreal>();
    result->d->top = emptyValue<qreal>();
    result->d->bottom = emptyValue<qreal>();
    return result;
}

QMarginsF SizeProperty::toMargins() const
{
    return QMarginsF{d->left.value_or(0.0), d->top.value_or(0.0), d->right.value_or(0.0), d->bottom.value_or(0.0)};
}

bool Union::Properties::operator==(const SizeProperty &left, const SizeProperty &right)
{
    if (left.left() != right.left()) {
        return false;
    }
    if (left.right() != right.right()) {
        return false;
    }
    if (left.top() != right.top()) {
        return false;
    }
    if (left.bottom() != right.bottom()) {
        return false;
    }
    return true;
}

QDebug operator<<(QDebug debug, Union::Properties::SizeProperty *type)
{
    QDebugStateSaver saver(debug);
    debug.nospace() << qPrintable(type->toString(0, ToStringFlag::Types));
    return debug;
}

QDataStream &operator<<(QDataStream &stream, const Union::Properties::SizeProperty *type)
{
    stream << type->left();
    stream << type->right();
    stream << type->top();
    stream << type->bottom();
    return stream;
}

QDataStream &operator>>(QDataStream &stream, std::unique_ptr<Union::Properties::SizeProperty> &type)
{
    {
        std::optional<qreal> data;
        stream >> data;
        type->setLeft(data);
    }
    {
        std::optional<qreal> data;
        stream >> data;
        type->setRight(data);
    }
    {
        std::optional<qreal> data;
        stream >> data;
        type->setTop(data);
    }
    {
        std::optional<qreal> data;
        stream >> data;
        type->setBottom(data);
    }

    return stream;
}