/**
 ******************************************************************************
 *
 * @file       telemetry.h
 * @author     The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
 * @addtogroup GCSPlugins GCS Plugins
 * @{
 * @addtogroup UAVTalkPlugin UAVTalk Plugin
 * @{
 * @brief The UAVTalk protocol plugin
 *****************************************************************************/
/*
 * This program 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.
 * 
 * This program 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 this program; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef TELEMETRY_H
#define TELEMETRY_H

#include "uavtalk.h"
#include "uavobjects/uavobjectmanager.h"
#include "uavobjects/gcstelemetrystats.h"
#include <QMutex>
#include <QMutexLocker>
#include <QTimer>
#include <QQueue>

class Telemetry: public QObject
{
    Q_OBJECT

public:
    typedef struct {
        quint32 txBytes;
        quint32 rxBytes;
        quint32 txObjectBytes;
        quint32 rxObjectBytes;
        quint32 rxObjects;
        quint32 txObjects;
        quint32 txErrors;
        quint32 rxErrors;
        quint32 txRetries;
    } TelemetryStats;

    Telemetry(UAVTalk* utalk, UAVObjectManager* objMngr);
    TelemetryStats getStats();
    void resetStats();


signals:

private slots:
    void objectUpdatedAuto(UAVObject* obj);
    void objectUpdatedManual(UAVObject* obj);
    void objectUnpacked(UAVObject* obj);
    void updateRequested(UAVObject* obj);
    void newObject(UAVObject* obj);
    void newInstance(UAVObject* obj);
    void processPeriodicUpdates();
    void transactionCompleted(UAVObject* obj);
    void transactionTimeout();

private:
    // Constants
    static const int REQ_TIMEOUT_MS = 250;
    static const int MAX_RETRIES = 2;
    static const int MAX_UPDATE_PERIOD_MS = 1000;
    static const int MIN_UPDATE_PERIOD_MS = 1;
    static const int MAX_QUEUE_SIZE = 20;

    // Types
    /**
     * Events generated by objects
     */
    typedef enum {
        EV_UNPACKED = 0x01, /** Object data updated by unpacking */
        EV_UPDATED = 0x02, /** Object data updated by changing the data structure */
        EV_UPDATED_MANUAL = 0x04, /** Object update event manually generated */
        EV_UPDATE_REQ = 0x08 /** Request to update object data */
    } EventMask;

    typedef struct {
        UAVObject* obj;
        qint32 updatePeriodMs; /** Update period in ms or 0 if no periodic updates are needed */
        qint32 timeToNextUpdateMs; /** Time delay to the next update */
    } ObjectTimeInfo;

    typedef struct {
        UAVObject* obj;
        EventMask event;
        bool allInstances;
    } ObjectQueueInfo;

    typedef struct {
        UAVObject* obj;
        bool allInstances;
        bool objRequest;
        qint32 retriesRemaining;
        bool acked;
    } ObjectTransactionInfo;

    // Variables
    UAVObjectManager* objMngr;
    UAVTalk* utalk;
    GCSTelemetryStats* gcsStatsObj;
    QList<ObjectTimeInfo> objList;
    QQueue<ObjectQueueInfo> objQueue;
    QQueue<ObjectQueueInfo> objPriorityQueue;
    ObjectTransactionInfo transInfo;
    bool transPending;
    QMutex* mutex;
    QTimer* updateTimer;
    QTimer* transTimer;
    QTimer* statsTimer;
    qint32 timeToNextUpdateMs;
    quint32 txErrors;
    quint32 txRetries;

    // Methods
    void registerObject(UAVObject* obj);
    void addObject(UAVObject* obj);
    void setUpdatePeriod(UAVObject* obj, qint32 periodMs);
    void connectToObjectInstances(UAVObject* obj, quint32 eventMask);
    void updateObject(UAVObject* obj);
    void processObjectUpdates(UAVObject* obj, EventMask event, bool allInstances, bool priority);
    void processObjectTransaction();
    void processObjectQueue();


};

#endif // TELEMETRY_H
