/*****************************************************************************
 *
 * Testmanager - Graphical Automation and Visualisation Tool
 *
 * Copyright (C) 2018  Florian Pose <fp@igh.de>
 *
 * This file is part of Testmanager.
 *
 * Testmanager is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * Testmanager is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with Testmanager. If not, see <http://www.gnu.org/licenses/>.
 *
 ****************************************************************************/

#ifndef DATA_SOURCE_H
#define DATA_SOURCE_H

/****************************************************************************/

#include "DataNode.h"

#include <pdcom5.h>
#include <QtPdCom1/Process.h>
#include <QtPdCom1/VariableList.h>

#ifndef PDCOM_VERSION_CODE
# error "No PDCOM_VERSION_CODE found."
#elif \
    !PDCOM_DEVEL \
    && (PDCOM_VERSION_CODE < PDCOM_VERSION(5, 0, 0) \
    || PDCOM_VERSION_CODE >= PDCOM_VERSION(6, 0, 0))
# error "Invalid PdCom version."
#endif

#include <QtPdCom1/MessageModel.h>

#include <QTcpSocket>
#include <QUrl>
#include <QDockWidget>
#include <memory>

/****************************************************************************/

namespace QtPdCom {
    class LoginManager;
}
class DataModel;

/****************************************************************************/

class DataSource:
    public DataNode
{
    Q_OBJECT

    public:
        DataSource(DataModel *, const QUrl &url, const QUrl & = QUrl());
        virtual ~DataSource();

        bool isConnected() const;
        void connectToHost();
        void disconnectFromHost();

        void setUrl(const QUrl &);
        const QUrl &getUrl() const {
            return url;
        }
        const QUrl &getConnectUrl() const {
            return connectUrl;
        }

        QString errorString() const;

        DataNode *findDataNode(const QUrl &);

        QtPdCom::LoginManager* getLoginManager() const {
            return loginManager;
        }

        void logout();

        void filter(const QRegExp &);

        // DataNode
        QVariant nodeData(int, int);
        void nodeFlags(Qt::ItemFlags &, int) const;

        void updateStats();
        int getInRate() const { return inRate; }
        int getOutRate() const { return outRate; }

        const QString &getMessagePath() const { return messagePath; }
        void setMessagePath(const QString &);
        bool hasMessages() const;
        void connectMessages(QObject *);
        void showMessages(QMainWindow *);

        void read(const QJsonObject &);
        void write(QJsonObject &) const;

        void callPendingCallbacks();

        QtPdCom::Process& getProcess() const { return *process; }
        bool lastLoginSuccessful() const { return lastLoginSuccessful_; }

    signals:
        void variableTreeBuilt();
        void disconnected();
        void error();

    private slots:
        void socketError();
        void socketDisconnected();
        void connectTimeout();
        void onListReplyReceived(QtPdCom::VariableList);

    private:
        std::unique_ptr<QtPdCom::Process> const process;
        QUrl url; // saved URL
        QUrl connectUrl; // connected and displayed
        QString name;
        bool connectionDesired;
        QTimer connectTimer;

        int bytesOut;
        int bytesIn;
        int outRate;
        int inRate;

        QString messagePath;
        QtPdCom::MessageModel messageModel;
        QDockWidget *messageDockWidget;
        QtPdCom::LoginManager *loginManager;

        bool lastLoginSuccessful_ = false;

        void appendVariable(PdCom::Variable const&);

        // PdCom::Process
        void reset();

        DataSource();

        QtPdCom::LoginManager* createLoginManager(const QUrl& url);

    public:
        // this is a) for login() on an already connected DataSource
        // or b) for an unconnected DataSource in case login is mandatory
        QMetaObject::Connection credentialsNeededAction;
};

/****************************************************************************/

#endif
