/*! \file pipipelinethread.h
 * \brief Class for create multihread pipeline
*/
/*
	PIP - Platform Independent Primitives
	Class for create multihread pipeline
	Copyright (C) 2016 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 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, see <http://www.gnu.org/licenses/>.
*/

#ifndef PIPIPELINETHREAD_H
#define PIPIPELINETHREAD_H

#include "pithread.h"

template <typename T1, typename T2>
class PIPipelineThread : public PIThread
{
	PIOBJECT_SUBCLASS(PIPipelineThread, PIThread)
public:
	PIPipelineThread() {
		cnt = 0;
		max_size = 0;
		wait_next_pipe = true;
		next_overload = false;
	}
	~PIPipelineThread() {
		stop();
		if (!waitForFinish(1000)) terminate();
	}
	template <typename T3>
	void connectTo(PIPipelineThread<T2, T3> * next) {
		CONNECT2(void, T2, bool *, this, calculated, next, enqueue)
	}
	EVENT2(calculated, const T2 &, v, bool *, overload)
	EVENT_HANDLER1(void, enqueue, const T1 &, v) {enqueue(v, 0);}
	EVENT_HANDLER2(void, enqueue, const T1 &, v, bool *, overload) {
		mutex.lock();
		if (max_size > 0 && in.size() < max_size) {
			in.enqueue(v);
			if (overload) *overload = false;
		} else {
			if (overload) *overload = true;
		}
		mutex.unlock();
	}
	const ullong * counterPtr() const {return &cnt;}
	ullong counter() const {return cnt;}
	bool isEmpty() {
		bool ret;
		mutex.lock();
		ret = in.isEmpty();
		mutex.unlock();
		return ret;
	}
	int queSize() {
		int ret;
		mutex.lock();
		ret = in.size();
		mutex.unlock();
		return ret;
	}
	void clear() {
		mutex.lock();
		in.clear();
		next_overload = false;
		mutex.unlock();
	}
	void stopCalc() {
		if (isRunning()) {
			stop();
			if (!waitForFinish(100)) terminate();
			mutex.unlock();
			mutex_l.unlock();
		}
	}
	T2 getLast() {
		T2 ret;
		mutex_l.lock();
		ret = last;
		mutex_l.unlock();
		return ret;
	}
	
	uint maxQueSize() {
		uint ret;
		mutex.lock();
		ret = max_size;
		mutex.unlock();
		return ret;
	}
	
	void setMaxQueSize(uint count) {
		mutex.lock();
		count = max_size;
		if (max_size > 0 && in.size() > max_size) in.resize(max_size);
		mutex.unlock();
	}
	
	bool isWaitNextPipe() {return wait_next_pipe;}
	void setWaitNextPipe(bool wait) {wait_next_pipe = wait;}

protected:
	virtual T2 calc(const T1 &v, bool &ok) = 0;

private:
	void begin() {cnt = 0;}
	void run() {
		mutex.lock();
		if (in.isEmpty()) {
			mutex.unlock();
			piMSleep(1);
			return;
		}
		if (next_overload) {
			calculated(last, &next_overload);
		} else {
			T1 t = in.dequeue();
			bool ok = true;
			mutex.unlock();
			T2 r = calc(t, ok);
			mutex_l.lock();
			last = r;
			mutex_l.unlock();
			cnt++;
			if (ok) calculated(r, &next_overload);
		}
	}
	PIQueue<T1> in;
	T2 last;
	PIMutex mutex;
	PIMutex mutex_l;
	ullong cnt;
	uint max_size;
	bool wait_next_pipe;
	bool next_overload;
};

#endif // PIPIPELINETHREAD_H
