/*BINFMTC:
 *  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");
    }
}
