/*!
    \page tutorial5.html
    \title Tutorial 5 - The "Network Info" plugin
    \brief The plugin shows you how to use the NetworkManager and how asynchronous actions work
    \ingroup tutorials
    
    \section1 Topics
    This tutorial will show you how to:
    \list
        \li \unicode{0x25B6} Use the hardware resource \l{NetworkManager}
    \endlist


    In the tutorial we make a plugin with the name \b {"Network Info"}. This plugin will use the \l{NetworkManager} hardware resource to fetch the location and WAN ip of your internet connection from \l{http://ip-api.com/json}. It will have an \l Action called \e "update" which will refresh the \l{State}{States} of the \l{Device}.

In order to get started with our new \b {"Network Info"} plugin we use the minimal plugin as template and start from there. Make a copy of the minimal folder and name the new folder \b networkinfo-diy. In this case \b{networkinfo-diy} because the folder \b networkinfo already exits from the \tt plugin-template repository.
    
    \section1 Create the basic structure
    \code
        $ cp -rv minimal/ networkinfo-diy
        ‘minimal/’ -> ‘networkinfo-diy’
        ‘minimal/plugins.pri’ -> ‘networkinfo-diy/plugins.pri’
        ‘minimal/minimal.pro’ -> ‘networkinfo-diy/minimal.pro’
        ‘minimal/devicepluginminimal.json’ -> ‘networkinfo-diy/devicepluginminimal.json’
        ‘minimal/devicepluginminimal.h’ -> ‘networkinfo-diy/devicepluginminimal.h’
        ‘minimal/devicepluginminimal.cpp’ -> ‘networkinfo-diy/devicepluginminimal.cpp’
    \endcode
    
    \note Delete the minimal.pro.user file if there is any.
    
    Now we can rename the files using the plugin name convention:
    \code
        $ cd networkinfo-diy/
        $ mv minimal.pro networkinfo.pro
        $ mv devicepluginminimal.h devicepluginnetworkinfo.h
        $ mv devicepluginminimal.cpp devicepluginnetworkinfo.cpp
        $ mv devicepluginminimal.json devicepluginnetworkinfo.json
    \endcode
    
    \section2 Change the \tt networkinfo.pro
    Open the \tt networkinfo.pro file with the \e {Qt Creator} and open that file in the editor:

    \code
        include(plugins.pri)

        TARGET = $$qtLibraryTarget(guh_devicepluginminimal)

        message("Building $$deviceplugin$${TARGET}.so")

        SOURCES += \
            devicepluginminimal.cpp \

        HEADERS += \
            devicepluginminimal.h \

    \endcode
    
    \list 1
        \li Change the \tt TARGET name form \tt guh_devicepluginminimal \unicode{0x2192} \tt guh_devicepluginnetworkinfo
        \li Change the SOURCES file from \tt devicepluginminimal.cpp \unicode{0x2192} \tt devicepluginnetworkinfo.cpp
        \li Change the HEADERS file from \tt devicepluginminimal.h \unicode{0x2192} \tt devicepluginnetworkinfo.h
    \endlist

    Your file sould look now like this:
    \code
        include(plugins.pri)

        TARGET = $$qtLibraryTarget(guh_devicepluginnetworkinfo)

        message("Building $$deviceplugin$${TARGET}.so")

        SOURCES += \
            devicepluginnetworkinfo.cpp \

        HEADERS += \
            devicepluginnetworkinfo.h \
    \endcode
    
    If you save the file, the header and source file should appear in the project structure of the \e {Qt Creator}.

    \section2 Change the \tt devicepluginnetworkinfo.h
    Open the \tt devicepluginnetworkinfo.h file.
    
    \code
        #ifndef DEVICEPLUGINMINIMAL_H
        #define DEVICEPLUGINMINIMAL_H

        #include "plugin/deviceplugin.h"
        #include "devicemanager.h"

        class DevicePluginMinimal : public DevicePlugin
        {
            Q_OBJECT

            Q_PLUGIN_METADATA(IID "guru.guh.DevicePlugin" FILE "devicepluginminimal.json")
            Q_INTERFACES(DevicePlugin)

        public:
            explicit DevicePluginMinimal();

            DeviceManager::HardwareResources requiredHardware() const override;
            DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
        };

        #endif // DEVICEPLUGINMINIMAL_H
    \endcode
    
    \list 1
        \li Change the \tt {#ifndef}, \tt {#define} and \tt #define name from \tt DEVICEPLUGINMINIMAL_H \unicode{0x2192} \tt DEVICEPLUGINNETWORKINFO_H
        \li Change the class name form \tt DevicePluginMinimal \unicode{0x2192} \tt DevicePluginNetworkInfo
        \li Change in the \tt Q_PLUGIN_METADATA line the \tt FILE parameter from \tt "devicepluginminimal.json" \unicode{0x2192} \tt "devicepluginnetworkinfo.json" to set \l{The Plugin JSON file}.
        \li Change the constructor name from \tt DevicePluginMinimal \unicode{0x2192} \tt DevicePluginNetworkInfo
    \endlist

    Your file sould look now like this:
    
    \code
        #ifndef DEVICEPLUGINNETWORKINFO_H
        #define DEVICEPLUGINNETWORKINFO_H

        #include "plugin/deviceplugin.h"
        #include "devicemanager.h"

        class DevicePluginNetworkInfo : public DevicePlugin
        {
            Q_OBJECT

            Q_PLUGIN_METADATA(IID "guru.guh.DevicePlugin" FILE "devicepluginnetworkinfo.json")
            Q_INTERFACES(DevicePlugin)

        public:
            explicit DevicePluginNetworkInfo();

            DeviceManager::HardwareResources requiredHardware() const override;
            DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
        };

        #endif // DEVICEPLUGINNETWORKINFO_H
    \endcode

    \section2 Change the \tt devicepluginnetworkinfo.cpp

    Open the \tt devicepluginnetworkinfo.h file.
    
    \code
        #include "devicepluginminimal.h"
        #include "plugininfo.h"

        DevicePluginMinimal::DevicePluginMinimal()
        {
        }

        DeviceManager::HardwareResources DevicePluginMinimal::requiredHardware() const
        {
            return DeviceManager::HardwareResourceNone;
        }

        DeviceManager::DeviceSetupStatus DevicePluginMinimal::setupDevice(Device *device)
        {
            Q_UNUSED(device)
            qCDebug(dcMinimal) << "Hello word! Setting up a new device:" << device->name();
            qCDebug(dcMinimal) << "The new device has the DeviceId" << device->id().toString();
            qCDebug(dcMinimal) << device->params();

            return DeviceManager::DeviceSetupStatusSuccess;
        }
    \endcode
    
    \list 1
        \li Change the \tt {#include "devicepluginminimal.h"} \unicode{0x2192} \tt {#include "devicepluginnetworkinfo.h"}
        \li Change in each method implementation the \tt DevicePluginMinimal \unicode{0x2192} \tt DevicePluginNetworkInfo namespace.
    \endlist
        
    Your file sould look now like this:
    
    \code
        #include "devicepluginnetworkinfo.h"
        #include "plugininfo.h"

        DevicePluginNetworkInfo::DevicePluginNetworkInfo()
        {
        }

        DeviceManager::HardwareResources DevicePluginNetworkInfo::requiredHardware() const
        {
            return DeviceManager::HardwareResourceNone;
        }

        DeviceManager::DeviceSetupStatus DevicePluginNetworkInfo::setupDevice(Device *device)
        {
            Q_UNUSED(device)
            qCDebug(dcMinimal) << "Hello word! Setting up a new device:" << device->name();
            qCDebug(dcMinimal) << "The new device has the DeviceId" << device->id().toString();
            qCDebug(dcMinimal) << device->params();

            return DeviceManager::DeviceSetupStatusSuccess;
        }
    \endcode

    The basic structure of our new \l{DevicePlugin} is finished. You may recognize that the \tt {plugininfo.h} file does not exist yet. You have to build the plugin to generate that file. Each time you change \l{The Plugin JSON file} this file will be new generated. Once the build step is finished, you can take a look at that file (curser in line \tt {#include "plugininfo.h"} and press \tt F2)

    You will see in the build output following section:    
    \code
        /usr/bin/guh-generateplugininfo ../networkinfo-diy/devicepluginnetworkinfo.json plugininfo.h
        ../networkinfo-diy/devicepluginnetworkinfo.json -> plugininfo.h
         --> generate plugininfo.h
        PluginId for plugin "Minimal plugin" = 6878754a-f27d-4007-a4e5-b030b55853f5
        define VendorId MinimalVendorId = 3897e82e-7c48-4591-9a2f-0f56c55a96a4
        define DeviceClassId minimalDeviceClassId = 7014e5f1-5b04-407a-a819-bbebd11fa372
        define logging category: "dcMinimal"
         --> generated successfully "plugininfo.h"
         --> generate extern-plugininfo.h
         --> generated successfully "extern-plugininfo.h"
    \endcode
    
    This shows you how the \tt{plugininfo.h} and \tt{extern-plugininfo.h} will be generated. As you can see the UUID definitions and the logging category will be definend for the \b {Minimal} plugin because we have not changed yet \l{The Plugin JSON file}.

    The generated \tt {plugininfo.h} file will look like this:
    \code
        #ifndef PLUGININFO_H
        #define PLUGININFO_H
        #include "typeutils.h"
        #include <QLoggingCategory>

        // Id definitions
        PluginId pluginId = PluginId("6878754a-f27d-4007-a4e5-b030b55853f5");
        VendorId minimalVendorId = VendorId("3897e82e-7c48-4591-9a2f-0f56c55a96a4");
        DeviceClassId minimalDeviceClassId = DeviceClassId("7014e5f1-5b04-407a-a819-bbebd11fa372");

        // Loging category
        Q_DECLARE_LOGGING_CATEGORY(dcMinimal)
        Q_LOGGING_CATEGORY(dcMinimal, "Minimal")

        #endif // PLUGININFO_H    
    \endcode

    The generated \tt {extern-plugininfo.h} file will look like this:
    \code
        #ifndef EXTERNPLUGININFO_H
        #define EXTERNPLUGININFO_H
        #include "typeutils.h"
        #include <QLoggingCategory>

        // Id definitions
        extern VendorId minimalVendorId;
        extern DeviceClassId minimalDeviceClassId;

        // Logging category definition
        Q_DECLARE_LOGGING_CATEGORY(dcMinimal)

        #endif // EXTERNPLUGININFO_H    
    \endcode
    
    \section2 Change the \tt devicepluginnetworkinfo.json
    
    Before we can write our plugin JSON file we need to know which \l{State}{States}, \l{Action}{Actions} will be available. You can take a look at the \l{http://ip-api.com/} page. For the plugin we will need thouse information in a format which we can parse i.e. JSON \unicode{0x2192} \l{http://ip-api.com/json}.

    For more details about how to write the JSON file please take a look at \l{The Plugin JSON file} documentation.

    \note As you can see in this example the \l Vendor for this \l DevicePlugin is the \e guh. Of course you can define here a new Vendor (using \tt uuidgen to generate a new UUID). Please take a look at the existing \l{Vendor}{Vendors} and check if your \l Vendor already exists. If the \l{Vendor} exists, please copy the \e name, \e idName and \e id to make shore all \l{Device}{Devices} from one \l{Vendor} will be together in the system like in this example for \e guh. 

    Our new plugin will have the name \b {"Network Info"}, the corresponding logging categorie will be \tt dcNetworkInfo (defined from the \e {idName}). There will be one new \l{DeviceClass} with the \e name \b {Info about Network}. This \l{DeviceClass} has 6 \l{StateType}{StateTypes} and one \l{ActionType}.

    \code
        {
            "name": "Network Info",
            "idName": "NetworkInfo",
            "id": "c16852d7-f123-4dd5-983d-fc2eedb885aa",
            "vendors": [
                {
                    "name": "guh",
                    "idName": "guh",
                    "id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6",
                    "deviceClasses": [
                        {
                            "deviceClassId": "6c9d4852-cdfa-4eba-9ff2-c084d6f9d756",
                            "idName": "info",
                            "name": "Info about Network",
                            "createMethods": ["user"],
                            "paramTypes": [
                                {
                                    "name": "name",
                                    "type": "QString",
                                    "defaultValue": "Network Information"
                                }
                            ],
                            "stateTypes": [
                                {
                                    "name": "ip address",
                                    "id": "0b4751ca-f126-4369-bfc0-f745985ae59b",
                                    "idName": "address",
                                    "type": "QString",
                                    "defaultValue": "-"
                                },
                                {
                                    "name": "city",
                                    "id": "8c777cf7-1a54-4b80-a8fe-141ae2334a63",
                                    "idName": "city",
                                    "type": "QString",
                                    "defaultValue": "-"
                                },
                                {
                                    "name": "country",
                                    "id": "69a01d64-c68f-4175-85f3-69329fd66b52",
                                    "idName": "country",
                                    "type": "QString",
                                    "defaultValue": "-"
                                },
                                {
                                    "name": "time zone",
                                    "id": "ab5278ce-87e0-4a79-9d08-c989c50d62cb",
                                    "idName": "timeZone",
                                    "type": "QString",
                                    "defaultValue": "-"
                                },
                                {
                                    "name": "lon",
                                    "id": "5a3a54d3-afd4-464a-adba-23def0110ed7",
                                    "idName": "lon",
                                    "type": "double",
                                    "defaultValue": 0
                                },
                                {
                                    "name": "lat",
                                    "id": "f7b52b93-688d-47bb-83cc-85a694f33537",
                                    "idName": "lat",
                                    "type": "double",
                                    "defaultValue": 0
                                }
                            ],
                            "actionTypes": [
                                {
                                    "name": "update",
                                    "id": "0b4751ca-f126-4369-bfc0-f745985ae59b",
                                    "idName": "update"
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    \endcode

    Once you have changed \l{The Plugin JSON file} you should rebuild the whole project to make shore all changed will be considerated. In the \e {Qt Creator} got to the menu \unicode{0x2192} \b Build \unicode{0x2192} \b{Rebuild all} to create the new \tt plugininfo.h file. You should see in the build output something like this:

    \code
        /usr/bin/guh-generateplugininfo ../networkinfo-diy/devicepluginnetworkinfo.json plugininfo.h
        ../networkinfo-diy/devicepluginnetworkinfo.json -> plugininfo.h
         --> generate plugininfo.h
        PluginId for plugin "Network Info" = c16852d7-f123-4dd5-983d-fc2eedb885aa
        define VendorId NetworkInfoVendorId = 2062d64d-3232-433c-88bc-0d33c0ba2ba6
        define DeviceClassId infoDeviceClassId = 6c9d4852-cdfa-4eba-9ff2-c084d6f9d756
        define StateTypeId addressStateTypeId = 0b4751ca-f126-4369-bfc0-f745985ae59b
        define StateTypeId cityStateTypeId = 8c777cf7-1a54-4b80-a8fe-141ae2334a63
        define StateTypeId countryStateTypeId = 69a01d64-c68f-4175-85f3-69329fd66b52
        define StateTypeId timeZoneStateTypeId = ab5278ce-87e0-4a79-9d08-c989c50d62cb
        define StateTypeId lonStateTypeId = 5a3a54d3-afd4-464a-adba-23def0110ed7
        define StateTypeId latStateTypeId = f7b52b93-688d-47bb-83cc-85a694f33537
        define logging category: "dcNetworkInfo"
         --> generated successfully "plugininfo.h"
         --> generate extern-plugininfo.h
         --> generated successfully "extern-plugininfo.h"
    \endcode
    
    \note You have to change the \tt {qCDebug(dcMinimal)} \unicode{0x2192} \tt {qCDebug(dcNetworkInfo)} because you have changed the plugin \e idName and therefore also the logging categorie. You need to start guh now with the parameter \b {\tt {guhd -n -d NetworkInfo}} to see the debug output of the new plugin.

    If you make a syntax error in the JSON file, you will get a build error with the position of the syntax error in the JSON file. Now your definitions should be in the plugininfo.h file and ready to use in the plugin source code. 

    \section1 Writing the plugin
    
    Now we have our basic for starting to implement the new defined plugin. If you install the current plugin, start \tt guhd and add the a \b {Info about Network} device whith \b {\tt guh-cli} you can check the device states and should see something like this:

    \code
        ========================================================
        -> States of device "Info about Network" {83a1c0bb-c169-4292-a100-85af5fa9a1a4}:

                                 ip address: -
                                       city: -
                                    country: -
                                  time zone: -
                                        lon: 0
                                        lat: 0
        --------------------------------------------------------

    \endcode 
        
    All defined states are already availabe in the system and initialized with the \e defaultValue
parameter from \l{The Plugin JSON file}.    

    \section2 Define the required hardware resource
    Now we have to fetch the data from \l{http://ip-api.com/json} once the action \tt update will be executed. The first thing we have to define is the hardware resource. Since we are communicating with a REST API we need the \l{NetworkManager} hardware resource, which is basically a \l{http://doc.qt.io/qt-5/qnetworkaccessmanager.html}{QNetworkAccessManager} for all plugins.

    \code
        DeviceManager::HardwareResources DevicePluginNetworkInfo::requiredHardware() const
        {
            return DeviceManager::HardwareResourceNetworkManager;
        }
    \endcode
    
        
    \section2 Implement executeAction method
    The next verry important method we have to implement and override is the \l{DevicePlugin::executeAction()} method, which will be calle when the user wants to execute a certain \l{Action}. 

    \code
    DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
    \endcode
    
    The implementation looks like this:
    \code
        DeviceManager::DeviceError DevicePluginNetworkInfo::executeAction(Device *device, const Action &action)
        {
            // check if this device is a Network info device using the DeviceClassId
            if (device->deviceClassId() != infoDeviceClassId) {
                return DeviceManager::DeviceErrorDeviceClassNotFound;
            }

            // check if the requested action is our "update" action ...
            if (action.actionTypeId() == updateActionTypeId) {

                // Print information that we are executing now the update action
                qCDebug(dcNetworkInfo) << "Execute update action" << action.id();

                // Create a network request
                QNetworkRequest locationRequest(QUrl("http://ip-api.com/json"));

                // Call the GET method from the NetworkManager
                QNetworkReply *reply = networkManagerGet(locationRequest);

                // Hash the reply, because we dont get the result immediately
                m_asyncActionReplies.insert(reply, action.id());

                // Hash the device for this action
                m_asyncActions.insert(action.id(), device);

                // Tell the DeviceManager that this is an async action and the result of the execution will
                // be emitted later.
                return DeviceManager::DeviceErrorAsync;
            }

            // ...otherwise the ActionType does not exist
            return DeviceManager::DeviceErrorActionTypeNotFound;
        }
    \endcode
    
    \section2 Implement networkManagerReplyReady method
    Once the result of your pending network request is finished, the method \l{DevicePlugin::networkManagerReplyReady()} will be called, so we have to implement this method in our plugin header file and override the method:
    \code
    void networkManagerReplyReady(QNetworkReply *reply) override;
    \endcode

    The implementation looks like this:
    \code
        // This method will be called whenever the reply from a NetworkManager call is ready.
        void DevicePluginNetworkInfo::networkManagerReplyReady(QNetworkReply *reply)
        {
            // Make shore this is our reply
            if (!m_asyncActionReplies.keys().contains(reply))
                return;

            // This is one of our action replies!!

            // Take the corresponding action from our hash
            ActionId actionId = m_asyncActionReplies.take(reply);

            // Check the status code of the reply
            if (reply->error()) {

                // Print the warning message
                qCWarning(dcNetworkInfo) << "Reply error" << reply->errorString();

                // The action execution is finished, and was not successfully
                emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareNotAvailable);

                // Important -> delete the reply to prevent a memory leak!
                reply->deleteLater();
                return;
            }

            // The request was successfull, lets read the payload
            QByteArray data = reply->readAll();

            // Important -> delete the reply to prevent a memory leak!
            reply->deleteLater();

            // Process the data from the reply
            actionDataReady(actionId, data);
        }
    \endcode
    
    \section2 Update the state values

    Once the reply was read successfully we have to read the json document and set our state values to the fetched values. For this we implement a private method called:

    \code
        void actionDataReady(const ActionId &actionId, const QByteArray &data);
    \endcode

    First we have to check if the received data is a vaild JSON document. If not, the action execution \b "update" was not successfull and we have to report the error. Otherwise we read the data and set the state values of our device.

    \code
        void DevicePluginNetworkInfo::actionDataReady(const ActionId &actionId, const QByteArray &data)
        {
            // Convert the rawdata to a json document
            QJsonParseError error;
            QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);

            // Check if we got a valid JSON document
            if(error.error != QJsonParseError::NoError) {
                qCWarning(dcNetworkInfo) << "Failed to parse JSON data" << data << ":" << error.errorString();

                // the action execution is finished, and was not successfully
                emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareFailure);
                return;
            }

            // print the fetched data in json format to stdout
            qCDebug(dcNetworkInfo) << jsonDoc.toJson();

            // Get the device for this action
            Device *device = m_asyncActions.take(actionId);

            // Parse the data and update the states of our device
            QVariantMap dataMap = jsonDoc.toVariant().toMap();

            // Set the city state
            if (dataMap.contains("city")) {
                device->setStateValue(cityStateTypeId, dataMap.value("city").toString());
            }

            // Set the country state
            if (dataMap.contains("countryCode")) {
                device->setStateValue(countryStateTypeId, dataMap.value("countryCode").toString());
            }

            // Set the wan ip
            if (dataMap.contains("query")) {
                device->setStateValue(addressStateTypeId, dataMap.value("query").toString());
            }

            // Set the time zone state
            if (dataMap.contains("timezone")) {
                device->setStateValue(timeZoneStateTypeId, dataMap.value("timezone").toString());
            }

            // Set the longitude state
            if (dataMap.contains("lon")) {
                device->setStateValue(lonStateTypeId, dataMap.value("lon").toDouble());
            }

            // Set the latitude state
            if (dataMap.contains("lat")) {
                device->setStateValue(latStateTypeId, dataMap.value("lat").toDouble());
            }

            qCDebug(dcNetworkInfo) << "Action" << actionId << "execution finished successfully.";

            // Emit the successfull action execution result to the device manager
            emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError);
        }
    \endcode
    
    You can find the full example in the \tt plugin-templates \unicode{0x2192} \tt networkinfo folder.    

    \section1 Test the plugin
    
    Rebuild the whole project to make shore all changes are registered and install the plugin (see \l{Install the plugin}{Tutorial 1 - Install the plugin}).

    \list 1    
        \li Start guh with following command:

            \code
                $ guhd -n -d NetworkInfo
            \endcode

        \li Start guh-cli and add the a new "Info" devcice.
        \li Use guh-cli to check if the device states are initialized with the default values from \l{Change the devicepluginnetworkinfo.json}: 

            \tt "Devices" \unicode{0x2192} \tt "List..." \unicode{0x2192} \tt {"List device states"} \unicode{0x2192} \tt {"Your device name"}.
        \li Use guh-cli to execute the \b update action: 

            \tt "Devices" \unicode{0x2192} \tt "Execute action" \unicode{0x2192} \tt {"Your device name"} \unicode{0x2192} \tt {update}
        \li Use guh-cli to check if the device states were updated sucessfully: 

            \tt "Devices" \unicode{0x2192} \tt "List..." \unicode{0x2192} \tt {"List device states"} \unicode{0x2192} \tt {"Your device name"}.

    \endlist
*/ 


