// This file was generated automatically by Qleany's generator, edit at your own risk! 
// If you do, be careful to not overwrite it when you run the generator again.
#pragma once

#include <QObject>
{% for header in headers -%}
#include {{ header }}
{% endfor %}

{% for field in fields -%}
{% if field.is_foreign -%}
using namespace {{ application_cpp_domain_name }}::Contracts::DTO::{{ field.foreign_feature_name }};
{% endif -%}
{% endfor %}

namespace {{ application_cpp_domain_name }}::Contracts::DTO::{{ feature_pascal_name }}
{

class {{ dto_pascal_type }}
{
    Q_GADGET
{% for field in fields %}
    Q_PROPERTY({{ field.type }} {{ field.name }} READ {{ field.name }} WRITE set{{ field.pascal_name }})
{%- endfor %}

  public:
    struct MetaData {
    {% for field in fields -%}
        bool {{ field.name }}Set = false;
    {% endfor -%}

        bool getSet(const QString &fieldName) const
        {
            {% for field in fields -%}
            if (fieldName == "{{ field.name }}")
            {
                return {{ field.name }}Set;
            }
            {%- endfor %}
            return false;
        }
        
        bool areDetailsSet() const
        {
           
                {% for field in fields %}
                {% if field.is_foreign %}
                if({{ field.name }}Set)
                    return true;
                {% endif %}
                {% endfor %}
            return false;
            
            
        }
    };

    {% if is_relation_dto %}
        {% for relation_field in relation_fields -%}
            {% if loop.first %}
            enum RelationField {
                Undefined,
            {% endif -%}
                {{ relation_field.pascal_name }}{% if not loop.last %},{% endif %}
            {% if loop.last -%}
            };
            Q_ENUM(RelationField);
            {% endif %}
        {%- endfor %}
    {% endif %}

    {{ dto_pascal_type }}() : {{ fields_init_values }}
    {
    }

    ~{{ dto_pascal_type }}()
    {
    }

    {{ dto_pascal_type }}({% for field in fields %}{% if field.type in ['int', 'double', 'float', 'bool'] %} {{ field.type }} {{ field.name }}{% else %} const {{ field.type }} &{{ field.name }}{% endif %}{% if not loop.last %}, {% endif %} {% endfor %}) 
        : {% for field in fields %}m_{{ field.name }}({{ field.name }}){% if not loop.last %}, {% endif %}{% endfor %}
    {
    }

    {{ dto_pascal_type }}(const {{ dto_pascal_type }} &other) : m_metaData(other.m_metaData), {% for field in fields %}m_{{ field.name }}(other.m_{{ field.name }}){% if not loop.last %}, {% endif %}{% endfor %}
    {
    }

    {% for field in fields %}{% if field.name == 'id' %}
    Q_INVOKABLE bool isValid() const
    {
        return m_id > 0;
    }

    Q_INVOKABLE bool isNull() const
    {
        return !isValid();
    }

    Q_INVOKABLE bool isInvalid() const
    {
        return !isValid();
    }
    {% endif %} {% endfor %}

    {{ dto_pascal_type }} &operator=(const {{ dto_pascal_type }} &other)
    {
        if (this != &other)
        {
            m_metaData = other.m_metaData;
            {% for field in fields -%}
            m_{{ field.name }} = other.m_{{ field.name }};
            {% endfor %}
        }
        return *this;
    }

    {{ dto_pascal_type }} &operator=(const {{ dto_pascal_type }} &&other)
    {
        if (this != &other)
        {
            m_metaData = other.m_metaData;
            {% for field in fields -%}
            m_{{ field.name }} = other.m_{{ field.name }};
            {% endfor %}
        }
        return *this;
    }

    {{ dto_pascal_type }} &mergeWith(const {{ dto_pascal_type }} &other)
    {
        if (this != &other)
        {
            {% for field in fields -%}
            if (other.m_metaData.{{ field.name }}Set)
            {
                m_{{ field.name }} = other.m_{{ field.name }};
                m_metaData.{{ field.name }}Set = true;
            }
            {% endfor %}
        }
        return *this;
    }

    //import operator
    {{ dto_pascal_type }} &operator<<(const {{ dto_pascal_type }} &other)
    {
        return mergeWith(other);
    }

    friend bool operator==(const {{ dto_pascal_type }} &lhs, const {{ dto_pascal_type }} &rhs);


    friend uint qHash(const {{ dto_pascal_type }} &dto, uint seed) noexcept;



{% for field in fields %}
    // ------ {{ field.name }} : -----

    {{ field.type }} {{ field.name }}() const
    {
        return m_{{ field.name }};
    }

    void set{{ field.pascal_name }}({% if field.type in ['int', 'double', 'float', 'bool'] %} {{ field.type }} {{ field.name }}{% else %} const {{ field.type }} &{{ field.name }}{% endif %})
    {
        m_{{ field.name }} = {{ field.name }};
        m_metaData.{{ field.name }}Set = true;
    }
    
{% endfor %}

    MetaData metaData() const
    {
        return m_metaData;
    }

  private:
  MetaData m_metaData;
{% for field in fields%}
    {{ field.type }} m_{{ field.name }};
{%- endfor %}
};

inline bool operator==(const {{ dto_pascal_type }} &lhs, const {{ dto_pascal_type }} &rhs)
{

    return 
            {% for field in fields -%}
            lhs.m_{{ field.name }} == rhs.m_{{ field.name }} {% if not loop.last %} && {% endif %}
            {%- endfor %}
    ;
}

inline uint qHash(const {{ dto_pascal_type }} &dto, uint seed = 0) noexcept
{        // Seed the hash with the parent class's hash
        uint hash = 0;

        // Combine with this class's properties
        {% for field in fields -%}
        hash ^= ::qHash(dto.m_{{ field.name }}, seed);
        {% endfor %}

        return hash;
}

} // namespace {{ application_cpp_domain_name }}::Contracts::DTO::{{ feature_pascal_name }}
Q_DECLARE_METATYPE({{ application_cpp_domain_name }}::Contracts::DTO::{{ feature_pascal_name }}::{{ dto_pascal_type }})
