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

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

void usage();
void die(const char *);
char *get_default_filepath(char *);
void download_file(char *url, char *filepath, CURL *curl);
int file_exists(const char *filename);

enum Download_types{
	URL_ONLY,
	URL_FILE_ONLY,
	URL_FILE_AND_FILEPATHS_FILE
};

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

	char *url_file_path = NULL;
	char *filepaths_file_path = NULL;
	
	//get args
	if(argc == 1) 
		usage();
	else if(argc == 2){
		url = argv[1];
		filepath = get_default_filepath(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{
				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();
	
	if(!curl)
		die("Curl init failed!");

	enum Download_types download_type;

	if (url != NULL && filepath != NULL && url_file_path == NULL && filepaths_file_path == NULL){		// only 1 file
		download_type = URL_ONLY;
	}else if(url == NULL && filepath == NULL && url_file_path != NULL && filepaths_file_path == NULL){	// -uf
		download_type = URL_FILE_ONLY;
	}else if(url == NULL && filepath == NULL && url_file_path != NULL && filepaths_file_path != NULL){	// -uf and  -ff 
		download_type = URL_FILE_AND_FILEPATHS_FILE;
	}else{
		puts("Unsupported arguments combination given!");
		usage();
	}	

	FILE *url_file = NULL;
	FILE *filepaths_file = NULL;
	char *curr_url;
	char *curr_filepath;
	size_t url_len = 0;
	size_t filepath_len = 0;


	switch(download_type){
		case URL_ONLY:
			download_file(url, filepath, curl);	
			break;
		//TODO:refactor (merge if possible) 2 cases below
		case URL_FILE_ONLY:
			url_file = fopen(url_file_path, "r");
			
			if(!url_file)
				die("Could not open given url file!");

						while(getline(&curr_url, &url_len, url_file) != -1){

				if (curr_url[strlen(curr_url) - 1] == '\n')
						curr_url[strlen(curr_url) - 1] = '\0';

				download_file(curr_url, get_default_filepath(curr_url), curl);
			}

			free(curr_url);
			fclose(url_file);
			break;
		case URL_FILE_AND_FILEPATHS_FILE:
			url_file = fopen(url_file_path, "r");
			filepaths_file = fopen(filepaths_file_path, "r");
			
			if(!url_file)
				die("Could not open given url file!");
			if(!filepaths_file)
				die("Could not open given filepaths file!");

			while(getline(&curr_url, &filepath_len, url_file) != -1){

				if(getline(&curr_filepath, &url_len, filepaths_file) == -1)
					curr_filepath = get_default_filepath(curr_url);
				if (curr_filepath[strlen(curr_filepath) - 1] == '\n')
						curr_filepath[strlen(curr_filepath) - 1] = '\0';

				if (curr_url[strlen(curr_url) - 1] == '\n')
						curr_url[strlen(curr_url) - 1] = '\0';

				download_file(curr_url, curr_filepath, curl);
			}

			free(curr_url);
			fclose(url_file);

			break;
	}
		curl_easy_cleanup(curl);
	return 0;
}

void
die(const char * error)
{
	fprintf(stderr, "Error: %s\n", error);
	exit(EXIT_FAILURE);
}

void
download_file(char *url, char *filepath, CURL *curl)
{
	FILE *file;
	file = fopen(filepath,"wb");

	if(!file){
		fprintf(stderr, "Error: Could not open file for writing: %s\n", filepath);
		exit(1);
	}

	curl_easy_setopt(curl, CURLOPT_URL, url);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);	
	
	CURLcode res = curl_easy_perform(curl);

	fclose(file);
	if(res != CURLE_OK)
		fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));	
	else
		fprintf(stdout, "curl returned %s; Downloaded %s \n", curl_easy_strerror(res), filepath);	
}

char* 
get_default_filepath(char *url)
{
	char *url_copy = malloc(strlen(url) + 1);
    if (!url_copy) {
		die("Malloc failed!");
    }

    strcpy(url_copy, url);

    char *filepath = url_copy;
    char *tmp = strtok(filepath, "/");
    while(tmp != NULL){
        filepath = tmp;
        tmp = strtok(NULL, "/");
    }
	// TODO: Move memory allocation and deallocation logic to the caller function for better resource management.
    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"
     "At least one of the -u arguments should be given.\n"
     "Supported combinations:\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);
}


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