/*! \file pigrabberbase.h
 * \ingroup Thread
 * \~\brief
 * \~english Abstract class for create grabbers
 * \~russian Базовый класс для создания грабберов
 */
/*
	PIP - Platform Independent Primitives
	Abstract class for create grabbers
	Andrey Bychkov work.a.b@yandex.ru

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser 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 Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PIGRABBERBASE_H
#define PIGRABBERBASE_H

#include "pithread.h"
#include "pidiagnostics.h"


template<typename T = PIByteArray>
class PIGrabberBase: public PIThread
{
	PIOBJECT_SUBCLASS(PIGrabberBase, PIThread);
public:
	PIGrabberBase() {
		is_opened = false;
		is_recording = false;
	}
	virtual ~PIGrabberBase() {
		stopGrabber(false);
	}
	virtual bool isOpened() const {return is_opened;}
	virtual bool isRecording() const {return is_recording;}
	virtual void startRecord(const PIString & filename) {
		if (!isOpened()) return;
		if (isRecording()) return;
		rec_mutex.lock();
		startRecordInternal(filename);
		is_recording = true;
		rec_mutex.unlock();
	}
	virtual void stopRecord() {
		if (!isOpened()) return;
		if (!isRecording()) return;
		rec_mutex.lock();
		is_recording = false;
		stopRecordInternal();
		rec_mutex.unlock();
	}
	T last() const {
		T ret;
		last_mutex.lock();
		ret = last_;
		last_mutex.unlock();
		return ret;
	}
	bool isEmpty() {
		bool ret;
		que_mutex.lock();
		ret = que.isEmpty();
		que_mutex.unlock();
		return ret;
	}
	int queSize() {
		int ret;
		que_mutex.lock();
		ret = que.size();
		que_mutex.unlock();
		return ret;
	}
	T dequeue() {
		T ret;
//		piCoutObj << "start";
		que_mutex.lock();
		if (!que.isEmpty()) {
//			piCoutObj << "dequeue";
			ret = que.dequeue();
		}
//		piCoutObj << "end";
		que_mutex.unlock();
		return ret;
	}
	void stopGrabber(bool wait_forever = true) {
		if (isRunning()) {
			stop();
			if (wait_forever) waitForFinish();
			else {
				if (!waitForFinish(100)) terminate();
				stopRecord();
				close();
			}
		}
	}
	bool open() {
		bool ret = openInternal();
		if (!is_opened && ret)
			opened();
		is_opened = ret;
		return ret;
	}
	void close() {
		bool em = is_opened;
		closeInternal();
		last_ = T();
		if (em)
			closed();
		is_opened = false;
	}
	const PIDiagnostics & diag() const {return diag_;}
	void clear() {
		que_mutex.lock();
		que.clear();
		que_mutex.unlock();
	}
	void restart() {
		clear();
		close();
	}

	EVENT(dataReady);
	EVENT(opened);
	EVENT(closed);

protected:
	virtual void init() {}
	virtual bool openInternal() = 0;
	virtual void closeInternal() = 0;
	virtual int get(T & val) = 0;
	virtual void record(const T & val) {}
	virtual void startRecordInternal(const PIString & filename) {}
	virtual void stopRecordInternal() {}

	bool is_opened, is_recording;
	mutable PIMutex rec_mutex;

private:
	void begin() override {
		init();
	}
	void run() override {
		if (!isOpened()) {
			open();
			diag_.reset();
			if (!is_opened)
				piMSleep(200);
		}
		if (isOpened()) {
			T c;
			int ret = get(c);
			if (ret < 0) {
				close();
				return;
			}
			if (ret > 0) {
				piMinSleep();
				return;
			}
			diag_.received(1);
			que_mutex.lock();
			que.enqueue(c);
			que_mutex.unlock();
			if (isRecording()) {
				rec_mutex.lock();
				record(c);
				rec_mutex.unlock();
				diag_.sended(1);
			}
			last_mutex.lock();
			last_ = c;
			last_mutex.unlock();
			dataReady();
		}
	}

	void end() override {
		stopRecord();
		close();
	}

	T last_;
	PIQueue<T> que;
	PIDiagnostics diag_;
	mutable PIMutex que_mutex, last_mutex;
};

#endif // PIGRABBERBASE_H
