#ifndef INPUTSTREAM_H
#define INPUTSTREAM_H

/*
 * @(#)InputStream.h
 *
 * This file is part of webCDwriter - Network CD/DVD Writing.
 *
 * Copyright (C) 2004 Jörg P. M. Haeger
 *
 * webCDwriter is free software. See CDWserver.cpp for details.
 */

#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include "String.h"
#include "StringBuffer.h"

class InputStream {
	int fd;
	char *buf;
	int size;
	int p1, p2;

public:
	InputStream(int fd, int size = 16 * 1024) {
		this->fd = fd;
		buf = new char[size];
		this->size = size;
		p1 = p2 = 0;
	}

	~InputStream() {
		delete[] buf;
	}

	int read() {
		if (p1 == p2) {
			resetBuffer();
			if (readInternal() <= 0)
				return -1;
		}
		return buf[p1++];
	}

	int read(char *aBuf, int off, int len) {
		if (len <= 0)
			return len;
		if (p1 == p2)
			if (readInternal() < 0)
				return -1;
		if (len > p2 - p1)
			len = p2 - p1;
		memcpy(&aBuf[off], &buf[p1], len);
		p1 += len;
		return len;
	}

	int readInternal() {
		if (p1 == p2)
			resetBuffer();
		while (true) {
			int n = ::read(fd, &buf[p2], size - p2);
			if (n < 0) {
				if (errno == EINTR)
					continue;
				return -1;
			}
			else
				p2 += n;
			return n;
		}
	}

	String *readLine() {
		StringBuffer sb = new StringBuffer();
		while (true) {
			int ch = read();
			if (ch < 0)
				break;
			if (ch == '\n')
				break;
			sb.append((char) ch);
		}
		return new String(sb);
	}

	char *readLine(char *buf, int len) {
		int i = 0;
		while (i < len - 1) {
			int ch = read();
			if (ch < 0)
				if (i == 0)
					return NULL;
				else
					break;
			if (ch == '\n')
				break;
			buf[i++] = ch;
		}
		buf[i] = 0;
		return buf;
	}

	void resetBuffer() {
		p1 = p2 = 0;
	}

	long skip(long n) {
		if (p1 + n <= p2) {
			p1 += n;
			return n;
		}

		long i = n;
		i -= p2 - p1;
		resetBuffer();
		while (i > 0) {
			int m = readInternal();
			if (m < 0)
				break;
			if (m > i)
				m = i;
			p1 += m;
			if (p1 == p2)
				resetBuffer();
			i -= m;
		}
		return n - i;
	}

	int waitForInput(int seconds) {
		fd_set fdSet;
		FD_ZERO(&fdSet);
		FD_SET(fd, &fdSet);

		struct timeval timeout;
		timeout.tv_sec = seconds;
		timeout.tv_usec = 0;

		int res;
		do {
	 		res = select(FD_SETSIZE, &fdSet, NULL, NULL, &timeout);
		} while (res == -1L && errno == EINTR);

		return res;
	}
};

#endif
