#include"qpipe.h"

#ifdef Q_WS_WIN

static uint readFromHandle(HANDLE h, char *buf, uint size)
{
	if(size > 0) {
		ulong r;
		if(ReadFile(h, buf, size, &r, 0))
			return r;
	}
	return 0;
}

#else

#include<unistd.h>
#include<fcntl.h>
#include<errno.h>

#endif

//----------------------------------------------------------------------------
// QPipeEnd
//----------------------------------------------------------------------------
QPipeEnd::QPipeEnd()
{
	reset();
}

QPipeEnd::~QPipeEnd()
{
	close();
}

void QPipeEnd::reset()
{
#ifdef Q_WS_WIN
	p = 0;
#else
	p = -1;
#endif
}

bool QPipeEnd::isOpen() const
{
#ifdef Q_WS_WIN
	return (p ? true: false);
#else
	return (p != -1 ? true: false);
#endif
}

const QPipeId & QPipeEnd::id() const
{
	return p;
}

void QPipeEnd::setId(const QPipeId &x)
{
	p = x;
}

void QPipeEnd::close()
{
	if(!isOpen())
		return;
#ifdef Q_WS_WIN
	CloseHandle(p);
#else
	::close(p);
#endif
	reset();
}

void QPipeEnd::release()
{
	reset();
}

QString QPipeEnd::toString() const
{
#ifdef Q_WS_WIN
	DWORD dw;
	memcpy(&dw, &p, sizeof(DWORD));
	return QString::number(dw);
#else
	return QString::number(p);
#endif
}

int QPipeEnd::write(const QByteArray &buf)
{
#ifdef Q_WS_WIN
	DWORD written;
	if(!WriteFile(p, buf.data(), buf.size(), &written, 0))
		return -1;
	return written;
#else
	return (::write(p, buf.data(), buf.size()));
#endif
}

QByteArray QPipeEnd::readAll(bool *done)
{
#ifdef Q_WS_WIN
	if(done)
		*done = false;
	unsigned long i, r;
	char dummy;
	if(!PeekNamedPipe(p, &dummy, 1, &r, &i, 0))
		return QByteArray();
	if(i <= 0)
		return QByteArray();

	QByteArray buf;
	buf.resize(i);
	int size = readFromHandle(p, buf.data(), i);
	if(size == 0)
		return QByteArray();
	return buf;
#else
	if(done)
		*done = false;
	QByteArray buf;
	while(1) {
		char block[1024];
		int n = read(p, block, 1024);
		if(n < 0) {
			if(errno == EAGAIN) {
				break;
			}
			else {
				if(done)
					*done = true;
				break;
			}
		}
		else if(n == 0) {
			if(done)
				*done = true;
			break;
		}

		int oldsize = buf.size();
		buf.resize(oldsize + n);
		memcpy(buf.data() + oldsize, block, n);
	}

	return buf;
#endif
}

#ifdef Q_WS_WIN
bool QPipeEnd::winDupHandle()
{
	HANDLE h;
	if(!DuplicateHandle(GetCurrentProcess(), p, GetCurrentProcess(), &h, 0, FALSE, DUPLICATE_SAME_ACCESS))
		return false;
	CloseHandle(p);
	p = h;
	return true;
}

#else

bool QPipeEnd::setBlock(bool b)
{
	int flags = fcntl(p, F_GETFL);
	if(!b)
		flags |= O_NONBLOCK;
	else
		flags &= ~O_NONBLOCK;
	if(fcntl(p, F_SETFL, flags) == -1)
		return false;
	return true;
}
#endif


//----------------------------------------------------------------------------
// QPipe
//----------------------------------------------------------------------------
QPipe::QPipe()
{
}

QPipe::~QPipe()
{
	close();
}

void QPipe::closeReadEnd()
{
	i.close();
}

void QPipe::closeWriteEnd()
{
	o.close();
}

int QPipe::write(const QByteArray &buf)
{
	return o.write(buf);
}

QByteArray QPipe::readAll(bool *done)
{
	return i.readAll(done);
}

bool QPipe::open()
{
	close();

#ifdef Q_WS_WIN
	SECURITY_ATTRIBUTES secAttr;
	memset(&secAttr, 0, sizeof secAttr);
	secAttr.nLength = sizeof secAttr;
	secAttr.bInheritHandle = TRUE;

	HANDLE r, w;
	if(!CreatePipe(&r, &w, &secAttr, 0))
		return false;
	i.setId(r);
	o.setId(w);
#else
	int p[2];
	if(pipe(p) == -1)
		return false;
	i.setId(p[0]);
	o.setId(p[1]);
#endif
	return true;
}

void QPipe::close()
{
	i.close();
	o.close();
}

void QPipe::release()
{
	i.release();
	o.release();
}
