#include "clindexerprotocol.h"
#include <memory>
#include <stdio.h>

#define ACK_MAGIC 1975

class CharDeleter {
	char *m_ptr;
public:
	CharDeleter(char *p) : m_ptr(p) {}
	~CharDeleter() {
		if(m_ptr) {
			delete [] m_ptr;
			m_ptr = NULL;
		}
	}
};

clIndexerProtocol::clIndexerProtocol()
{
}

clIndexerProtocol::~clIndexerProtocol()
{
}

bool clIndexerProtocol::ReadReply(clNamedPipe* conn, clIndexerReply& reply)
{
	// first we read sizeof(size_t) to get the actual data size
	size_t buff_len(0);
	size_t actual_read(0);

	if ( !conn->read((void*)&buff_len, sizeof(buff_len), &actual_read, 10000) ) {
		fprintf(stderr, "ERROR: ReadReply: Failed to read from the pipe, reason: %d\n", conn->getLastError());
		return false;
	}

	if (actual_read != sizeof(buff_len)) {
		fprintf(stderr, "ERROR: ReadReply: Protocol error: expected %u bytes, got %u. reason: %d\n",
				sizeof(buff_len),
				actual_read,
				conn->getLastError());
		return false;
	}

	if ((buff_len / (1024*1024)) > 15) {
		// Dont read buffers larger than 15MB...
		return false;
	}

	char *data = new char[buff_len];
	CharDeleter deleter(data);

	int bytes_left(buff_len);
	size_t bytes_read(0);
	while (bytes_left > 0) {
		if ( !conn->read(data+bytes_read, bytes_left, &actual_read, 10000) ) {
			fprintf(stderr, "ERROR: Protocol error: expected %u bytes, got %u\n", buff_len, actual_read);
			return false;
		}
		bytes_left -= actual_read;
		bytes_read += actual_read;
	}

	reply.fromBinary(data);
	return true;
}

bool clIndexerProtocol::ReadRequest(clNamedPipe* conn, clIndexerRequest& req)
{
	// first we read sizeof(size_t) to get the actual data size
	size_t buff_len(0);
	size_t actual_read(0);

	if ( !conn->read((void*)&buff_len, sizeof(buff_len), &actual_read, -1) ) {
		fprintf(stderr, "ERROR: Failed to read from the pipe, reason: %d\n", conn->getLastError());
		return false;
	}

	if (actual_read != sizeof(buff_len)) {
		fprintf(stderr, "ERROR: Protocol error: expected %u bytes, got %u\n", sizeof(buff_len), actual_read);
		return false;
	}

	if(buff_len == 0)
		return false;

	char *data = new char[buff_len];
	CharDeleter deleter(data);

	int bytes_left(buff_len);
	size_t bytes_read(0);
	while (bytes_left > 0) {
		if ( !conn->read(data+bytes_read, bytes_left, &actual_read, -1) ) {
			fprintf(stderr, "ERROR: [%s] Protocol error: expected %u bytes, got %u\n", __PRETTY_FUNCTION__, buff_len, actual_read);
			return false;
		}
		bytes_left -= actual_read;
		bytes_read += actual_read;
	}

	req.fromBinary(data);
	return true;
}

bool clIndexerProtocol::SendReply(clNamedPipe* conn, clIndexerReply& reply)
{
	size_t buff_size(0);
	char *data = reply.toBinary(buff_size);
	CharDeleter deleter(data);

	// send the reply size
	size_t written(0);
	conn->write((void*)&buff_size, sizeof(buff_size), &written, -1);


	int bytes_left(buff_size);
	int bytes_to_write(0);
	int bytes_written(0);

	while (bytes_left > 0) {
		// we write in chunks of 3000 bytes
		if (bytes_left < 3000) {
			bytes_to_write = bytes_left;
		} else {
			bytes_to_write = 3000;
		}

		size_t actual_written(0);
		if ( !conn->write(data+bytes_written, bytes_to_write, &actual_written, -1) ) {
			return false;
		}

		bytes_left -= actual_written;
		bytes_written += actual_written;
	}

	// the above problem does not exist under Windows' NamedPipes
	return true;
}

bool clIndexerProtocol::SendRequest(clNamedPipe* conn, clIndexerRequest& req)
{
	size_t size(0);
	size_t written(0);

	char *data = req.toBinary(size);
	CharDeleter deleter(data);

	// write request
	if (!conn->write((void*)&size, sizeof(size), &written, -1)) {
		printf("ERROR: [%s] protocol error: rc %d\n", __PRETTY_FUNCTION__, conn->getLastError());
		return false;
	}

	int bytes_left(size);
	int bytes_to_write(0);
	int bytes_written(0);

	while (bytes_left > 0) {
		// we write in chunks of 3000 bytes
		if (bytes_left < 3000) {
			bytes_to_write = bytes_left;
		} else {
			bytes_to_write = 3000;
		}

		size_t actual_written(0);
		if ( !conn->write(data+bytes_written, bytes_to_write, &actual_written, -1) ) {
			return false;
		}

		bytes_left -= actual_written;
		bytes_written += actual_written;
	}
	return true;
}
