/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtSystems module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qserviceinterfacedescriptor_p.h"
#ifndef QT_NO_DATASTREAM
#include <qdatastream.h>
#endif

#include <QDebug>
#include <QStringList>

QT_BEGIN_NAMESPACE

/*!
    \class QServiceInterfaceDescriptor
    \ingroup servicefw
    \inmodule QtServiceFramework
    \brief The QServiceInterfaceDescriptor class identifies a service implementation.

    A service can implement multiple interfaces and each interface can have multiple implementations.
    The QServiceInterfaceDescriptor class enscapsulates this information, as illustrated
    by the diagram below.

    \image qserviceinterfacedescriptor.png Service-Interface-Implementation

    The major version tag indicates the interface version and the minor version tag identifies the implementation
    version. Subsequent versions of the same interface must be binary compatible to previous versions
    of the same interface.

    In the above example service A and B implement the interface \e com.nokia.qt.x.
    In fact Service A provides two different implementations for the very same interface.
    This is indicated by the changed minor version number. Although Service B is
    using the same interface it's implementation actually utilizes the second version of
    the interface \e com.nokia.qt.x. Binary compatibility guarantees that clients
    who know version 1 can utilize version 2. If an existing interface has to be changed
    in a non-compatible way a new interface (name) is required.

    \section1 Namespaces

    A QServiceInterfaceDescriptor (the quadruble of service name,
    interface name, interface version and implementation version) uniquely
    identifies a service implementation on a device. Interface names follow
    the java namespace convention.

    The namespace \e com.nokia.qt.* is reserved for future Qt development.

    \sa QServiceFilter, QServiceManager
*/

/*!
    \enum QServiceInterfaceDescriptor::Attribute

    This enum describes the possible attribute types which can be attached
    to a QServiceInterfaceDescriptor.

    \value      Capabilities            The capabilities attribute is a QStringList and
                                        describes the capabilities that a service client
                                        would require to use the service if capability
                                        checks are enforced.
    \value      Location                This attribute points to either the location
                                        where the plug-in providing this service is stored or
                                        where the name of the service IPC path is found.
                                        If the service is plug-in based the location is the
                                        name and/or path of the plugin. If the service is
                                        IPC based the location is the name of the socket address.
    \value      ServiceDescription      This attribute provides a general description for
                                        the service.
    \value      InterfaceDescription    This attribute provides a description for the interface
                                        implementation.
    \value      ServiceType             This attribute specifies the QService::Type that the
                                        service is being provided.
*/

/*!
    Creates a new QServiceInterfaceDescriptor.
*/
QServiceInterfaceDescriptor::QServiceInterfaceDescriptor()
    :  d(0)
{
}

/*!
    Destroys the QServiceInterfaceDescriptor object.
*/
QServiceInterfaceDescriptor::~QServiceInterfaceDescriptor()
{
    if (d)
        delete d;
}

/*!
    Creates a copy of QServiceInterfaceDescriptor contained in \a other.
*/
QServiceInterfaceDescriptor::QServiceInterfaceDescriptor(const QServiceInterfaceDescriptor& other)
    : d(0)
{
    (*this) = other; //use assignment operator
}

/*!
    \fn  QServiceInterfaceDescriptor& QServiceInterfaceDescriptor::operator=(const QServiceInterfaceDescriptor& other)

    Copies the content of the QServiceInterfaceDescriptor object contained
    in \a other into this one.
*/
QServiceInterfaceDescriptor& QServiceInterfaceDescriptor::operator=(const QServiceInterfaceDescriptor& other)
{
    if (&other == this)
        return *this;

    if ( !other.isValid() ) {
        if (d)
            delete d;
        d = 0;
        return *this;
    }

    if (!d)
        d = new QServiceInterfaceDescriptorPrivate;

    (*d) = *(other.d);
    return *this;
}

/*!
    \fn bool QServiceInterfaceDescriptor::operator==(const QServiceInterfaceDescriptor& other) const

    Compares a QServiceInterfaceDescriptor to \a other. Returns true if they
    are equal and false otherwise.
*/
bool QServiceInterfaceDescriptor::operator==(const QServiceInterfaceDescriptor& other) const
{
    if (isValid() ^ other.isValid())
        return false;

    if (!d)
        return true;

    if ((*d) == *(other.d))
        return true;
    return false;
}

/*!
    \fn bool QServiceInterfaceDescriptor::operator!=(const QServiceInterfaceDescriptor& other) const

    Compares a QServiceInterfaceDescriptor to \a other. Returns true
    if they are not equal and false otherwise.
*/

/*!
    \fn bool QServiceInterfaceDescriptor::isValid() const

    Returns true if this descriptor is valid; otherwise returns false.
*/
bool QServiceInterfaceDescriptor::isValid() const
{
    return d ? true : false;
}

/*!
    \fn  bool QServiceInterfaceDescriptor::scope() const

    Returns true if this implementation is provided for all users on the system.

    \sa QService::Scope
*/
QService::Scope QServiceInterfaceDescriptor::scope() const
{
    return d ? d->scope : QService::UserScope;
}

/*!
    \fn  QString QServiceInterfaceDescriptor::serviceName() const

    Returns the name of service that provides this implementation.
*/
QString QServiceInterfaceDescriptor::serviceName() const
{
    return d ? d->serviceName : QString();
}

/*!
    \fn  QString QServiceInterfaceDescriptor::interfaceName() const

    Returns the name of the interface that is implemented.
*/
QString QServiceInterfaceDescriptor::interfaceName() const
{
    return d ? d->interfaceName : QString();
}

/*!
    \fn  int QServiceInterfaceDescriptor::majorVersion() const

    Returns the version of the interface.

    Subsequent versions of an interface are binary compatible
    to previous versions of the same interface. If an interface
    is broken it must use a new interface name.
*/
int QServiceInterfaceDescriptor::majorVersion() const
{
    return d ? d->major : -1;
}

/*!
    \fn  int QServiceInterfaceDescriptor::minorVersion() const

    Returns the version of the implementation.
*/
int QServiceInterfaceDescriptor::minorVersion() const
{
    return d ? d->minor : -1;
}

/*!
    \fn  QVariant QServiceInterfaceDescriptor::attribute(QServiceInterfaceDescriptor::Attribute which) const

    Returns the value for the attribute \a which; otherwise returns
    an invalid QVariant.
*/
QVariant QServiceInterfaceDescriptor::attribute(QServiceInterfaceDescriptor::Attribute which) const
{
    if (d)
        return d->attributes.value(which);
    return QVariant();
}

/*!
    \fn  QString QServiceInterfaceDescriptor::customAttribute(const QString& which) const

    Returns the value for the custom attribute \a which; otherwise
    returns a null string.
*/
QString QServiceInterfaceDescriptor::customAttribute(const QString& which) const
{
    if (d)
        return d->customAttributes[which];
    return QString();
}

/*!
  Returns a list of custom attributes attached to the service.
 */
QStringList QServiceInterfaceDescriptor::customAttributes() const
{
    if (d)
        return d->customAttributes.keys();
    return QStringList();
}

#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QServiceInterfaceDescriptor &desc)
{
    if (desc.isValid()) {
        QString serviceInterface = QString(QLatin1String("%1 %2.%3")).arg(desc.interfaceName())
                .arg(desc.majorVersion() < 0 ? '?' : desc.majorVersion())
                .arg(desc.minorVersion() < 0 ? '?' : desc.minorVersion());
        dbg.nospace() << "QServiceInterfaceDescriptor(";
        dbg.nospace() << "service=" << desc.serviceName() << ", ";
        dbg.nospace() << "interface=" << serviceInterface;
        dbg.nospace() << ")";
    } else {
        dbg.nospace() << "QServiceInterfaceDescriptor(invalid)";
    }
    return dbg.space();
}
#endif

#ifndef QT_NO_DATASTREAM

QDataStream &operator<<(QDataStream &out, const QServiceInterfaceDescriptor::Attribute &k)
{
    out << qint8(k);
    return out;
}

QDataStream &operator>>(QDataStream &in, QServiceInterfaceDescriptor::Attribute &k)
{
    quint8 key;
    in >> key;
    k = (QServiceInterfaceDescriptor::Attribute)key;
    return in;
}
/*!
    \fn QDataStream &operator<<(QDataStream &out, const QServiceInterfaceDescriptor &dc)
    \relates QServiceInterfaceDescriptor

    Writes service interface descriptor \a dc to the stream \a out and returns a reference
    to the stream.
*/

QDataStream &operator<<(QDataStream &out, const QServiceInterfaceDescriptor &dc)
{
    const quint32 magicNumber = 0x77AFAFA;
    const quint16 majorVersion = 1;
    const quint16 minorVersion = 0;
    const qint8 valid = dc.isValid();
    out << magicNumber << majorVersion << minorVersion;
    out << valid;
    if (valid) {
       out << dc.d->serviceName;
       out << dc.d->interfaceName;
       out << dc.d->major;
       out << dc.d->minor;
       out << dc.d->attributes;
       out << dc.d->customAttributes;
       out << (qint8)dc.d->scope;
    }
    return out;
}

/*!
    \fn QDataStream &operator>>(QDataStream &in, QServiceInterfaceDescriptor &dc)
    \relates QServiceInterfaceDescriptor

    Reads a service interface descriptor into \a dc from the stream \a in and returns a
    reference to the stream.
*/
QDataStream &operator>>(QDataStream &in, QServiceInterfaceDescriptor &dc)
{
    const quint32 magicNumber = 0x77AFAFA;
    quint32 storedMagicNumber;
    in >> storedMagicNumber;
    if (storedMagicNumber != magicNumber) {
        qWarning() << "Datastream doesn't provide searialized QServiceInterfaceDescriptor";
        return in;
    }

    const quint16 currentMajorVersion = 1;
    quint16 majorVersion = 0;
    quint16 minorVersion = 0;

    in >> majorVersion >> minorVersion;
    if (majorVersion != currentMajorVersion) {
        qWarning() << "Unknown serialization format for QServiceInterfaceDescriptor.";
        return in;
    }
    //Allow all minor versions.

    qint8 valid;
    in >> valid;
    if (valid) {
        if (!dc.isValid())
            dc.d = new QServiceInterfaceDescriptorPrivate;
        in >> dc.d->serviceName;
        in >> dc.d->interfaceName;
        in >> dc.d->major;
        in >> dc.d->minor;
        in >> dc.d->attributes;
        in >> dc.d->customAttributes;
        in >> valid;
        dc.d->scope = (QService::Scope) valid;
    } else { //input stream contains invalid descriptor
        //use assignment operator
        dc = QServiceInterfaceDescriptor();
    }

    return in;
}
#endif //QT_NO_DATASTREAM



QT_END_NAMESPACE

