/*
 * Copyright (c) 2003, 2004 Nokia
 * Author: tsavola@movial.fi
 *
 * This program is licensed under GPL (see COPYING for details)
 */

#include "types.h"
#include "buffer.h"
#include "protocol.h"
#include "common.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <assert.h>

/**
 * Allocates and initializes the buffer_t structure and allocates the buffer.
 */
buffer_t *buf_alloc()
{
	buffer_t *buf = calloc(1, sizeof (buffer_t));
	if (buf) {
		buf->mem_size = BUFFER_SIZE;
		buf->mem = malloc(BUFFER_SIZE);
		if (!buf->mem) {
			free(buf);
			buf = 0;
		}
	}
	return buf;
}

/**
 * Frees memory allocated by buffer and the buffer.
 * @param buf the buffer
 */
void buf_free(buffer_t *buf)
{
	if (buf->mem)
		free(buf->mem);

	free(buf);
}

/**
 * Sets the EOF flag. When the buffer is written completely, this causes the
 * @param buf the buffer
 * target file to be closed.
 */
void buf_set_eof(buffer_t *buf)
{
	buf->eof = TRUE;
}

/**
 * Calculates the amount of data currently in the buffer.
 * @param buf the buffer
 * @return the amount of data in the buffer
 */
size_t buf_size(buffer_t *buf)
{
	return buf->end - buf->start;
}

/**
 * Checks if the buffer is empty and doesn't have the EOF flag set.
 * @param buf the buffer
 * @returns TRUE or FALSE
 */
bool_t buf_is_empty(buffer_t *buf)
{
	return buf->end == 0 && buf->eof == FALSE;
}

/**
 * Appends data to a buffer from a file. If buffer is full, a larger block of
 * memory is allocated for it.
 * @param buf the buffer
 * @param fd the file descriptor
 * @param len the amount of data to be copied
 * @return the number of bytes actually read, -1 on error
 */
ssize_t buf_read_in(buffer_t *buf,
		    int fd,
		    size_t len)
{
	size_t size;

	assert (!buf->eof);

	size = buf->end + len;
	if (size > buf->mem_size) {
		/* TODO: use relloac() */

		uint8_t *mem;

		mem = malloc(size);
		if (!mem) {
			return -1;
		}

		memcpy(mem, buf->mem, buf->mem_size);
		free(buf->mem);

		buf->mem_size = size;
		buf->mem = mem;
	}

	if (read_buf(fd, buf->mem + buf->end, len) < 0) {
		return -1;
	}

	buf->end += len;

	return len;
}

/**
 * Writes as much data from a buffer to a file as possible.
 * @param buf the buffer
 * @param fd the file descriptor
 * @return the amount of data actually written or -1 if fd doesn't want data
 */
ssize_t buf_write_out(buffer_t *buf,
		      int *fd)
{
	size_t size;
	ssize_t len = 0;

	size = buf_size(buf);
	if (size) {
		assert(buf->start < buf->mem_size);

		len = write_ni(*fd, &buf->mem[buf->start], size);
		if (len < 0) {
			if (errno == EAGAIN) {
				len = 0;
			} else {
				close(*fd);
				*fd = -1;

				return -1;
			}
		}

		buf->start += len;

		size = buf_size(buf);
		if (size == 0) {
			buf->start = 0;
			buf->end = 0;
		}
	}

	if (size == 0 && buf->eof) {
		close(*fd);
		*fd = -1;
		errno = 0;

		/* Make buffer empty. */
		buf->eof = FALSE;
	}

	return len;
}
