/*
 * Copyright (c) 2011 Red Hat, Inc.
 *
 * All rights reserved.
 *
 * Author: Angus Salkeld <asalkeld@redhat.com>
 *
 * libqb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * libqb 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "os_base.h"
#include <signal.h>
#include <syslog.h>

#include <qb/qbdefs.h>
#include <qb/qblog.h>

#define MY_TAG_ONE   (1)
#define MY_TAG_TWO   (1 << 1)
#define MY_TAG_THREE (1 << 2)

static uint8_t _log_priority = LOG_WARNING;

static void
func_one(void)
{
	FILE *fd;

	qb_enter();
	qb_logt(LOG_DEBUG, MY_TAG_TWO, "arf arf?");
	qb_logt(LOG_CRIT, MY_TAG_THREE, "arrrg!");
	qb_logt(134, MY_TAG_THREE, "big priority");
	qb_logt(LOG_ERR, MY_TAG_THREE, "oops, I did it again");
	qb_log(LOG_INFO, "are you aware ...");

	fd = fopen("/nothing.txt", "r+");
	if (fd == NULL) {
		qb_perror(LOG_ERR, "can't open(\"/nothing.txt\")");
	} else {
		fclose(fd);
	}
	qb_leave();
}

static void
func_two(void)
{
	qb_enter();
	qb_logt(LOG_DEBUG, 0, "arf arf?");
	qb_logt(LOG_CRIT, MY_TAG_ONE, "arrrg!");
	qb_log(LOG_ERR, "oops, I did it again");
	qb_logt(LOG_INFO, MY_TAG_THREE, "are you aware ...");
	qb_leave();
}

static void
show_usage(const char *name)
{
	printf("usage: \n");
	printf("%s <options>\n", name);
	printf("\n");
	printf("  options:\n");
	printf("\n");
	printf("  -v             verbose\n");
	printf("  -t             threaded logging\n");
	printf("  -e             log to stderr\n");
	printf("  -b             log to blackbox\n");
	printf("  -f <filename>  log to a file\n");
	printf("  -h             show this help text\n");
	printf("\n");
}

static int32_t do_blackbox = QB_FALSE;
static int32_t do_threaded = QB_FALSE;

static void
sigsegv_handler(int sig)
{
	(void)signal(SIGSEGV, SIG_DFL);
	if (do_blackbox) {
		qb_log_blackbox_write_to_file("simple-log.fdata");
	}
	qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
	raise(SIGSEGV);
}

static const char *
my_tags_stringify(uint32_t tags)
{
	if (qb_bit_is_set(tags, QB_LOG_TAG_LIBQB_MSG_BIT)) {
		return "libqb";
	} else if (qb_bit_is_set(tags, 0)) {
		return "ONE";
	} else if (qb_bit_is_set(tags, 1)) {
		return "TWO";
	} else if (qb_bit_is_set(tags, 2)) {
		return "THREE";
	} else {
		return "MAIN";
	}
}

static void
trace_logger(int32_t t,
	     struct qb_log_callsite *cs, time_t timestamp, const char *msg)
{
	char output_buffer[QB_LOG_MAX_LEN];
	output_buffer[0] = '\0';
	qb_log_target_format(t, cs, timestamp, msg, output_buffer);
	fprintf(stderr, "%s\n", output_buffer);
}

static void
m_filter(struct qb_log_callsite *cs)
{
	if ((cs->priority >= LOG_ALERT &&
	     cs->priority <= _log_priority) &&
	    strcmp(cs->filename, __FILE__) == 0) {
		qb_bit_set(cs->targets, QB_LOG_STDERR);
	} else {
		qb_bit_clear(cs->targets, QB_LOG_STDERR);
	}
}

int32_t
main(int32_t argc, char *argv[])
{
	const char *options = "vhteobdf:";
	int32_t opt;
	int32_t tracer;
	int32_t do_stderr = QB_FALSE;
	int32_t do_stdout = QB_FALSE;
	int32_t do_dump_blackbox = QB_FALSE;
	char *logfile = NULL;
	int32_t log_fd = -1;

	while ((opt = getopt(argc, argv, options)) != -1) {
		switch (opt) {
		case 'd':
			do_dump_blackbox = QB_TRUE;
			break;
		case 't':
			do_threaded = QB_TRUE;
			break;
		case 'e':
			do_stderr = QB_TRUE;
			break;
		case 'o':
			do_stdout = QB_TRUE;
			break;
		case 'b':
			do_blackbox = QB_TRUE;
			break;
		case 'f':
			logfile = optarg;
			break;
		case 'v':
			_log_priority++;
			break;
		case 'h':
		default:
			show_usage(argv[0]);
			exit(0);
			break;
		}
	}

	if (do_dump_blackbox) {
		qb_log_blackbox_print_from_file("simple-log.fdata");
		exit(0);
	}

	signal(SIGSEGV, sigsegv_handler);

	qb_log_init("simple-log", LOG_USER, LOG_INFO);
	qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_THREADED, do_threaded);
	qb_log_tags_stringify_fn_set(my_tags_stringify);

	if (do_stderr) {
		qb_log_filter_fn_set(m_filter);
		qb_log_format_set(QB_LOG_STDERR, "[%p] %4g: %f:%l %b");
		qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);

		tracer = qb_log_custom_open(trace_logger, NULL, NULL, NULL);
		qb_log_ctl(tracer, QB_LOG_CONF_ENABLED, QB_TRUE);
		qb_log_format_set(tracer, "%4g: %n() %b");
		qb_log_filter_ctl2(tracer, QB_LOG_FILTER_ADD,
				   QB_LOG_FILTER_FILE, __FILE__,
				   LOG_TRACE, 200);
	}
	if (do_stdout) {
		qb_log_filter_ctl2(QB_LOG_STDOUT, QB_LOG_FILTER_ADD,
				   QB_LOG_FILTER_FILE, __FILE__,
				   LOG_ALERT, QB_MIN(LOG_DEBUG, _log_priority));
		qb_log_format_set(QB_LOG_STDOUT, "[%p] %4g: %f:%l %b");
		qb_log_ctl(QB_LOG_STDOUT, QB_LOG_CONF_ENABLED, QB_TRUE);
	}
	if (do_blackbox) {
		qb_log_filter_ctl(QB_LOG_BLACKBOX, QB_LOG_FILTER_ADD,
				  QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
		qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 4096);
		qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_THREADED, QB_FALSE);
		qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
	}
	if (logfile) {
		log_fd = qb_log_file_open(logfile);
		qb_log_filter_ctl(log_fd, QB_LOG_FILTER_ADD,
				  QB_LOG_FILTER_FILE, __FILE__, _log_priority);
		qb_log_format_set(log_fd, "[%N] %t %n() [%p] %b");
		qb_log_ctl(log_fd, QB_LOG_CONF_THREADED, do_threaded);
		qb_log_ctl(log_fd, QB_LOG_CONF_ENABLED, QB_TRUE);
	}
	if (do_threaded) {
		qb_log_thread_start();
	}
	qb_log(LOG_DEBUG, "hello");
	qb_log(LOG_INFO, "this is an info");
	qb_log(LOG_NOTICE, "hello - notice?");
	{
		char * str = NULL;
		qb_log(LOG_ERR,
		       "%s-%d-%s-%u",
		       NULL, 952, str, 56);
	}
	func_one();
	func_two();

	if (!do_threaded) {
		/* Disabling syslog here will prevent the logs from
		 * getting flushed in qb_log_fini() if threaded
		 * logging is on.
		 */
		qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);

		qb_log(LOG_WARNING, "no syslog");
		qb_log(LOG_ERR, "no syslog");
	}

	if (do_blackbox) {
		logfile = NULL;
		logfile[5] = 'a';
	} else {
		qb_log_fini();
	}
	return 0;
}
