#include "stdout_watcher.h"
#include <stdio.h>
#include <thread>
#ifdef Q_OS_LINUX
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
#ifdef Q_OS_WINDOWS
#include  <io.h>
#include <fcntl.h>
#endif

stdout_watcher::stdout_watcher(QObject *parent) : QThread(parent),
	m_bStop(false)
{

}
void stdout_watcher::stop_and_wait()
{
	m_bStop = true;
	fprintf (stdout,"Bye!\n");
	fprintf (stderr,"Bye!\n");
	for (int i=0;i<10;++i)
		wait(200);
	this->terminate();
}

#ifdef Q_OS_WINDOWS
#define OUT_BUFF_SIZE 1024


void stdout_watcher::run()
{
	auto pt = [&](FILE * stda,bool stderrb)->void{
		int fdStdOutPipe[2];
		int fdStdOut;
		if (_pipe(fdStdOutPipe, OUT_BUFF_SIZE*1024, O_TEXT)!=0)
		{
			return ;
		}
		fdStdOut = _dup(_fileno(stda));
		fflush( stda );
		_dup2(fdStdOutPipe[1], _fileno(stda));
		ios::sync_with_stdio();
		setvbuf( stda, NULL, _IONBF, 0 ); // absolutely needed
		QByteArray arrbuf;
		while(!m_bStop)
		{
			char buffer[OUT_BUFF_SIZE*2];
			int nOutRead = _read(fdStdOutPipe[0], (void *)buffer,(size_t) OUT_BUFF_SIZE);
			buffer[nOutRead] = '\0';
			if (nOutRead >0 )
			{
				arrbuf.append(buffer,nOutRead);
			}
			int cp = 0;
			int sz = arrbuf.size();
			const char * p = arrbuf.constData();

			for (int i=0;i<sz;++i)
			{
				if (p[i]=='\n')
				{
					QByteArray arrEmit = arrbuf.left(i);
					emit evt_stdata(arrEmit,stderrb);
					arrbuf = arrbuf.mid(i+1);
					break;
				}
			}
		}
		_dup2(fdStdOut, _fileno(stda));
		ios::sync_with_stdio();

		_close(fdStdOut);
		_close(fdStdOutPipe[0]);
		_close(fdStdOutPipe[1]);


	};
	std::thread t1(pt, stderr,true);
	std::thread t2(pt, stdout,false);

	t1.join();
	t2.join();
}
#endif


#ifdef Q_OS_LINUX

void stdout_watcher::run()
{
	auto pt = [&](FILE * stda,bool stderrb)->void{
		int  stdout_bk; //is fd for stdout backup
		stdout_bk = dup(fileno(stda));
		int pfd_stdout[2];
		pipe2(pfd_stdout,O_NONBLOCK); // O_NONBLOCK);

		// What used to be stdout will now go to the pipe.
		dup2(pfd_stdout[1], fileno(stda));
		while (!m_bStop)
		{
			char buf[1024];
			int red = 0;
			if ((red = read(pfd_stdout[0], buf, 1024)) >0 )
				emit evt_stdata(QByteArray(buf,red),stderrb);
			else
				msleep(10);
		}
		close(pfd_stdout[1]);
		close(pfd_stdout[0]);
		dup2(stdout_bk, fileno(stda));//restore
	};
	std::thread t1(pt, stderr,true);
	std::thread t2(pt, stdout,false);

	t1.join();
	t2.join();
}

#endif
