// 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 "ImageProperty.h"

#include <QRegularExpression>

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

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

class Union::Properties::ImagePropertyPrivate
{
public:
    std::optional<std::filesystem::path> source;
    std::optional<qreal> width;
    std::optional<qreal> height;
    std::optional<qreal> xOffset;
    std::optional<qreal> yOffset;
    std::optional<Union::Properties::ImageFlags> flags;
    std::optional<Union::Color> maskColor;
};

ImageProperty::ImageProperty()
    : d(std::make_unique<ImagePropertyPrivate>())
{
}

ImageProperty::ImageProperty(const ImageProperty &other)
    : d(std::make_unique<ImagePropertyPrivate>())
{
    d->source = other.d->source;
    d->width = other.d->width;
    d->height = other.d->height;
    d->xOffset = other.d->xOffset;
    d->yOffset = other.d->yOffset;
    d->flags = other.d->flags;
    d->maskColor = other.d->maskColor;
}

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

ImageProperty::~ImageProperty() = default;

ImageProperty &ImageProperty::operator=(const ImageProperty &other)
{
    if (this != &other) {
        d->source = other.d->source;
        d->width = other.d->width;
        d->height = other.d->height;
        d->xOffset = other.d->xOffset;
        d->yOffset = other.d->yOffset;
        d->flags = other.d->flags;
        d->maskColor = other.d->maskColor;
    }
    return *this;
}

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

std::optional<std::filesystem::path> ImageProperty::source() const
{
    return d->source;
}

void ImageProperty::setSource(const std::optional<std::filesystem::path> &newValue)
{
    if (newValue == d->source) {
        return;
    }

    d->source = newValue;
}

std::optional<qreal> ImageProperty::width() const
{
    return d->width;
}

void ImageProperty::setWidth(const std::optional<qreal> &newValue)
{
    if (newValue == d->width) {
        return;
    }

    d->width = newValue;
}

std::optional<qreal> ImageProperty::height() const
{
    return d->height;
}

void ImageProperty::setHeight(const std::optional<qreal> &newValue)
{
    if (newValue == d->height) {
        return;
    }

    d->height = newValue;
}

std::optional<qreal> ImageProperty::xOffset() const
{
    return d->xOffset;
}

void ImageProperty::setXOffset(const std::optional<qreal> &newValue)
{
    if (newValue == d->xOffset) {
        return;
    }

    d->xOffset = newValue;
}

std::optional<qreal> ImageProperty::yOffset() const
{
    return d->yOffset;
}

void ImageProperty::setYOffset(const std::optional<qreal> &newValue)
{
    if (newValue == d->yOffset) {
        return;
    }

    d->yOffset = newValue;
}

std::optional<Union::Properties::ImageFlags> ImageProperty::flags() const
{
    return d->flags;
}

void ImageProperty::setFlags(const std::optional<Union::Properties::ImageFlags> &newValue)
{
    if (newValue == d->flags) {
        return;
    }

    d->flags = newValue;
}

std::optional<Union::Color> ImageProperty::maskColor() const
{
    return d->maskColor;
}

void ImageProperty::setMaskColor(const std::optional<Union::Color> &newValue)
{
    if (newValue == d->maskColor) {
        return;
    }

    d->maskColor = newValue;
}

bool ImageProperty::hasAnyValue() const
{
    if (d->source.has_value()) {
        return true;
    }
    if (d->width.has_value()) {
        return true;
    }
    if (d->height.has_value()) {
        return true;
    }
    if (d->xOffset.has_value()) {
        return true;
    }
    if (d->yOffset.has_value()) {
        return true;
    }
    if (d->flags.has_value()) {
        return true;
    }
    if (d->maskColor.has_value()) {
        return true;
    }
    return false;
}

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

    if (d->source.has_value() && d->source.value() != emptyValue<std::filesystem::path>()) {
        return false;
    }
    if (d->width.has_value() && d->width.value() != emptyValue<qreal>()) {
        return false;
    }
    if (d->height.has_value() && d->height.value() != emptyValue<qreal>()) {
        return false;
    }
    if (d->xOffset.has_value() && d->xOffset.value() != emptyValue<qreal>()) {
        return false;
    }
    if (d->yOffset.has_value() && d->yOffset.value() != emptyValue<qreal>()) {
        return false;
    }
    if (d->flags.has_value() && d->flags.value() != emptyValue<Union::Properties::ImageFlags>()) {
        return false;
    }
    if (d->maskColor.has_value() && d->maskColor.value() != emptyValue<Union::Color>()) {
        return false;
    }

    return true;
}

QString ImageProperty::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 << "ImageProperty(" << maybeNewLine;
    } else if (indentation > 0) {
        out << maybeNewLine;
    }

    out << indent(indentation, multiline, true) << "source: ";
    if (d->source) {
        out << d->source->c_str() << maybeNewLine;
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "width: ";
    if (d->width) {
        out << d->width.value() << maybeNewLine;
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "height: ";
    if (d->height) {
        out << d->height.value() << maybeNewLine;
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "xOffset: ";
    if (d->xOffset) {
        out << d->xOffset.value() << maybeNewLine;
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "yOffset: ";
    if (d->yOffset) {
        out << d->yOffset.value() << maybeNewLine;
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "flags: ";
    if (d->flags) {
        out << d->flags.value() << maybeNewLine;
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "maskColor: ";
    if (d->maskColor) {
        out << d->maskColor->toString() << maybeNewLine;
    } else {
        out << empty << maybeNewLine;
    }

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

    out.flush();

    return result;
}

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

    if (!destination->d->source.has_value()) {
        destination->d->source = source->d->source;
    }
    if (!destination->d->width.has_value()) {
        destination->d->width = source->d->width;
    }
    if (!destination->d->height.has_value()) {
        destination->d->height = source->d->height;
    }
    if (!destination->d->xOffset.has_value()) {
        destination->d->xOffset = source->d->xOffset;
    }
    if (!destination->d->yOffset.has_value()) {
        destination->d->yOffset = source->d->yOffset;
    }
    if (!destination->d->flags.has_value()) {
        destination->d->flags = source->d->flags;
    }
    if (!destination->d->maskColor.has_value()) {
        destination->d->maskColor = source->d->maskColor;
    }
}

std::unique_ptr<ImageProperty> ImageProperty::empty()
{
    auto result = std::make_unique<ImageProperty>();
    result->d->source = emptyValue<std::filesystem::path>();
    result->d->width = emptyValue<qreal>();
    result->d->height = emptyValue<qreal>();
    result->d->xOffset = emptyValue<qreal>();
    result->d->yOffset = emptyValue<qreal>();
    result->d->flags = emptyValue<Union::Properties::ImageFlags>();
    result->d->maskColor = emptyValue<Union::Color>();
    return result;
}

bool Union::Properties::operator==(const ImageProperty &left, const ImageProperty &right)
{
    if (left.source() != right.source()) {
        return false;
    }
    if (left.width() != right.width()) {
        return false;
    }
    if (left.height() != right.height()) {
        return false;
    }
    if (left.xOffset() != right.xOffset()) {
        return false;
    }
    if (left.yOffset() != right.yOffset()) {
        return false;
    }
    if (left.flags() != right.flags()) {
        return false;
    }
    if (left.maskColor() != right.maskColor()) {
        return false;
    }
    return true;
}

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

QDataStream &operator<<(QDataStream &stream, const Union::Properties::ImageProperty *type)
{
    stream << type->source();
    stream << type->width();
    stream << type->height();
    stream << type->xOffset();
    stream << type->yOffset();
    stream << type->flags();
    stream << type->maskColor();
    return stream;
}

QDataStream &operator>>(QDataStream &stream, std::unique_ptr<Union::Properties::ImageProperty> &type)
{
    {
        std::optional<std::filesystem::path> data;
        stream >> data;
        type->setSource(data);
    }
    {
        std::optional<qreal> data;
        stream >> data;
        type->setWidth(data);
    }
    {
        std::optional<qreal> data;
        stream >> data;
        type->setHeight(data);
    }
    {
        std::optional<qreal> data;
        stream >> data;
        type->setXOffset(data);
    }
    {
        std::optional<qreal> data;
        stream >> data;
        type->setYOffset(data);
    }
    {
        std::optional<Union::Properties::ImageFlags> data;
        stream >> data;
        type->setFlags(data);
    }
    {
        std::optional<Union::Color> data;
        stream >> data;
        type->setMaskColor(data);
    }

    return stream;
}