/* 
 *
 * Copyright 2007-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de>
 *
 * This software may be used and distributed according to the terms
 * of the GNU General Public License, incorporated herein by reference.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <libgen.h>
#include <string.h>
#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>
#include "util.h"
#include "../config.h"

char *get_query();
int   create_result_dir(char *nmz_result);
int   generate_result(char *query, char *nmz_index, 
		      char *nmz_result, int result_limit);
void  build_index(char *nmz_index, char *mail_dirs[], int num_mail_dirs);
void  usage();

#define BUF_LEN 1024
#define MAX_MAIL_DIRS 1024
#define LENGTH(x) (sizeof x / sizeof x[0])

int 
main(int argc, char **argv) {
	char *query, *home, *result = 0, **my_argv;
	int c, errflg = 0, count, index_flag = 0, num_mail_dirs = 0, my_argc, i;
	char  nmz_base[BUF_LEN];
	char  nmz_result[BUF_LEN];
	char  nmz_index[BUF_LEN];
	char *mail_dirs[MAX_MAIL_DIRS];
	int   result_limit = 2000;

	home = getenv("HOME");
	if (!home) {
		perror("getenv");
		exit(1);
	}

	snprintf(nmz_base, sizeof(nmz_base), "%s/.nmzmail", home);
	mkdir(nmz_base, 0777);

	while ((c = getopt(argc, argv, "b:r:in:")) != -1) {
		switch (c) {
			case 'b':
				strncpy(nmz_base, optarg, sizeof(nmz_base));
				break;
			case 'r':
				result = optarg;
				break;
			case 'i':
				index_flag = 1;
				break;
			case 'n':
				result_limit = atoi(optarg);
				if (result_limit <= 0) {
					errflg++;
				}
				break;
			case '?':
				errflg++;
		}
	}

	if (errflg) {
		usage();
		exit(1);
	}

	my_argc = argc - optind;
	my_argv = argv + optind;

	for (i=0; i<my_argc; i++) {
		if (num_mail_dirs < MAX_MAIL_DIRS) {
			mail_dirs[num_mail_dirs++] = my_argv[i];
		} else {
			fprintf(stderr,
				"maximum number (%d) of mail directories exceeded.\n", 
				MAX_MAIL_DIRS);
			break;
		}
	}

	if (result) {
		strncpy(nmz_result, result, sizeof(nmz_result));
	} else {
		snprintf(nmz_result, sizeof(nmz_result), "%s/result", nmz_base);
	}

	snprintf(nmz_index, sizeof(nmz_index), "%s/index", nmz_base);

	if (index_flag) {
		build_index(nmz_index, mail_dirs, num_mail_dirs);
	} else {
		query = get_query(nmz_base);

		if (query == NULL) {
			fprintf(stderr, "Could not get query string.\n");
			exit(1);
		}

		if (create_result_dir(nmz_result) != 0) {
			fprintf(stderr, "Could not setup result directory %s.\n",
				nmz_result);
			exit(1);
		}

		count = generate_result(query, nmz_index, nmz_result, result_limit);

		printf("%d matches.\n", count);
	}

	exit(0);
}

char *
get_query(char *nmz_base) {
	char *query;
	char  nmz_history[BUF_LEN];

	snprintf(nmz_history, sizeof(nmz_history), "%s/history", nmz_base); 
	using_history();
	read_history(nmz_history);

	query = readline("Query: ");

	if (query != NULL) {
		add_history(query);
		write_history(nmz_history);
		history_truncate_file(nmz_history, 50);
	}

	return query;
}

int 
remove_links(char *dir) {
	DIR *dirp;
	struct dirent *dp;
	struct stat stat_buf;
	int retval = 0;
	char buf[BUF_LEN];

	dirp = opendir(dir);

	if (!dirp) {
		if (errno == ENOENT) {
			return 0;
		} else {
			perror("opendir");
			return 1;
		}
	}

	while(dirp) {
		errno = 0;
		if ((dp = readdir(dirp)) != NULL) {
			snprintf(buf, sizeof(buf), "%s/%s", dir, dp->d_name);
			if (lstat(buf, &stat_buf) != 0) {
				perror("lstat");
			} else {
				if ((stat_buf.st_mode&S_IFMT)==S_IFLNK) {
					unlink(buf);
				} else if ((stat_buf.st_mode&S_IFMT)!=S_IFDIR) {
					fprintf(stderr, "%s is not a symbolic link; aborting.\n",
						buf);
					retval = 1;
					break;
				}
			}
		} else if (errno != 0) {
			perror("readdir");
			retval = 1;
			break;
		} else {
			break;
		}
	}

	closedir(dirp);
	return retval;
}

int 
create_result_dir(char *nmz_result) { 
	char buf[BUF_LEN];
	
	snprintf(buf, sizeof(buf), "%s/cur", nmz_result);
	if (remove_links(buf) != 0) {
	  return 1;
	}

	mkdir(nmz_result, 0777);
	snprintf(buf, sizeof(buf), "%s/cur", nmz_result);
	mkdir(buf, 0777);
	snprintf(buf, sizeof(buf), "%s/new", nmz_result);
	mkdir(buf, 0777);
	snprintf(buf, sizeof(buf), "%s/tmp", nmz_result);
	mkdir(buf, 0777);

	return 0;
}

int 
generate_result(char *query, char *nmz_index, 
	char *nmz_result, int result_limit) {
	char buf[BUF_LEN], buf1[BUF_LEN], result_limit_str[BUF_LEN];
	int  count = 0;
	FILE *fp;
	char *args[32];
	int status, i = 0;
	pid_t pid;

	snprintf(result_limit_str, sizeof(result_limit_str), "%d", result_limit);

	args[i++] = "namazu";
	args[i++] = "-q";
	args[i++] = "-n";
	args[i++] = result_limit_str;
	args[i++] = "-l";
	args[i++] = "-s";
	args[i++] = query;
	args[i++] = nmz_index;
	args[i++] = NULL;

	fp = pexecvp("namazu", args, &pid, "r");

	if (!fp) {
		perror("pexecvp");
		return 0;
	}

	while (fgets(buf, sizeof(buf), fp)) {
		int ret;
		char *newl = strchr(buf, (int) '\n');
		if (*newl) {
			*newl = '\0';
		}

		snprintf(buf1, sizeof(buf1), "%s/cur/%s", nmz_result, basename(buf));

		ret = symlink(buf, buf1);
		if (ret != 0) {
			perror("symlink");
		} else {
			count++;
		}
	}

	fclose(fp);
	waitpid(pid, &status, 0); 
	if(WEXITSTATUS(status) == 127) {
		fprintf(stderr, "namazu executable not found.\n");
	}

	return count;
}

void 
build_index(char *nmz_index, char *mail_dirs[], int num_mail_dirs) {
	char *args[MAX_MAIL_DIRS + 10];
	int j, i = 0;

	mkdir(nmz_index, 0777);
	args[i++] = "mknmz";
	args[i++] = "-Len_US.ISO8859-1";
	args[i++] = "-O";
	args[i++] = nmz_index;
	args[i++] = "--mailnews";
	args[i++] = "--allow=.*";

	for (j = 0; j < num_mail_dirs && i < LENGTH(args) - 1; j++) {
		args[i++] = mail_dirs[j];
	}

	args[i] = NULL;

	execvp("mknmz", args);
	perror("execvp");
}

void 
usage() {
	fprintf(stderr,
		"nmzmail version %s\n"
		"usage: nmzmail [-b <base>] [-r <result>] -i <maildirs> |\n"
		"               [-b <base>] [-r <result>] [-n <limit>]\n"
		, VERSION);
}
