/*
 * debug.c - for debug and logging
 *
 * Copyright (C) 2019 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: anjan.chanda@iopsys.eu
 *
 */

#include "debug.h"
#include "utils.h"
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/stat.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>


static int loglevel;
static bool syslogging;
static uint32_t features;
static int ffd = -1;
static int ofd = -1;
static const char *logfile;
static FILE *outfile;
static bool logfile_isfifo;
extern const char *PROG_NAME;
static const int syslog_level[] = { LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG };

uint32_t logfeature_to_enum(const char *s)
{
	if (!strncmp(s, "timer", 5))
		return BIT(LOG_TIMER);

	if (!strncmp(s, "steer", 5))
		return BIT(LOG_STEER);

	if (!strncmp(s, "bsteer", 6))
		return BIT(LOG_BSTEER);

	if (!strncmp(s, "apconfig", 8))
		return BIT(LOG_APCFG);

	if (!strncmp(s, "sta", 3))
		return BIT(LOG_STA);

	if (!strncmp(s, "dyncntlr", 8))
		return BIT(LOG_DCNTLR);

	if (!strncmp(s, "dpp", 3))
		return BIT(LOG_DPP);

	if (!strncmp(s, "ts", 2))
		return BIT(LOG_TS);

	if (!strncmp(s, "qos", 3))
		return BIT(LOG_QOS);

	if (!strncmp(s, "channel", 7))
		return BIT(LOG_CHANNEL);

	if (!strncmp(s, "scan", 4))
		return BIT(LOG_SCAN);
#if (EASYMESH_VERSION >= 6)
	if (!strncmp(s, "mlo", 3))
		return BIT(LOG_MLO);
#endif
#ifdef DYNBH
	if (!strncmp(s, "dynbh", 7))
		return BIT(LOG_DYNBH);
#endif
	if (!strncmp(s, "misc", 4))
		return BIT(LOG_MISC);

	if (!strncmp(s, "default", 7))
		return BIT(LOG_DEFAULT);

	if (!strncmp(s, "all", 3))
		return LOG_FEATURE_ALL;

	return 0;
}

const char *logfeature_to_string(uint32_t e)
{
	switch (e) {
	case LOG_TIMER:   return "timer";
	case LOG_STEER:   return "steer";
	case LOG_BSTEER:  return "bsteer";
	case LOG_APCFG:   return "apconfig";
	case LOG_STA:     return "sta";
	case LOG_DPP:     return "dpp";
	case LOG_DCNTLR:  return "dyncntlr";
	case LOG_TS:      return "ts";
	case LOG_QOS:     return "qos";
	case LOG_CHANNEL: return "channel";
	case LOG_SCAN:    return "scan";
#if (EASYMESH_VERSION >= 6)
	case LOG_MLO: return "mlo";
#endif
#ifdef DYNBH
	case LOG_DYNBH:   return "dynbh";
#endif
	case LOG_MISC:    return "misc";
	case LOG_DEFAULT: return "default";
	}

	return "";
}

void restart_logging(void *args)
{
	struct log_options *lopts = (struct log_options *)args;

	syslogging = lopts->syslogging;
	logfile = lopts->logfile;
	logfile_isfifo = lopts->logfile_isfifo;
	loglevel = lopts->level;
	features = lopts->features;

	if (syslogging)
		openlog(PROG_NAME, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);

	syslog(syslog_level[loglevel > 3 ? 3 : loglevel],
	       "Set log features = 0x%08x, level = %d", features, loglevel);

	if (!logfile) {
		outfile = stderr;
		ofd = fileno(stderr);
		return;
	}

	if (logfile_isfifo) {
		struct stat st;
		int rfd;

		if (stat(logfile, &st))
			unlink(logfile);

		mkfifo(logfile, 0600);
		if (stat(logfile, &st) == -1 || !S_ISFIFO(st.st_mode))
			return;

		rfd = open(logfile, O_RDONLY | O_NONBLOCK);
		if (rfd) {
			ffd = open(logfile, O_WRONLY | O_NONBLOCK);
			close(rfd);
		}
	} else {
		ofd = open(logfile, O_CREAT | O_WRONLY | O_APPEND | O_NONBLOCK);
	}
}

void stop_logging(void)
{
	if (syslogging)
		closelog();

	if (outfile)
		fclose(outfile);

	if (ofd > 0) {
		close(ofd);
		ofd = -1;
	}

	if (ffd) {
		close(ffd);
		unlink(logfile);
	}
}

static void log_timestamp(int fd)
{
	time_t now = time(NULL);
	struct tm *tm_now = localtime(&now);
	const char *tm_fmt = "[%d-%02d-%02d %02d:%02d:%02d] ";

	dprintf(fd, tm_fmt,
		tm_now->tm_year + 1900,
		tm_now->tm_mon + 1,
		tm_now->tm_mday,
		tm_now->tm_hour,
		tm_now->tm_min,
		tm_now->tm_sec);
}

void log_message(int feature, int level, const char *fmt, ...)
{
	va_list args;
	int fd = -1;

	if (!(BIT(feature) & features))
		return;

	if (level > loglevel)
		return;

	va_start(args, fmt);
	if (syslogging && level >= 0)
		vsyslog(syslog_level[(loglevel > 3) ? 3 : loglevel], fmt, args);

	if (logfile_isfifo && ffd > 0)
		fd = ffd;
	else if (ofd >= 0)
		fd = ofd;

	if (fd != -1) {
		log_timestamp(fd);
		dprintf(fd, "[%d]: ", getpid());
		vdprintf(fd, fmt, args);
	}

	va_end(args);
}

void dump(const unsigned char *buf, int len, char *label)
{
	int i;

	if (label)
		printf("---- %s ----", label);

	for (i = 0; i < len; i++) {
		if (!(i % 4))
			printf("  ");
		if (!(i % 16))
			printf("\n ");
		printf("%02x ", buf[i] & 0xff);
	}

	if (label)
		printf("\n--------------\n");
}
