#ifndef TDE_WIDGETS_ENGINEWORKER_H
#define TDE_WIDGETS_ENGINEWORKER_H

#include <QWidget>

#include "common.h"
#include "opaque.h"

// -- TDE NAMESPACE ----------------------------------------------------

namespace TDE {

// -- Widgets NAMESPACE ------------------------------------------------

namespace Widgets {

const long OBSERVERS_UPDATE_PERIOD = 200;
const long REMOTES_UPDATE_PERIOD = 50;


// http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/

/*!
 * \class EngineObserversWorker
 * \brief Worker thread used to notify observers of changes in DataStore variable values.
 *
 * **WARNING**: This is an advanced topic.
 *
 * Runs DataStore::updateObservers() method with a period of
 * \ref OBSERVERS_UPDATE_PERIOD ms.
 *
 * \see DataStore
 * \see EngineRunner
 */
class EngineObserversWorker : public QObject
{
	Q_OBJECT

public:
	EngineObserversWorker(SHARED_PTR<TDE::DataStore> ds_p);
	~EngineObserversWorker();

public slots:
	void process();
	void terminate();

signals:
	void finished();
	void error(QString err);

private:
	// add your variables here
	SHARED_PTR<TDE::DataStore> m_ds_p;
};

/*!
 * \class EngineRemotesWorker
 * \brief Worker thread used to poll remote Modbus TCP nodes.
 *
 * **WARNING**: This is an advanced topic.
 *
 * Runs a DataStore::updateRemotesBlocks() method with a period of
 * \ref REMOTES_UPDATE_PERIOD ms.
 *
 * \see DataStore
 * \see DataStoreModbusServer
 * \see EngineRunner
 */
class EngineRemotesWorker : public QObject
{
	Q_OBJECT

public:
	EngineRemotesWorker(SHARED_PTR<TDE::DataStore> ds_p);
	~EngineRemotesWorker();

public slots:
	void process();
	void terminate();

signals:
	void finished();
	void error(QString err);

private:
	// add your variables here
	SHARED_PTR<TDE::DataStore> m_ds_p;
};

/*!
 * \class EngineModbusServerWorker
 * \brief Worker thread used to run the Modbus TCP server (client).
 *
 * **WARNING**: This is an advanced topic.
 *
 * Runs a DataStoreModbusServer().
 *
 * \see DataStore
 * \see DataStoreModbusServer
 * \see EngineRunner
 */
class EngineModbusServerWorker : public QObject
{
	Q_OBJECT

public:
	EngineModbusServerWorker(SHARED_PTR<TDE::DataStore> ds_p);
	~EngineModbusServerWorker();

public slots:
	void process();
	void terminate();

signals:
	void finished();
	void error(QString err);

private:
	// add your variables here
	SHARED_PTR<TDE::DataStore> m_ds_p;
	DataStoreModbusServer* m_modbus_server;
};

/*!
 * \class EngineRunner
 * \brief Class responsible for running all engine threads.
 *
 * **WARNING**: This is an advanced topic.
 *
 * \see EngineObserversWorker
 * \see EngineRemotesWorker
 * \see EngineModbusServerWorker
 */
class EngineRunner : public QObject
{
	Q_OBJECT

public:
	EngineRunner(SHARED_PTR<TDE::DataStore> ds_p);
	~EngineRunner();

	bool isObserversWorkerRunning() const		{ return m_observers_started; }
	bool isRemotesWorkerRunning() const			{ return m_remotes_started; }
	bool isModbusServerWorkerRunning() const	{ return m_modbus_server_started; }
	bool isRunning() const						{ return isObserversWorkerRunning() && isRemotesWorkerRunning() && isModbusServerWorkerRunning(); }
	bool launch();

public slots:
	void errorString(const QString& error);

private:
	bool _launchObserversWorker();
	bool _launchRemotesWorker();
	bool _launchModbusServerWorker();

	SHARED_PTR<TDE::DataStore> m_ds_p;

	QThread*               m_observers_thread;
	EngineObserversWorker* m_observers_worker;

	QThread*               m_remotes_thread;
	EngineRemotesWorker*   m_remotes_worker;

	QThread*                  m_modbus_server_thread;
	EngineModbusServerWorker* m_modbus_server_worker;

	bool m_observers_started;
	bool m_remotes_started;
	bool m_modbus_server_started;
};

/*!
 * \class EngineRunnerProvider
 * \brief Monoid factory for constucting the EngineRunner.
 *
 * **WARNING**: This is an advanced topic.
 *
 * \see Andrei Alexandrescu, "Modern C++ Design"
 */
class EngineRunnerProvider
{
public:
	static SHARED_PTR<EngineRunner> Instance();
	static SHARED_PTR<EngineRunner> Instance(SHARED_PTR<TDE::DataStore> ds_p);

	static bool IsRunning();
	static bool Launch(SHARED_PTR<TDE::DataStore> ds_p);

private:
	static void CleanUp();
	static SHARED_PTR<TDE::DataStore> DataStore(SHARED_PTR<TDE::DataStore> ds_p);

	static SHARED_PTR<EngineRunner> m_instance;
};

} /* end of namespace Widgets */

} /* end of namespace TDE */

#endif /* TDE_WIDGETS_ENGINEWORKER_H */
