#include "compat.h"
#include <libbackuppcd.h>

#define BPC_CLIENT_LIST 0
#define BPC_CLIENT_GET_INCR 1
#define BPC_CLIENT_GET_FULL 2

const char *bpc_mungepath(const char *path) {
	static char ret[8192];
	char *path_cp, *path_cp_s;
	char *retptr, *tmpbuf;

	if (!path) {
		return(NULL);
	}

	path_cp_s = path_cp = strdup(path);
	retptr = ret;
	ret[0] = '\0';

	if (path[0] == '/') {
		strcat(retptr, "f%2f");
		retptr += 4;
	}

	for (; (tmpbuf = strsep(&path_cp, "/")) != NULL;) {
		if (tmpbuf[0] == '\0') {
			continue;
		}

		*retptr = '/'; retptr++;
		*retptr = 'f'; retptr++;
		*retptr = '\0';
		strcat(retptr, tmpbuf);
		retptr += strlen(tmpbuf);
	}

	if (*retptr == '/') {
		retptr--;
	}
	*retptr = '\0';

	free(path_cp_s);

	return(ret);
}

int main(int argc, char **argv) {
	BPC_CONN *conn1, *conn2 = NULL;
	struct bpc_fileinfo *finfo = NULL;
	char *host, *port_str, *command, *username, *password;
	char localfile[16384];
	int snprintf_ret;
	int port;
	int mode;
	int ret;
	char type_string[][7] = {"dir", "file", "syml", "sock", "fifo", "blk", "chr", "hrdl"};
	const char *pooldir = "/tmp/bpc/cpool";
	const char *lastdir = "/tmp/bpc/pc/test/1";

	if (argc != 6) {
		printf("Usage: backuppcd-client <host> <port> <username> <password> {GET|LIST|GETFULL}\n");
		return(EXIT_FAILURE);
	}

	host = argv[1];
	port_str = argv[2];
	username = argv[3];
	password = argv[4];
	command = argv[5];

	port = strtoul(port_str, NULL, 10);

	conn1 = bpc_connect(host, port, username, password);
	if (!conn1) {
		fprintf(stderr, "Failed (connect)\n");
		return(EXIT_FAILURE);
	}

	if (strcasecmp(command, "list") == 0) {
		mode = BPC_CLIENT_LIST;
	} else if (strcasecmp(command, "get") == 0) {
		mode = BPC_CLIENT_GET_INCR;
	} else if (strcasecmp(command, "getfull") == 0) {
		mode = BPC_CLIENT_GET_FULL;
	} else {
		fprintf(stderr, "Invalid operation: %s\n", command);
		return(EXIT_FAILURE);
	}

	/*
	 * Begin operation
	 */
	switch (mode) {
		case BPC_CLIENT_LIST:
			ret = bpc_list_open(conn1, "/", 1, BPC_HASH_NONE, NULL, NULL);
			break;
		case BPC_CLIENT_GET_INCR:
			ret = bpc_list_open(conn1, "/", 1, BPC_HASH_BPC, NULL, NULL);

			conn2 = bpc_connect(host, port, username, password);
			if (!conn2) {
				fprintf(stderr, "Failed (connect)\n");
				return(EXIT_FAILURE);
			}
			break;
		case BPC_CLIENT_GET_FULL:
			ret = bpc_get_open(conn1, "/", 1, BPC_HASH_NONE, NULL, NULL);
			break;
	}

	if (!ret) {
		fprintf(stderr, "Failed (open)\n");
		return(EXIT_FAILURE);
	}

	/*
	 * Process every entry.
	 */
	while (1) {
		switch (mode) {
			case BPC_CLIENT_LIST:
				finfo = bpc_list(conn1);
				break;
			case BPC_CLIENT_GET_INCR:
				finfo = bpc_list(conn1);
				break;
			case BPC_CLIENT_GET_FULL:
				finfo = bpc_get_head(conn1);
				break;
		}

		if (!finfo) {
			printf("--- end ---\n");
			break;
		}

		printf("[%4s] %06o %6lu %6lu %10llu %12lu %s",
		       type_string[finfo->type],
		       (unsigned int) finfo->mode,
		       (unsigned long) finfo->uid,
		       (unsigned long) finfo->gid,
		       (unsigned long long) finfo->size,
		       (unsigned long) finfo->mtime,
		       finfo->name);
		if (finfo->type == BPC_FILE_SYMLINK || finfo->type == BPC_FILE_HRDLINK) {
			printf(" -> %s", finfo->linkdest);
		}
		printf("\n");

		if (mode == BPC_CLIENT_LIST) {
			continue;
		}

		snprintf_ret = snprintf(localfile, sizeof(localfile), "%s/%s", ".", bpc_mungepath(finfo->name));
		if (snprintf_ret < 0 || snprintf_ret >= sizeof(localfile)) {
			PRINTERR("Filename too long.  Something is almost definitely wrong, aborting.");
			ret = 0;
			CHECKPOINT;
			break;
		}

		switch (mode) {
			case BPC_CLIENT_GET_INCR:
				if (finfo->type == BPC_FILE_DIR) {
					backuppc_mkdir(localfile);
				}

				if (finfo->type != BPC_FILE_REG) {
					break;
				}

				/*
				 * XXX: SIMPLE DUMB CHECK:
				 */
				if (access(localfile, F_OK) == 0) {
					break;
				}

				CHECKPOINT;
				SPOTVAR_S(finfo->name);
				ret = bpc_get_open(conn2, finfo->name, 0, BPC_HASH_NONE, NULL, NULL);
				CHECKPOINT;
				if (!ret) {
					CHECKPOINT;
					break;
				}

				CHECKPOINT;
				finfo = bpc_get_head(conn2);
				CHECKPOINT;
				if (!finfo) {
					ret = bpc_get_close(conn2);
					CHECKPOINT;
					break;
				}

				ret = bpc_copyfile(conn2, finfo, localfile, 0);
				if (!ret) {
					CHECKPOINT;
				}

				finfo = bpc_get_head(conn2);
				if (finfo != NULL) {
					ret = 0;
					break;
				}

				ret = bpc_get_close(conn2);
				CHECKPOINT;
				break;
			case BPC_CLIENT_GET_FULL:
				ret = bpc_copyfile(conn1, finfo, localfile, 0);

				CHECKPOINT;
				break;
		}

		if (!ret) {
			printf("---- failed during file copy ----\n");
			break;
		}
	}

	/*
	 * Cleanup
	 */
	switch (mode) {
		case BPC_CLIENT_LIST:
			ret = bpc_list_close(conn1);
			break;
		case BPC_CLIENT_GET_INCR:
			ret = bpc_list_close(conn1);
			break;
		case BPC_CLIENT_GET_FULL:
			ret = bpc_get_close(conn1);
			break;
	}

	if (!ret) {
		fprintf(stderr, "Failed (close).\n");
		return(EXIT_FAILURE);
	}

	bpc_disconnect(conn1);
	if (conn2) {
		bpc_disconnect(conn2);
	}

	fprintf(stderr, "Done.\n");
	return(EXIT_SUCCESS);
}
