
#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "Error.h"
#include "Client2.h"

Client2::Client2()
{
	force = 0;
	strcpy(prefix, "");
	numOfBytes = numOfCannotRead = numOfDoesNotExist = numOfFiles = 0;
}

void Client2::handle(const char *format, ...)
{
	va_list ap;
	va_start(ap, format);
	char str[1024];
	vsprintf(str, format, ap);
	va_end(ap);

	if (force)
		printf("%s\n", str);
	else
		throw new Error(str);
}

void Client2::putFile(const char *path)
{
	struct stat statBuf;
	if (stat(path, &statBuf) != 0)
	{
		if (errno == ENOENT)
		{
			handle("%s does not exist", path);
			numOfDoesNotExist++;
			return;
		}
		throw new Error("Cannot stat file \"%s\".", path);
	}

	if (S_ISDIR(statBuf.st_mode))
	{
		DIR *dir = opendir(path);
		if (dir == NULL)
		{
			handle("Cannot open directory \"%s\".", path);
			numOfCannotRead++;
			return;
		}

		struct dirent *dirEntry;
		while ((dirEntry = readdir(dir)) != NULL)
		{
			if (strcmp(dirEntry->d_name, ".") == 0
				|| strcmp(dirEntry->d_name, "..") == 0)
				continue;

			char tmpPathName[strlen(path) + 1
				+ strlen(dirEntry->d_name) + 1];
			sprintf(tmpPathName, "%s/%s",
				path, dirEntry->d_name);
			putFile(tmpPathName);
		}

		closedir(dir);

		// make empty dir and/or set the modification time
		char name[4 * 1024];
		sprintf(name, "%s/%s", prefix, path);
		mkdir(name, statBuf.st_mode, statBuf.st_mtime);
		return;
	}
	else
	if (!S_ISREG(statBuf.st_mode))
	{
		handle("%s is not a regular file", path);
		numOfCannotRead++;
		return;
	}

	FILE *inStream = fopen(path, "r");
	if (inStream == NULL)
	{
		handle("Cannot open file \"%s\".", path);
		numOfCannotRead++;
		return;
	}

	printf("%s %6.2f%%", path, 0.0);
	fflush(stdout);
	char name[4 * 1024];
	sprintf(name, "%s/%s", prefix, path);
	Client::putFile(name,
		statBuf.st_size, statBuf.st_mode, statBuf.st_mtime,
		inStream);
	fclose(inStream);
	printf("\n");

	numOfFiles++;
	numOfBytes += statBuf.st_size;
}

void Client2::putTrack(const char *type, const char *path)
{
	int length = -1;
	FILE *inStream;
	if (path == NULL
		|| strcmp(path, "-") == 0
		|| strcmp(path, "--stdin") == 0)
	{
		path = "stdin";
		inStream = stdin;
	}
	else
	{
		struct stat statBuf;
		if (stat(path, &statBuf) != 0)
		{
			if (errno == ENOENT)
			{
				handle("%s does not exist", path);
				numOfDoesNotExist++;
				return;
			}
			throw new Error("Cannot stat file \"%s\".", path);
		}

		if (S_ISDIR(statBuf.st_mode))
			throw new Error("%s is a directory", path);

		if (S_ISREG(statBuf.st_mode))
			length = statBuf.st_size;

		inStream = fopen(path, "r");
		if (inStream == NULL)
		{
			handle("Cannot open track \"%s\".", path);
			numOfCannotRead++;
			return;
		}
	}

	printf("Track (%s) %s ", type, path);
	if (length > 0)
		printf("%9.2f%%", 0.0);
	else
		printf("%8d KB", 0);
	fflush(stdout);

	int bytesTransmitted = 0;
	while (1)
	{
		char buf[32 * 1024];
		int n = fread(buf, 1, sizeof buf, inStream);
		if (n == 0)
			break;
		Client::putTrack(type, n, buf);
		bytesTransmitted += n;
		if (length > 0)
		{
			printf("\b\b\b\b\b\b\b\b\b\b%9.2f%%",
				100.0 * bytesTransmitted / length);
			fflush(stdout);
		}
		else
		{
			printf("\b\b\b\b\b\b\b\b\b\b\b%8d KB",
				bytesTransmitted / 1024);
			fflush(stdout);
		}
		type = "continue";
	}
	printf("\n");

	if (inStream != stdin)
		fclose(inStream);

	if (bytesTransmitted == 0)
		throw new Error("Track \"%s\" seems to be empty.", path);

	numOfFiles++;
	numOfBytes += bytesTransmitted;
}

void Client2::setForce()
{
	force = 1;
}

void Client2::setPrefix(const char *path)
{
	strcpy(prefix, path);
}
