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

#include <QRegularExpression>

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

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

class Union::Properties::ShadowPropertyPrivate
{
public:
    std::unique_ptr<OffsetProperty> offset;
    std::optional<Union::Color> color;
    std::optional<qreal> size;
    std::optional<qreal> blur;
    std::unique_ptr<LineProperty> left;
    std::unique_ptr<LineProperty> right;
    std::unique_ptr<LineProperty> top;
    std::unique_ptr<LineProperty> bottom;
    std::unique_ptr<CornerProperty> topLeft;
    std::unique_ptr<CornerProperty> topRight;
    std::unique_ptr<CornerProperty> bottomLeft;
    std::unique_ptr<CornerProperty> bottomRight;
};

ShadowProperty::ShadowProperty()
    : d(std::make_unique<ShadowPropertyPrivate>())
{
}

ShadowProperty::ShadowProperty(const ShadowProperty &other)
    : d(std::make_unique<ShadowPropertyPrivate>())
{
    d->offset = std::make_unique<OffsetProperty>();
    *(d->offset) = *(other.d->offset);
    d->color = other.d->color;
    d->size = other.d->size;
    d->blur = other.d->blur;
    d->left = std::make_unique<LineProperty>();
    *(d->left) = *(other.d->left);
    d->right = std::make_unique<LineProperty>();
    *(d->right) = *(other.d->right);
    d->top = std::make_unique<LineProperty>();
    *(d->top) = *(other.d->top);
    d->bottom = std::make_unique<LineProperty>();
    *(d->bottom) = *(other.d->bottom);
    d->topLeft = std::make_unique<CornerProperty>();
    *(d->topLeft) = *(other.d->topLeft);
    d->topRight = std::make_unique<CornerProperty>();
    *(d->topRight) = *(other.d->topRight);
    d->bottomLeft = std::make_unique<CornerProperty>();
    *(d->bottomLeft) = *(other.d->bottomLeft);
    d->bottomRight = std::make_unique<CornerProperty>();
    *(d->bottomRight) = *(other.d->bottomRight);
}

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

ShadowProperty::~ShadowProperty() = default;

ShadowProperty &ShadowProperty::operator=(const ShadowProperty &other)
{
    if (this != &other) {
        *(d->offset) = *(other.d->offset);
        d->color = other.d->color;
        d->size = other.d->size;
        d->blur = other.d->blur;
        *(d->left) = *(other.d->left);
        *(d->right) = *(other.d->right);
        *(d->top) = *(other.d->top);
        *(d->bottom) = *(other.d->bottom);
        *(d->topLeft) = *(other.d->topLeft);
        *(d->topRight) = *(other.d->topRight);
        *(d->bottomLeft) = *(other.d->bottomLeft);
        *(d->bottomRight) = *(other.d->bottomRight);
    }
    return *this;
}

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

OffsetProperty *ShadowProperty::offset() const
{
    return d->offset.get();
}

void ShadowProperty::setOffset(std::unique_ptr<OffsetProperty> &&newValue)
{
    d->offset = std::move(newValue);
}

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

void ShadowProperty::setColor(const std::optional<Union::Color> &newValue)
{
    if (newValue == d->color) {
        return;
    }

    d->color = newValue;
}

std::optional<qreal> ShadowProperty::size() const
{
    return d->size;
}

void ShadowProperty::setSize(const std::optional<qreal> &newValue)
{
    if (newValue == d->size) {
        return;
    }

    d->size = newValue;
}

std::optional<qreal> ShadowProperty::blur() const
{
    return d->blur;
}

void ShadowProperty::setBlur(const std::optional<qreal> &newValue)
{
    if (newValue == d->blur) {
        return;
    }

    d->blur = newValue;
}

LineProperty *ShadowProperty::left() const
{
    return d->left.get();
}

void ShadowProperty::setLeft(std::unique_ptr<LineProperty> &&newValue)
{
    d->left = std::move(newValue);
}

LineProperty *ShadowProperty::right() const
{
    return d->right.get();
}

void ShadowProperty::setRight(std::unique_ptr<LineProperty> &&newValue)
{
    d->right = std::move(newValue);
}

LineProperty *ShadowProperty::top() const
{
    return d->top.get();
}

void ShadowProperty::setTop(std::unique_ptr<LineProperty> &&newValue)
{
    d->top = std::move(newValue);
}

LineProperty *ShadowProperty::bottom() const
{
    return d->bottom.get();
}

void ShadowProperty::setBottom(std::unique_ptr<LineProperty> &&newValue)
{
    d->bottom = std::move(newValue);
}

CornerProperty *ShadowProperty::topLeft() const
{
    return d->topLeft.get();
}

void ShadowProperty::setTopLeft(std::unique_ptr<CornerProperty> &&newValue)
{
    d->topLeft = std::move(newValue);
}

CornerProperty *ShadowProperty::topRight() const
{
    return d->topRight.get();
}

void ShadowProperty::setTopRight(std::unique_ptr<CornerProperty> &&newValue)
{
    d->topRight = std::move(newValue);
}

CornerProperty *ShadowProperty::bottomLeft() const
{
    return d->bottomLeft.get();
}

void ShadowProperty::setBottomLeft(std::unique_ptr<CornerProperty> &&newValue)
{
    d->bottomLeft = std::move(newValue);
}

CornerProperty *ShadowProperty::bottomRight() const
{
    return d->bottomRight.get();
}

void ShadowProperty::setBottomRight(std::unique_ptr<CornerProperty> &&newValue)
{
    d->bottomRight = std::move(newValue);
}

bool ShadowProperty::hasAnyValue() const
{
    if (d->offset && d->offset->hasAnyValue()) {
        return true;
    }
    if (d->color.has_value()) {
        return true;
    }
    if (d->size.has_value()) {
        return true;
    }
    if (d->blur.has_value()) {
        return true;
    }
    if (d->left && d->left->hasAnyValue()) {
        return true;
    }
    if (d->right && d->right->hasAnyValue()) {
        return true;
    }
    if (d->top && d->top->hasAnyValue()) {
        return true;
    }
    if (d->bottom && d->bottom->hasAnyValue()) {
        return true;
    }
    if (d->topLeft && d->topLeft->hasAnyValue()) {
        return true;
    }
    if (d->topRight && d->topRight->hasAnyValue()) {
        return true;
    }
    if (d->bottomLeft && d->bottomLeft->hasAnyValue()) {
        return true;
    }
    if (d->bottomRight && d->bottomRight->hasAnyValue()) {
        return true;
    }
    return false;
}

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

    if (d->offset && !d->offset->isEmpty()) {
        return false;
    }
    if (d->color.has_value() && d->color.value() != emptyValue<Union::Color>()) {
        return false;
    }
    if (d->size.has_value() && d->size.value() != emptyValue<qreal>()) {
        return false;
    }
    if (d->blur.has_value() && d->blur.value() != emptyValue<qreal>()) {
        return false;
    }
    if (d->left && !d->left->isEmpty()) {
        return false;
    }
    if (d->right && !d->right->isEmpty()) {
        return false;
    }
    if (d->top && !d->top->isEmpty()) {
        return false;
    }
    if (d->bottom && !d->bottom->isEmpty()) {
        return false;
    }
    if (d->topLeft && !d->topLeft->isEmpty()) {
        return false;
    }
    if (d->topRight && !d->topRight->isEmpty()) {
        return false;
    }
    if (d->bottomLeft && !d->bottomLeft->isEmpty()) {
        return false;
    }
    if (d->bottomRight && !d->bottomRight->isEmpty()) {
        return false;
    }

    return true;
}

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

    out << indent(indentation, multiline, true) << "offset: ";
    if (d->offset) {
        out << d->offset->toString(indentation + 2, flags);
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "color: ";
    if (d->color) {
        out << d->color->toString() << maybeNewLine;
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "size: ";
    if (d->size) {
        out << d->size.value() << maybeNewLine;
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "blur: ";
    if (d->blur) {
        out << d->blur.value() << maybeNewLine;
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "left: ";
    if (d->left) {
        out << d->left->toString(indentation + 2, flags);
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "right: ";
    if (d->right) {
        out << d->right->toString(indentation + 2, flags);
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "top: ";
    if (d->top) {
        out << d->top->toString(indentation + 2, flags);
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "bottom: ";
    if (d->bottom) {
        out << d->bottom->toString(indentation + 2, flags);
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "topLeft: ";
    if (d->topLeft) {
        out << d->topLeft->toString(indentation + 2, flags);
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "topRight: ";
    if (d->topRight) {
        out << d->topRight->toString(indentation + 2, flags);
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "bottomLeft: ";
    if (d->bottomLeft) {
        out << d->bottomLeft->toString(indentation + 2, flags);
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "bottomRight: ";
    if (d->bottomRight) {
        out << d->bottomRight->toString(indentation + 2, flags);
    } else {
        out << empty << maybeNewLine;
    }

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

    out.flush();

    return result;
}

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

    if (source->d->offset) {
        if (!destination->d->offset) {
            destination->d->offset = std::make_unique<OffsetProperty>();
        }
        OffsetProperty::resolveProperties(source->d->offset.get(), destination->d->offset.get());
    }
    if (!destination->d->color.has_value()) {
        destination->d->color = source->d->color;
    }
    if (!destination->d->size.has_value()) {
        destination->d->size = source->d->size;
    }
    if (!destination->d->blur.has_value()) {
        destination->d->blur = source->d->blur;
    }
    if (source->d->left) {
        if (!destination->d->left) {
            destination->d->left = std::make_unique<LineProperty>();
        }
        LineProperty::resolveProperties(source->d->left.get(), destination->d->left.get());
    }
    if (source->d->right) {
        if (!destination->d->right) {
            destination->d->right = std::make_unique<LineProperty>();
        }
        LineProperty::resolveProperties(source->d->right.get(), destination->d->right.get());
    }
    if (source->d->top) {
        if (!destination->d->top) {
            destination->d->top = std::make_unique<LineProperty>();
        }
        LineProperty::resolveProperties(source->d->top.get(), destination->d->top.get());
    }
    if (source->d->bottom) {
        if (!destination->d->bottom) {
            destination->d->bottom = std::make_unique<LineProperty>();
        }
        LineProperty::resolveProperties(source->d->bottom.get(), destination->d->bottom.get());
    }
    if (source->d->topLeft) {
        if (!destination->d->topLeft) {
            destination->d->topLeft = std::make_unique<CornerProperty>();
        }
        CornerProperty::resolveProperties(source->d->topLeft.get(), destination->d->topLeft.get());
    }
    if (source->d->topRight) {
        if (!destination->d->topRight) {
            destination->d->topRight = std::make_unique<CornerProperty>();
        }
        CornerProperty::resolveProperties(source->d->topRight.get(), destination->d->topRight.get());
    }
    if (source->d->bottomLeft) {
        if (!destination->d->bottomLeft) {
            destination->d->bottomLeft = std::make_unique<CornerProperty>();
        }
        CornerProperty::resolveProperties(source->d->bottomLeft.get(), destination->d->bottomLeft.get());
    }
    if (source->d->bottomRight) {
        if (!destination->d->bottomRight) {
            destination->d->bottomRight = std::make_unique<CornerProperty>();
        }
        CornerProperty::resolveProperties(source->d->bottomRight.get(), destination->d->bottomRight.get());
    }
}

std::unique_ptr<ShadowProperty> ShadowProperty::empty()
{
    auto result = std::make_unique<ShadowProperty>();
    result->d->offset = OffsetProperty::empty();
    result->d->color = emptyValue<Union::Color>();
    result->d->size = emptyValue<qreal>();
    result->d->blur = emptyValue<qreal>();
    result->d->left = LineProperty::empty();
    result->d->right = LineProperty::empty();
    result->d->top = LineProperty::empty();
    result->d->bottom = LineProperty::empty();
    result->d->topLeft = CornerProperty::empty();
    result->d->topRight = CornerProperty::empty();
    result->d->bottomLeft = CornerProperty::empty();
    result->d->bottomRight = CornerProperty::empty();
    return result;
}

bool Union::Properties::operator==(const ShadowProperty &left, const ShadowProperty &right)
{
    if (left.offset() && right.offset()) {
        if (*(left.offset()) != *(right.offset())) {
            return false;
        }
    } else if (left.offset() != right.offset()) {
        return false;
    }
    if (left.color() != right.color()) {
        return false;
    }
    if (left.size() != right.size()) {
        return false;
    }
    if (left.blur() != right.blur()) {
        return false;
    }
    if (left.left() && right.left()) {
        if (*(left.left()) != *(right.left())) {
            return false;
        }
    } else if (left.left() != right.left()) {
        return false;
    }
    if (left.right() && right.right()) {
        if (*(left.right()) != *(right.right())) {
            return false;
        }
    } else if (left.right() != right.right()) {
        return false;
    }
    if (left.top() && right.top()) {
        if (*(left.top()) != *(right.top())) {
            return false;
        }
    } else if (left.top() != right.top()) {
        return false;
    }
    if (left.bottom() && right.bottom()) {
        if (*(left.bottom()) != *(right.bottom())) {
            return false;
        }
    } else if (left.bottom() != right.bottom()) {
        return false;
    }
    if (left.topLeft() && right.topLeft()) {
        if (*(left.topLeft()) != *(right.topLeft())) {
            return false;
        }
    } else if (left.topLeft() != right.topLeft()) {
        return false;
    }
    if (left.topRight() && right.topRight()) {
        if (*(left.topRight()) != *(right.topRight())) {
            return false;
        }
    } else if (left.topRight() != right.topRight()) {
        return false;
    }
    if (left.bottomLeft() && right.bottomLeft()) {
        if (*(left.bottomLeft()) != *(right.bottomLeft())) {
            return false;
        }
    } else if (left.bottomLeft() != right.bottomLeft()) {
        return false;
    }
    if (left.bottomRight() && right.bottomRight()) {
        if (*(left.bottomRight()) != *(right.bottomRight())) {
            return false;
        }
    } else if (left.bottomRight() != right.bottomRight()) {
        return false;
    }
    return true;
}

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

QDataStream &operator<<(QDataStream &stream, const Union::Properties::ShadowProperty *type)
{
    {
        auto data = type->offset();
        stream << bool(data);
        if (data) {
            stream << data;
        }
    }
    stream << type->color();
    stream << type->size();
    stream << type->blur();
    {
        auto data = type->left();
        stream << bool(data);
        if (data) {
            stream << data;
        }
    }
    {
        auto data = type->right();
        stream << bool(data);
        if (data) {
            stream << data;
        }
    }
    {
        auto data = type->top();
        stream << bool(data);
        if (data) {
            stream << data;
        }
    }
    {
        auto data = type->bottom();
        stream << bool(data);
        if (data) {
            stream << data;
        }
    }
    {
        auto data = type->topLeft();
        stream << bool(data);
        if (data) {
            stream << data;
        }
    }
    {
        auto data = type->topRight();
        stream << bool(data);
        if (data) {
            stream << data;
        }
    }
    {
        auto data = type->bottomLeft();
        stream << bool(data);
        if (data) {
            stream << data;
        }
    }
    {
        auto data = type->bottomRight();
        stream << bool(data);
        if (data) {
            stream << data;
        }
    }
    return stream;
}

QDataStream &operator>>(QDataStream &stream, std::unique_ptr<Union::Properties::ShadowProperty> &type)
{
    {
        bool hasData;
        stream >> hasData;

        if (hasData) {
            auto data = std::make_unique<OffsetProperty>();
            stream >> data;
            type->setOffset(std::move(data));
        }
    }
    {
        std::optional<Union::Color> data;
        stream >> data;
        type->setColor(data);
    }
    {
        std::optional<qreal> data;
        stream >> data;
        type->setSize(data);
    }
    {
        std::optional<qreal> data;
        stream >> data;
        type->setBlur(data);
    }
    {
        bool hasData;
        stream >> hasData;

        if (hasData) {
            auto data = std::make_unique<LineProperty>();
            stream >> data;
            type->setLeft(std::move(data));
        }
    }
    {
        bool hasData;
        stream >> hasData;

        if (hasData) {
            auto data = std::make_unique<LineProperty>();
            stream >> data;
            type->setRight(std::move(data));
        }
    }
    {
        bool hasData;
        stream >> hasData;

        if (hasData) {
            auto data = std::make_unique<LineProperty>();
            stream >> data;
            type->setTop(std::move(data));
        }
    }
    {
        bool hasData;
        stream >> hasData;

        if (hasData) {
            auto data = std::make_unique<LineProperty>();
            stream >> data;
            type->setBottom(std::move(data));
        }
    }
    {
        bool hasData;
        stream >> hasData;

        if (hasData) {
            auto data = std::make_unique<CornerProperty>();
            stream >> data;
            type->setTopLeft(std::move(data));
        }
    }
    {
        bool hasData;
        stream >> hasData;

        if (hasData) {
            auto data = std::make_unique<CornerProperty>();
            stream >> data;
            type->setTopRight(std::move(data));
        }
    }
    {
        bool hasData;
        stream >> hasData;

        if (hasData) {
            auto data = std::make_unique<CornerProperty>();
            stream >> data;
            type->setBottomLeft(std::move(data));
        }
    }
    {
        bool hasData;
        stream >> hasData;

        if (hasData) {
            auto data = std::make_unique<CornerProperty>();
            stream >> data;
            type->setBottomRight(std::move(data));
        }
    }

    return stream;
}