/*
 *  logging for cowdancer.
 *  Copyright (C) 2016 James Clarke
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 *
 */

#define _GNU_SOURCE

#include "log.h"

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

#include <curses.h>
#include <term.h>

static const char *red = "\033[0;31m";
static const char *yellow = "\033[1;33m";
static const char *blue = "\033[0;34m";
static const char *reset = "\033[0m";

static int called_setupterm = 0;
static log_level filter_level = log_info;
static log_use_colors use_colors = log_use_colors_auto;
/* Same as use_colors, but stays as auto even when auto has been resolved */
static log_use_colors use_colors_orig = log_use_colors_auto;

static int term_supports_colors(void) {
	/* Don't use log_printf - potential for infinite mutual recursion... */

	int colors;
	int erret;
	const char *term;

	term = getenv("TERM");

	if (!term || !*term) {
		return 0;
	}

	if (!called_setupterm) {
		if (setupterm(term, STDOUT_FILENO, &erret) != OK && erret <= 0) {
			fprintf(stderr, "E: Error calling setupterm: %d\n", erret);
			return 0;
		}

		called_setupterm = 1;
	}

	colors = tigetnum("colors");

	/* colors is -1 on error, which will be treated as not supported */
	return colors >= 8;
}

static FILE *file_for_level(log_level level) {
	switch (level & LOG_LEVEL_MASK) {
		case log_debug:
		case log_info:
			return stdout;
		case log_warn:
		case log_error:
		default:
			return stderr;
	}
}

log_level log_get_filter_level(void) {
	return filter_level;
}

void log_set_filter_level(log_level filter_level_new) {
	filter_level = filter_level_new;
}

log_use_colors log_get_use_colors(void) {
	if (use_colors == log_use_colors_auto) {
		if (term_supports_colors()) {
			use_colors = log_use_colors_yes;
		} else {
			use_colors = log_use_colors_no;
		}
	}

	return use_colors;
}

log_use_colors log_get_use_colors_unresolved(void) {
	return use_colors_orig;
}

void log_set_use_colors(log_use_colors use_colors_new) {
	use_colors = use_colors_orig = use_colors_new;
}

void log_perror(const char *s) {
	if (s != NULL && *s) {
		log_printf(log_error, "%s: %s", s, strerror(errno));
	} else {
		log_printf(log_error, "%s", strerror(errno));
	}
}

void log_printf(log_level level, const char *format, ...) {
	va_list args;

	if (level < filter_level) {
		return;
	}

	log_begin(level);

	va_start(args, format);
	log_vmiddle(level, format, args);
	va_end(args);

	log_end(level);
}

void log_begin(log_level level) {
	const char *color_str;
	FILE *file;
	const char *level_str;

	if (level < filter_level) {
		return;
	}

	file = file_for_level(level);

	if (use_colors == log_use_colors_auto) {
		if (term_supports_colors()) {
			use_colors = log_use_colors_yes;
		} else {
			use_colors = log_use_colors_no;
		}
	}

	switch (level & LOG_LEVEL_MASK) {
		case log_debug:
			color_str = blue;
			level_str = "D";
			break;
		case log_info:
			color_str = reset;
			level_str = "I";
			break;
		case log_warn:
			color_str = yellow;
			level_str = "W";
			break;
		case log_error:
			color_str = red;
			level_str = "E";
			break;
		default:
			color_str = red;
			level_str = "?";
			break;
	}

	if (use_colors == log_use_colors_yes) {
		fprintf(file, "%s", color_str);
	}

	fprintf(file, "%s: ", level_str);
}

void log_middle(log_level level, const char *format, ...) {
	va_list args;
	FILE *file;

	if (level < filter_level) {
		return;
	}

	file = file_for_level(level);

	va_start(args, format);
	vfprintf(file, format, args);
	va_end(args);
}

void log_vmiddle(log_level level, const char *format, va_list args) {
	FILE *file;

	if (level < filter_level) {
		return;
	}

	file = file_for_level(level);

	vfprintf(file, format, args);
}

void log_end(log_level level) {
	FILE *file;

	if (level < filter_level) {
		return;
	}

	file = file_for_level(level);

	if (use_colors == log_use_colors_yes) {
		fprintf(file, "%s\n", reset);
	} else {
		fprintf(file, "\n");
	}
}
