#include <curl/curl.h>
#include <curl/easy.h>

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void usage();
void die(const char *format, va_list args);
char *get_default_filepath(char *, char *);
CURLcode download_file(char *url, char *filepath, CURL *curl);
int file_exists(const char *filename);
void download_result_message(CURLcode res,char *url, char *filepath);
void drop_newline(char *);
void die_if_null(void *obj_ptr, const char *format, ...);

int
main(int argc, char** argv)
{
	char *filepath = NULL;
	char *url = NULL;

	char *url_file_path = NULL;
	char *filepaths_file_path = NULL;

	int show_progressbar = 0;

	//get args
	if(argc == 1 || !(strcmp(argv[1], "-h") != 0)) 
		usage();
	else if(argc == 2){
		url = argv[1];
		filepath = strchr(url, '/');
	}else{
		for(int i = 1; i < argc; i++)
			if(!strcmp(argv[i], "-f"))
				filepath = argv[++i];
			else if(!strcmp(argv[i], "-u"))
				url = argv[++i];
			else if(!strcmp(argv[i], "-uf"))
				url_file_path = argv[++i];
			else if(!strcmp(argv[i], "-ff"))
				filepaths_file_path = argv[++i];
			else if(!strcmp(argv[i], "-h"))
				usage();
			else if(!strcmp(argv[i], "-p"))
				show_progressbar = 1;
			else{
				usage();
			}
	}

	printf("URL: %s\nFilepath: %s\nURL File: %s\nFilepaths File: %s\n", url, filepath, url_file_path, filepaths_file_path);	
	CURL *curl = curl_easy_init();

	die_if_null(curl, "Curl init failed!");
	
	if(show_progressbar == 1)
		curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);

	FILE *url_file = NULL;
	FILE *filepaths_file = NULL;

	char *curr_url;
	char *curr_filepath;

	size_t url_len = 0;
	size_t filepath_len = 0;
	
	CURLcode result;

	if (url != NULL && filepath != NULL && url_file_path == NULL && filepaths_file_path == NULL){		// only 1 file
			//fprintf(stdout, "Starting %s\n", filepath);	
			result = download_file(url, filepath, curl);	
			download_result_message(result, url, filepath);
	}else if((url == NULL && filepath == NULL && url_file_path != NULL && filepaths_file_path == NULL) ||	// -uf
			(url == NULL && filepath == NULL && url_file_path != NULL && filepaths_file_path != NULL)){	// -uf and  -ff 
			url_file = fopen(url_file_path, "r");

			if(filepaths_file_path)
				filepaths_file = fopen(filepaths_file_path, "r");

			die_if_null(url_file, "Could not open given url file %s!", url_file_path);

			if(filepaths_file_path)
				die_if_null(filepaths_file,"Could not open given file file %s!", filepaths_file_path);

			while(getline(&curr_url, &url_len, url_file) != -1){
					drop_newline(curr_url);
				// Determine the filepath
				if (filepaths_file_path != NULL && getline(&curr_filepath, &filepath_len, filepaths_file) != -1) {
					// Try to read a filepath from the filepaths file
						drop_newline(curr_filepath);
					//fallback to default logic if EOF in filepath file or none filepath file given 
				}else
					curr_filepath = strchr(curr_url, '/');

				//fprintf(stdout, "Starting %s\n", filepath);	
				result = download_file(curr_url, curr_filepath, curl);
				download_result_message(result, curr_url, curr_filepath);
			}
			free(curr_url);
			free(curr_filepath);
			if (filepaths_file_path != NULL)
				fclose(filepaths_file);

			fclose(url_file);
	}else{
		puts("Unsupported arguments combination given!");
		usage();
	}	
	curl_easy_cleanup(curl);
	return 0;
}

void 
die(const char *format, va_list args) {
    vfprintf(stderr, format, args);
    fputc('\n', stderr);
    exit(EXIT_FAILURE);
}

void 
die_if_null(void *obj_ptr, const char *format, ...) {
    va_list args;
    va_start(args, format);

    if(obj_ptr == NULL)
        die(format, args);

    va_end(args);
}

void
download_result_message(CURLcode res, char *url, char *filepath)
{
	if(res != CURLE_OK)
		fprintf(stderr, "Failed: %s\n to download %s from %s!\n", curl_easy_strerror(res), filepath, url);	
	else
		fprintf(stdout, "%s; Downloaded %s \n", curl_easy_strerror(res), filepath);	
}

CURLcode
download_file(char *url, char *filepath, CURL *curl)
{
	FILE *file;
	file = fopen(filepath,"wb");
	if(file == NULL){
		fprintf(stderr, "Error: Could not open file for writing: %s\n", filepath);
		return CURLE_WRITE_ERROR;
	}

	curl_easy_setopt(curl, CURLOPT_URL, url);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
	

	CURLcode res = curl_easy_perform(curl);

	fclose(file);

	return res;	
}

char* 
get_default_filepath(char *url, char *place_holder)
{
	char *url_copy = realloc(place_holder, strlen(url) + 1);

	die_if_null(url_copy, "Malloc failed!");

    strcpy(url_copy, url);

    char *filepath = url_copy;
    char *tmp = strtok(filepath, "/");
	
    while(tmp != NULL){
        filepath = tmp;
        tmp = strtok(NULL, "/");
    }
    return filepath;
}

void
usage()
{
	puts("Usage:\n"
     "\t-u <url> to get file from. Other arguments can be omitted.\n"
     "\t-f <filepath> to save file to. Can be omitted; filename from URL will be used.\n"
     "\t-uf <filepath> file with URLs to fetch files from.\n"
     "\t-ff <filepath> file with paths to save files from URLs file. Can be omitted; filename from URL will be used.\n"
	 "\t-h print this.\n"
	 "\t-p enable default curl progressbar.\n"
     "At least one of the -u arguments should be given.\n"
     "Supported combinations for url/filepath options:\n"
     "\t<url>\n"
     "\t-u <url>\n"
     "\t-u <url> -f <path to save file to>\n"
     "\t-uf <path to file with URLs>\n"
     "\t-uf <path to file with URL> -ff <path to file with filenames>\n"
     "! Note: This program rewrites files!");
	exit(0);
}

void
drop_newline(char *str)
{
	if (str[strlen(str) - 1] == '\n')
					str[strlen(str) - 1] = '\0';
}

int 
file_exists(const char *filename) {
    return access(filename, F_OK) != -1;
}
