/*
  This file is part of CDO. CDO is a collection of Operators to
  manipulate and analyse Climate model Data.

  Copyright (C) 2003-2019 Uwe Schulzweida, <uwe.schulzweida AT mpimet.mpg.de>
  See COPYING file for copying and redistribution conditions.

  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; version 2 of the License.

  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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdarg.h>
#include <errno.h>
#include <cdi.h>
#include <pthread.h>
#include <string>

#include "exception.h"
#include "error.h"
#include "text.h"

namespace CimdOut
{
void (*exitProgram)(void);
const char *(*getContext)(void);
bool silentMode = 0;
bool reportError = _Verbose; //_Verbose from error.h
bool exitOnError = true;

void
enableSilentMode(bool enable)
{
  silentMode = enable;
}

void
enableErrorReporting(bool enable)
{
  reportError = enable;
}

void
setExitFunction(void (*func)(void))
{
  exitProgram = func;
}

void
enableExitOnError(void (*func)(void))
{
  exitOnError = func;
}

void
setContextFunction(const char *(*func)(void) )
{
  getContext = func;
}
}  // namespace CimdOut

void pstreamCloseAll(void);

char *
getGRB2ErrStr(const char *progname)
{
  const char *format = "To create a %s application with GRIB2 support use: ./configure --with-eccodes=<ECCODES root directory> ...";
  size_t finalSize = snprintf(nullptr, 0, format, progname);
  char *errStr = (char *) malloc(finalSize + 1);
  sprintf(errStr, format, progname);

  return errStr;
}

char *
getNCErrString(int filetype, const char *progname)
{
  const char *ncv = (filetype == CDI_FILETYPE_NC4 || filetype == CDI_FILETYPE_NC4C)
                        ? "4"
                        : ((filetype == CDI_FILETYPE_NC2) ? "2" : ((filetype == CDI_FILETYPE_NC5) ? "5" : ""));
#ifdef HAVE_LIBNETCDF
  const char *format = "%s was build with a NetCDF version which doesn't support NetCDF%s data!";
  size_t finalSize = snprintf(nullptr, 0, format, progname, ncv, ncv);
  char *errStr = (char *) malloc(finalSize + 1);
  sprintf(errStr, format, progname, ncv);
#else
  const char *format = "To create a %s application with NetCDF%s support use: ./configure --with-netcdf=<NetCDF%s root directory> ...";
  size_t finalSize = snprintf(nullptr, 0, format, progname, ncv, ncv);
  char *errStr = (char *) malloc(finalSize + 1);
  sprintf(errStr, format, progname, ncv, ncv);
#endif

  return errStr;
}

static char *
checkForMissingLib(int filetype, const char *progname)
{
  char *errStr = nullptr;

  switch (filetype)
    {
    case CDI_FILETYPE_GRB: break;
    case CDI_FILETYPE_GRB2:
      {
        errStr = getGRB2ErrStr(progname);
        break;
      }
    case CDI_FILETYPE_SRV: break;
    case CDI_FILETYPE_EXT: break;
    case CDI_FILETYPE_IEG: break;
    case CDI_FILETYPE_NC:
    case CDI_FILETYPE_NC2:
    case CDI_FILETYPE_NC4:
    case CDI_FILETYPE_NC4C:
    case CDI_FILETYPE_NC5:
      {
        errStr = getNCErrString(filetype, progname);
        break;
      }
    default: break;
    }

  return errStr;
}

void
cdiOpenError(int cdiErrno, const char *fmt, const char *path)
{
  printf("\n");
  set_text_color(stderr, RESET, RED);
  fprintf(stderr, "%s: ", CimdOut::getContext());
  reset_text_color(stderr);
  set_text_color(stderr, RESET, BLACK);
  fprintf(stderr, fmt, path);
  reset_text_color(stderr);
  fprintf(stderr, "\n");

  fprintf(stderr, "%s\n", cdiStringError(cdiErrno));

  if (cdiErrno == CDI_ELIBNAVAIL)
    {
      int byteorder;
      int filetype = cdiGetFiletype(path, &byteorder);
      char *errStr = checkForMissingLib(filetype, "CDO");
      if (errStr)
        {
          fprintf(stderr, "%s\n", errStr);
          free(errStr);
        }
    }

  if (CimdOut::exitOnError)
    {
      CimdOut::exitProgram();
    }
}

void
cdoAbort(const char *fmt, ...)
{
  printf("\n");
  set_text_color(stderr, RESET, RED);
  fprintf(stderr, "%s (Abort): ", CimdOut::getContext());
  reset_text_color(stderr);

  set_text_color(stderr, RESET, BLACK);
  va_list args;
  va_start(args, fmt);
  vfprintf(stderr, fmt, args);
  va_end(args);
  reset_text_color(stderr);
  fprintf(stderr, "\n");

  if (CimdOut::exitOnError)
    {
      CimdOut::exitProgram();
    }
}

void
cdoWarning(const char *fmt, ...)
{
  if (CimdOut::reportError)
    {
      set_text_color(stderr, BRIGHT, YELLOW);
      fprintf(stderr, "%s (Warning): ", CimdOut::getContext());
      reset_text_color(stderr);

      set_text_color(stderr, RESET, BLACK);
      va_list args;
      va_start(args, fmt);
      vfprintf(stderr, fmt, args);
      va_end(args);
      reset_text_color(stderr);
      fprintf(stderr, "\n");
    }
}

void
cdoPrint(const char *fmt, ...)
{
  if (!CimdOut::silentMode)
    {
      set_text_color(stderr, RESET, GREEN);
      fprintf(stderr, "%s: ", CimdOut::getContext());
      reset_text_color(stderr);

      set_text_color(stderr, RESET, BLACK);
      va_list args;
      va_start(args, fmt);
      vfprintf(stderr, fmt, args);
      va_end(args);
      reset_text_color(stderr);
      fprintf(stderr, "\n");
    }
}

void
cdoPrintBlue(const char *fmt, ...)
{
  if (!CimdOut::silentMode)
    {
      set_text_color(stderr, RESET, GREEN);
      fprintf(stderr, "%s: ", CimdOut::getContext());
      reset_text_color(stderr);

      set_text_color(stderr, RESET, BLUE);
      va_list args;
      va_start(args, fmt);
      vfprintf(stderr, fmt, args);
      va_end(args);
      reset_text_color(stderr);
      fprintf(stderr, "\n");
    }
}

void
cdoPrintRed(const char *fmt, ...)
{
  if (!CimdOut::silentMode)
    {
      set_text_color(stderr, RESET, GREEN);
      fprintf(stderr, "%s: ", CimdOut::getContext());
      reset_text_color(stderr);

      set_text_color(stderr, RESET, RED);
      va_list args;
      va_start(args, fmt);
      vfprintf(stderr, fmt, args);
      va_end(args);
      reset_text_color(stderr);
      fprintf(stderr, "\n");
    }
}
