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

#include <QRegularExpression>

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

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

class Union::Properties::LinePropertyPrivate
{
public:
    std::optional<qreal> size;
    std::optional<Union::Color> color;
    std::optional<Union::Properties::LineStyle> style;
    std::unique_ptr<ImageProperty> image;
};

LineProperty::LineProperty()
    : d(std::make_unique<LinePropertyPrivate>())
{
}

LineProperty::LineProperty(const LineProperty &other)
    : d(std::make_unique<LinePropertyPrivate>())
{
    d->size = other.d->size;
    d->color = other.d->color;
    d->style = other.d->style;
    d->image = std::make_unique<ImageProperty>();
    *(d->image) = *(other.d->image);
}

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

LineProperty::~LineProperty() = default;

LineProperty &LineProperty::operator=(const LineProperty &other)
{
    if (this != &other) {
        d->size = other.d->size;
        d->color = other.d->color;
        d->style = other.d->style;
        *(d->image) = *(other.d->image);
    }
    return *this;
}

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

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

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

    d->size = newValue;
}

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

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

    d->color = newValue;
}

std::optional<Union::Properties::LineStyle> LineProperty::style() const
{
    return d->style;
}

void LineProperty::setStyle(const std::optional<Union::Properties::LineStyle> &newValue)
{
    if (newValue == d->style) {
        return;
    }

    d->style = newValue;
}

ImageProperty *LineProperty::image() const
{
    return d->image.get();
}

void LineProperty::setImage(std::unique_ptr<ImageProperty> &&newValue)
{
    d->image = std::move(newValue);
}

bool LineProperty::hasAnyValue() const
{
    if (d->size.has_value()) {
        return true;
    }
    if (d->color.has_value()) {
        return true;
    }
    if (d->style.has_value()) {
        return true;
    }
    if (d->image && d->image->hasAnyValue()) {
        return true;
    }
    return false;
}

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

    if (d->size.has_value() && d->size.value() != emptyValue<qreal>()) {
        return false;
    }
    if (d->color.has_value() && d->color.value() != emptyValue<Union::Color>()) {
        return false;
    }
    if (d->style.has_value() && d->style.value() != emptyValue<Union::Properties::LineStyle>()) {
        return false;
    }
    if (d->image && !d->image->isEmpty()) {
        return false;
    }

    return true;
}

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

    out << indent(indentation, multiline, true) << "size: ";
    if (d->size) {
        out << d->size.value() << maybeNewLine;
    } 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) << "style: ";
    if (d->style) {
        out << d->style.value() << maybeNewLine;
    } else {
        out << empty << maybeNewLine;
    }
    out << indent(indentation, multiline, false) << "image: ";
    if (d->image) {
        out << d->image->toString(indentation + 2, flags);
    } else {
        out << empty << maybeNewLine;
    }

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

    out.flush();

    return result;
}

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

    if (!destination->d->size.has_value()) {
        destination->d->size = source->d->size;
    }
    if (!destination->d->color.has_value()) {
        destination->d->color = source->d->color;
    }
    if (!destination->d->style.has_value()) {
        destination->d->style = source->d->style;
    }
    if (source->d->image) {
        if (!destination->d->image) {
            destination->d->image = std::make_unique<ImageProperty>();
        }
        ImageProperty::resolveProperties(source->d->image.get(), destination->d->image.get());
    }
}

std::unique_ptr<LineProperty> LineProperty::empty()
{
    auto result = std::make_unique<LineProperty>();
    result->d->size = emptyValue<qreal>();
    result->d->color = emptyValue<Union::Color>();
    result->d->style = emptyValue<Union::Properties::LineStyle>();
    result->d->image = ImageProperty::empty();
    return result;
}

bool Union::Properties::operator==(const LineProperty &left, const LineProperty &right)
{
    if (left.size() != right.size()) {
        return false;
    }
    if (left.color() != right.color()) {
        return false;
    }
    if (left.style() != right.style()) {
        return false;
    }
    if (left.image() && right.image()) {
        if (*(left.image()) != *(right.image())) {
            return false;
        }
    } else if (left.image() != right.image()) {
        return false;
    }
    return true;
}

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

QDataStream &operator<<(QDataStream &stream, const Union::Properties::LineProperty *type)
{
    stream << type->size();
    stream << type->color();
    stream << type->style();
    {
        auto data = type->image();
        stream << bool(data);
        if (data) {
            stream << data;
        }
    }
    return stream;
}

QDataStream &operator>>(QDataStream &stream, std::unique_ptr<Union::Properties::LineProperty> &type)
{
    {
        std::optional<qreal> data;
        stream >> data;
        type->setSize(data);
    }
    {
        std::optional<Union::Color> data;
        stream >> data;
        type->setColor(data);
    }
    {
        std::optional<Union::Properties::LineStyle> data;
        stream >> data;
        type->setStyle(data);
    }
    {
        bool hasData;
        stream >> hasData;

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

    return stream;
}