#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#include <getopt.h>
#include <libgen.h>

#include "common.h"

/* non-portable functions */
static char* msvpvf_internal_strdup(const char* str) {
	size_t len = strlen(str) + 1;

	char* copy = malloc(len);
	if (!copy)
		return NULL;

	memcpy(copy, str, len);
	return copy;
}

static char *msvpvf_internal_asnprintf(const char *format, ...) {
	va_list args;
	int needed_size;
	char *output = NULL;

	va_start(args, format);

	needed_size = vsnprintf(NULL, 0, format, args);
	if (needed_size < 0)
		return NULL;

	va_end(args);

	output = malloc((needed_size + 1) * sizeof(char));
	if (!output)
		return NULL;

	va_start(args, format);

	needed_size = vsnprintf(output, needed_size + 1, format, args);
	if (needed_size < 0) {
		free(output);
		return NULL;
	}

	va_end(args);

	return output;
}

/* source needs read permissions, target needs write permissions, both must be in binary mode */
static int copy_file(FILE* source, FILE* target) {
	char ch[4096];

	while (!feof(source)) {
		size_t b = fread(ch, 1, sizeof(ch), source);
		if (b)
			fwrite(ch, 1, b, target);
	}

	return 0;
}

static const char* type_names[] = {
	[TYPES_VF] = "Movie Studio",
	[TYPES_VEG] = "Vegas Pro",
	[TYPES_UNKNOWN] = "Unknown",
};

static const char* type_extensions[] = {
	[TYPES_VF] = "vf",
	[TYPES_VEG] = "veg",
};

#ifndef ARRAYSIZE
#define ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif

static const char* help_text =
	"msvpvf help text\n"
	"author: Paper <paper@paper.us.eu.org>\n"
	"usage: %s <input>... (arguments)\n"
	"\n"
	"arguments:\n"
	"  -t, --type       one of [vf,veg] (required)\n"
	"  -v, --version    version to convert to (required)\n"
	"  -h, --help       show this screen (optional)\n";

static struct option options_long[] = {
	{"version", required_argument, NULL, 'v'},
	{"type", required_argument, NULL, 't'},
	{"help", 0, NULL, 'h'},
};

int main(int argc, char *argv[]) {
	uint8_t version = 0;
	enum types type = TYPES_VEG; /* ...? */

	int c;
	int option_index = 0;
	while ((c = getopt_long(argc, argv, "v:t:h", options_long, &option_index)) != -1) {
		/* option argument */
		switch (c) {
			case 'v':
				version = atoi(optarg);
				break;
			case 't':
				if (!strcmp(optarg, "vf")) {
					type = TYPES_VF;
				} else if (!strcmp(optarg, "veg")) {
					type = TYPES_VEG;
				} else {
					fprintf(stderr, "[ERROR]: Received an invalid type parameter!\n");
					printf(help_text, argv[0]);
				}

				break;
			case 'h':
			default:
				printf(help_text, argv[0]);
				break;
		}
	}

	if (argc <= optind) {
		fprintf(stderr, "[ERROR]: Missing input file(s)!\n");
		printf(help_text, argv[0]);
		return 1;
	}

	if (!version || !type) {
		printf(help_text, (argc > 0) ? argv[0] : "msvpvf");
		return 1;
	}

	while (optind < argc) {
		const char* input = argv[optind++];
		FILE *input_file, *output_file = NULL;
		uint8_t file_version = 0;
		enum types file_type = TYPES_UNKNOWN;

		printf("Input file name: %s\n", input);

		/* print information about the input file */
		input_file = fopen(input, "rb");
		if (!input_file) {
			fprintf(stderr, "[ERROR]: Error opening input file %s!\n\n", input);
			continue;
		}

		if (fgetc(input_file) == EOF) {
			fprintf(stderr, "[ERROR]: Input file \"%s\" is empty.\n", input);
			fclose(input_file);
			continue;
		}

		if (get_file_information(input_file, &file_version, &file_type)) {
			fprintf(stderr, "[ERROR]: Failed to get file information for input file \"%s\"!\n", input);
			fclose(input_file);
			continue;
		}

		printf("Input file version: %u\n", file_version);
		printf("Input file type: %s\n\n", type_names[file_type]);

		{
			/* open the output file... */
			char* basec = msvpvf_internal_strdup(input);
			char* bname = basename(basec);
			int ext = strrchr(bname, '.') - bname;

			char *output = msvpvf_internal_asnprintf("V%u_%.*s.%s", version, ext, bname, type_extensions[type]);
			if (!output) {
				fprintf(stderr, "[ERROR]: msvpvf_internal_asnprintf failed!\n");
				free(basec);
				free(output);
				fclose(input_file);
				continue;
			}

			output_file = fopen(output, "w+b");
			if (!output_file) {
				fprintf(stderr, "[ERROR]: Failed to open output file %s! Do you have write permissions?\n", output);
				free(basec);
				free(output);
				fclose(input_file);
				continue;
			}

			free(basec);
			free(output);
		}

		copy_file(input_file, output_file);

		fflush(stdout);
		fclose(input_file);

		set_file_information(output_file, version, type);
		fclose(output_file);
	}

	return 0;
}
