/*
 * Copyright (c) 2017 Shaun Feakes - All rights reserved
 *
 * You may use redistribute and/or modify this code under the terms of
 * the GNU General Public License version 2 as published by the 
 * Free Software Foundation. For the terms of this license, 
 * see <http://www.gnu.org/licenses/>.
 *
 * You are free to use this software under the terms of the GNU General
 * Public License, 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.
 *
 *  https://github.com/sfeakes/aqualinkd
 */


#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <syslog.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
//#include <time.h>
#include <ctype.h>
#include <fcntl.h>

#ifdef AD_DEBUG
#include <sys/time.h>
#endif

#ifdef AQ_MANAGER
#include <systemd/sd-journal.h>
#endif

#ifndef _UTILS_C_
#define _UTILS_C_
#endif

#include "utils.h"
#include "rs_msg_utils.h"
#include "aq_serial.h"
#include "aqualink.h"

#define DEFAULT_LOG_FILE "/tmp/aqualinkd-inline.log"
//#define MAXCFGLINE 265
#define TIMESTAMP_LENGTH 30

// Since this get's compiled without net_services for serial_logger
// pre-define this here rather than include netservices.h
void broadcast_log(char *msg);

static bool _daemonise = false;

#ifndef AQ_MANAGER
static bool _log2file = false;
static char *_log_filename = NULL;
static bool _cfg_log2file;
static int _cfg_log_level;
#endif
static int _log_level = LOG_WARNING;

static char *_loq_display_message = NULL;
logmask_t _logforcemask = 0;

//static char _log_filename[256];

#ifdef AQ_MANAGER
void setLoggingPrms(int level , bool deamonized, char *error_messages)
{
	_log_level = level;
  _daemonise = deamonized;
  _loq_display_message = error_messages;

  //_cfg_log_level = _log_level; 
}
#else
void setLoggingPrms(int level , bool deamonized, char* log_file, char *error_messages)
{
	_log_level = level;
  _daemonise = deamonized;
  _loq_display_message = error_messages;

  _cfg_log_level = _log_level;
  _cfg_log2file = _log2file;
  
  if (log_file == NULL || strlen(log_file) <= 0) {
    _log2file = false;
  } else {
    _log2file = true;
    _log_filename = log_file;
    //strcpy(_log_filename, log_file);
  }  
}
#endif // AQ_MANAGER

void setSystemLogLevel( int level)
{
  _log_level = level;
}
int getSystemLogLevel()
{
  return _log_level;
}
int getLogLevel(logmask_t from)
{

  if ( (from == RSSD_LOG || from == SLOG_LOG) && isMASK_SET(_logforcemask, from) && _log_level < LOG_DEBUG_SERIAL) {
    return LOG_DEBUG_SERIAL;
  } else if ( isMASK_SET(_logforcemask, from) && _log_level < LOG_DEBUG_SERIAL) {
    return LOG_DEBUG;
  } else if (from == RSTM_LOG && _log_level < LOG_DEBUG_SERIAL && !isMASK_SET(_logforcemask, from)) {
    // RSTM_LOG (RS serial timings) should only print if debug_serial or explicitly set in the forcelogmask.
    return LOG_NOTICE;
 }

  return _log_level;
}

#ifdef AQ_MANAGER
/*
void startInlineLog2File()
{
   _log2file = true;
  if (_log_filename == NULL)
    _log_filename = DEFAULT_LOG_FILE;
}
void stopInlineLog2File()
{
  _log2file = _cfg_log2file;
}
void cleanInlineLog2File() {
  if (_log_filename != NULL) {
    fclose(fopen(_log_filename, "w"));
  }
}
*/
#else // AQ_MANAGER
void startInlineDebug()
{
  _log_level = LOG_DEBUG;
  _log2file = true;
  if (_log_filename == NULL)
    _log_filename = DEFAULT_LOG_FILE;
}

void startInlineSerialDebug()
{
  _log_level = LOG_DEBUG_SERIAL;
  _log2file = true;
  if (_log_filename == NULL)
    _log_filename = DEFAULT_LOG_FILE;
}

void stopInlineDebug()
{
  _log_level = _cfg_log_level;
  _log2file = _cfg_log2file;
}
void cleanInlineDebug() {
  if (_log_filename != NULL) {
    fclose(fopen(_log_filename, "w"));
  }
}
char *getInlineLogFName()
{
  return _log_filename;
}

bool islogFileReady()
{
  if (_log_filename != NULL) {   
    struct stat st;
    stat(_log_filename, &st);
    if ( st.st_size > 0)
      return true;
  } 
  return false;
}
#endif // AQ_MANAGER




/*
* This function reports the last error 
*/
void LOGSystemError (int errnum, logmask_t from, const char *on_what)
{
  fputs (strerror (errno), stderr);
  fputs (": ", stderr);
  fputs (on_what, stderr);
  fputc ('\n', stderr);

  if (_daemonise == TRUE)
  {
    //logMessage (LOG_ERR, "%d : %s", errno, on_what);
    LOG(from, LOG_ERR, "%s (%d) : %s\n", strerror (errno), errno, on_what);
    closelog ();
  }
}
void displayLastSystemError (const char *on_what)
{
  LOGSystemError(errno, AQUA_LOG, on_what);
  /*
  fputs (strerror (errno), stderr);
  fputs (": ", stderr);
  fputs (on_what, stderr);
  fputc ('\n', stderr);

  if (_daemonise == TRUE)
  {
    //logMessage (LOG_ERR, "%d : %s", errno, on_what);
    LOG(AQUA_LOG, LOG_ERR, "%s (%d) : %s", strerror (errno), errno, on_what);
    closelog ();
  }
  */
}

/*
From -- syslog.h --
#define	LOG_EMERG	0	// system is unusable 
#define	LOG_ALERT	1	// action must be taken immediately 
#define	LOG_CRIT	2	// critical conditions 
#define	LOG_ERR		3	// error conditions 
#define	LOG_WARNING	4	// warning conditions 
#define	LOG_NOTICE	5	// normal but significant condition 
#define	LOG_INFO	6	// informational 
#define	LOG_DEBUG	7	// debug-level messages 
*/

char *elevel2text(int level) 
{
  switch(level) {
    case LOG_ERR:
      return "Error:";
      break;
    case LOG_WARNING:
      return "Warning:";
      break;
    case LOG_NOTICE:
      return "Notice:";
      break;
    case LOG_INFO:
      return "Info:";
      break;
    case LOG_DEBUG:
    default:
      return "Debug:";
      break;
  }
  
  return "";
}

int text2elevel(char* level)
{
  if (strcmp(level, "DEBUG_SERIAL") == 0) {
    return LOG_DEBUG_SERIAL;
  }
  if (strcmp(level, "DEBUG") == 0) {
    return LOG_DEBUG;
  }
  else if (strcmp(level, "INFO") == 0) {
    return LOG_INFO;
  }
  else if (strcmp(level, "WARNING") == 0) {
    return LOG_WARNING;
  }
  else if (strcmp(level, "NOTICE") == 0) {
    return LOG_NOTICE;
  }
  
  return  LOG_ERR; 
}

const char* loglevel2name(int level)
{
  if (level == LOG_DEBUG_SERIAL)
    return "Debug Serial:";

  return elevel2text(level);
}

const char* loglevel2cgn_name(int level)
{
  switch(level) {
    case LOG_ERR:
      return "ERROR";
      break;
    case LOG_WARNING:
      return "WARNING";
      break;
    case LOG_NOTICE:
      return "NOTICE";
      break;
    case LOG_INFO:
      return "INFO";
      break;
    case LOG_DEBUG_SERIAL:
      return "DEBUG_SERIAL";
      break;
    case LOG_DEBUG:
    default:
      return "DEBUG";
      break;
  }
}

const char *logmask2name(logmask_t from)
{
  switch (from) {
    case NET_LOG:
      return "NetService:";
    break;
    case ALLB_LOG:
      return "AllButton: ";
    break;
    case RSSA_LOG:
      return "RS SAdptr: ";
    break;
    case ONET_LOG:
      return "One Touch: ";
    break;
    case IAQT_LOG:
      return "iAQ Touch: ";
    break;
    case PDA_LOG:
      return "PDA:       ";
    break;
    case DJAN_LOG:
      return "JandyDvce: ";
    break;
    case DPEN_LOG:
      return "PentaDvce: ";
    break;
    case RSSD_LOG:
      return "RS Serial: ";
    break;
    case PROG_LOG:
      return "Panl&Prog: ";
    break;
    case DBGT_LOG:
      return "DebugTimer:";
    break;
    case SCHD_LOG:
      return "Sched/Timr:";
    break;
    case RSTM_LOG:
      return "RS Timings:";
    break;
    case SIM_LOG:
      return "Simulator: ";
    break;
    case SLOG_LOG:
      return "Serial Log:";
    break;
    case IAQL_LOG:
      return "iAqualink2: ";
    break;
    case AQUA_LOG:
    default:
      return "AqualinkD: ";
    break;
  }
}

void timestamp(char* time_string)
{
    time_t now;
    struct tm *tmptr;

    time(&now);
    tmptr = localtime(&now);
    strftime(time_string, TIMESTAMP_LENGTH, "%b-%d-%y %H:%M:%S %p ", tmptr);
}

//Move existing pointer
char *cleanwhitespace(char *str)
{
  char *end;

  if (str == NULL)
    return str;

  // Trim leading space
  while(isspace(*str)) str++;

  if(*str == 0)  // All spaces?
    return str;

  // Trim trailing space
  end = str + strlen(str) - 1;
  while(end > str && isspace(*end)) end--;

  // Write new null terminator
  if (end != (str + strlen(str) - 1) )
    *(end+1) = 0;

  return str;
}

// Return new pointer
char *stripwhitespace(char *str)
{
  // Should probably just call Trim and Chop functions.
  char *end;
  char *start = str;

  if(*start == 0 || strlen(str) <= 0 )  // All spaces?
    return str;


  // Trim leading space
  while(isspace(*start)) start++;

  if(*start == 0)  // All spaces?
    return start;

  // Trim trailing space
  end = str + strlen(str) - 1;
  while(end > str && isspace(*end)) end--;

  // Write new null terminator
  *(end+1) = 0;

  return start;
}

// Trim whispace (return new pointer) leave trailing whitespace
char *trimwhitespace(char *str)
{
  char *start = str;

  // Trim leading space
  while(isspace(*start)) start++;

  if(*start == 0)  // All spaces?
    return start;

  return start;
}


char *chopwhitespace(char *str)
{
  char *end;
  end = str + strlen(str) - 1;
  while(end > str && isspace(*end)) end--;
  
  // Write new null terminator
  *(end+1) = 0;

  return str;
}


/*
char *cleanquotes(char *str)
{
  char *end;
  // Trim leading whitespace
  //while(isspace(*str)) str++;
  //if(*str == 0)  // All spaces?
  //  return str;
syslog(LOG_INFO, "String to clean %s\n", str);

  while(*str=='"' || *str== '\'' || *str==' ') str++;
  if(*str == 0)  // All spaces
    return str;

  end = str + strlen(str) - 1;
  while(end > str && (*end=='"' || *end== '\'' || *end==' ')) end--;

  // Write new null terminator
  *(end+1) = 0;

syslog(LOG_INFO, "String cleaned %s\n", str);

  return str;
}
*/
int cleanint(char*str)
{
  if (str == NULL)
    return 0;
    
  str = cleanwhitespace(str);
  return atoi(str);
}

/*
void test(int msg_level, char *msg)
{
  char buffer[256];
  
  sprintf(buffer,"Level %d | MsgLvl %d | Dmn %d | LF %d | %s - %s",_log_level,msg_level,_daemonise,_log2file,_log_filename,msg);
  if ( buffer[strlen(buffer)-1] != '\n') {
    strcat(buffer, "\n");
  } 
  
  int fp = open("/var/log/aqualink.log", O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
  if (fp != -1) {
    write(fp, buffer, strlen(buffer) );
    close(fp);  
  } else {
    syslog(LOG_ERR, "Can't open file /var/log/aqualink.log/n");
  }
}
*/

void addDebugLogMask(logmask_t flag)
{
  _logforcemask |= flag;

  if (flag == IAQT_LOG) // If AQTouch add iAqualink
    _logforcemask |= IAQL_LOG;
}

void removeDebugLogMask(logmask_t flag)
{
  _logforcemask &= ~flag;

  if (flag == IAQT_LOG) // If AQTouch remove iAqualink
    _logforcemask &= ~IAQL_LOG;
}

void clearDebugLogMask()
{
  _logforcemask = 0;
}

bool isDebugLogMaskSet(logmask_t flag)
{
  return _logforcemask & flag;
}

void _LOG(logmask_t from, int msg_level, char * message, int message_buffer_size);

/*
void logMessage(int msg_level, const char *format, ...)
{
  if (msg_level > _log_level) {
    return;
  }

  char buffer[1024];
  va_list args;
  va_start(args, format);
  strncpy(buffer, "         ", 20);
  vsprintf (&buffer[20], format, args);
  va_end(args);

  _LOG(AQUA_LOG, msg_level, buffer);
}
*/

#define LOG_OFFSET 20 // Number of chars for logging the type example "Info:    iAQ Touch: "

void LOG_LARGEMSG(const logmask_t from, const int msg_level, const char *message, const int message_length)
{
  // message_length is not used at present.  But maybe in th future we can add a bufer using malloc and realloc that's 
  // reused between calls and simply use realloc if it's not large enough.
  // Need to be careful, as it would also need to be thread safe.

  if ( msg_level > getLogLevel(from))
    return;

  char buffer[LARGELOGBUFFER + LOG_OFFSET + 1];
  
  memset(buffer, ' ', LOG_OFFSET * sizeof(char)); 

  strncpy(&buffer[LOG_OFFSET], message, LARGELOGBUFFER);

  int len = rsm_strncpy(&buffer[LOG_OFFSET], (unsigned char*)message, LARGELOGBUFFER, message_length);

  if (len >= LARGELOGBUFFER) {
    sprintf(&buffer[LARGELOGBUFFER + LOG_OFFSET - 4], "...\n");
  }

  _LOG(from, msg_level, buffer, LARGELOGBUFFER + LOG_OFFSET);
}

void LOG(const logmask_t from, const int msg_level, const char * format, ...)
{
  //printf("msg_level=%d _log_level=%d mask=%d\n",msg_level,_log_level,(_logforcemask & from));
  /*
  if (msg_level > _log_level && ((_logforcemask & from) == 0) ) { // from NOT set in _logforcemask
    return;
  }
  */
  if ( msg_level > getLogLevel(from)) {
    return;
  }

  char buffer[LOGBUFFER];
  va_list args;
  va_start(args, format);
  //strncpy(buffer, "         ", 20);
  memset(buffer, ' ', LOG_OFFSET * sizeof(char)); 
  
  //vsprintf (&buffer[20], format, args);
  int size = vsnprintf (&buffer[LOG_OFFSET], LOGBUFFER-LOG_OFFSET-4, format, args);
  va_end(args);
  if (size >= LOGBUFFER-LOG_OFFSET-4 ) {
    sprintf(&buffer[LOGBUFFER-5], "...\n");
  }

  _LOG(from, msg_level, buffer, LOGBUFFER);
}


void _LOG(logmask_t from, int msg_level, char *message, int message_buffer_size)
{
  /*
   message should have the first LOG_OFFSET (20) characters as spaces, this allows us to add Type & From to message.
   example "                    The Message ..... "
   we add  "Warning: NetService:The Message ..... "
   Type is characters  0 to  8
   From is characters  9 to 20

  */

  int i;

  int msglen = strlen(&message[LOG_OFFSET]);

  // Fill first 20 chars and any non printable chars with a space.
  for(i = 0; i < message_buffer_size && i < (msglen+LOG_OFFSET+1) ; i++) {
    if (i > LOG_OFFSET && message[i] == '\0')
      break;

    if ( message[i] != 10 && message[i] != 13 && ( i < LOG_OFFSET || message[i] < 32 || message[i] > 125) ) {
      //printf ("Change %c to %c in %s\n",message[i], ' ', message);
      message[i] = ' ';
    }
  }

  // Add line feed to end of string if not already there.
  // i-1 == 10 and i == 0
  if (message[i] != '\0' || message[i-1] != '\n') {
    message[i] = '\n';
    message[i+1] = '\0';
  }

  //strncpy(&message[9], logmask2name(from), 11); // Will give compiller warning as doesn;t realise we are copying into middle of string.
  memcpy(&message[9], logmask2name(from), 11);

  // Logging has not been setup yet, so STD error & syslog
  if (_log_level == -1) {
    fprintf (stderr, "%s\n", message);
    syslog (msg_level, "%s", &message[9]);
    closelog ();
  }
  
  #ifdef AQ_MANAGER // Always use systemd journel with aqmanager
    //sd_journal_print()
    //openlog("aqualinkd", 0, LOG_DAEMON);
    if (msg_level > LOG_DEBUG)  // Let's not confuse syslog with custom levels
      sd_journal_print (LOG_DEBUG, "%s", &message[9]);
      //sd_journal_print_with_location(LOG_DEBUG, "aqualinkd", "%s", &message[9]);
    else
      sd_journal_print (msg_level, "%s", &message[9]);
      //sd_journal_print_with_location(msg_level, "aqualinkd", "%s", &message[9]);
    //closelog ();
  #else
  if (_daemonise == TRUE)
  {
    if (msg_level > LOG_DEBUG)  // Let's not confuse syslog with custom levels
      syslog (LOG_DEBUG, "%s", &message[9]);
    else
      syslog (msg_level, "%s", &message[9]);
    closelog ();
    //return;
  }
  #endif //AQ_MANAGER
  
  //int len;
  message[8] = ' ';
  char *strLevel = elevel2text(msg_level);
  //strncpy(message, strLevel, strlen(strLevel)); // Will give compiler warning, so use memcpy instead
  memcpy(message, strLevel, strlen(strLevel));
  //len = strlen(message); 
  /*
  if ( message[len-1] != '\n') {
    strcat(message, "\n");
  }
  */

  // Superceded systemd/sd-journal
  //with Send logs to any websocket that's interested.
  //broadcast_log(message);

  // Sent the log to the UI if configured.
  if (msg_level <= LOG_ERR && _loq_display_message != NULL) {
    //printf ("*** Showing error in web **** \n");
    //snprintf(_loq_display_message, AQ_MSGLONGLEN-2, "%s\n",message);
    int len = rsm_strncpy(_loq_display_message, (unsigned char*)message, AQ_MSGLONGLEN-1, message_buffer_size);
    _loq_display_message[len] = '\0';
    //printf ("*** Adding ERROR to buffer '%s' **** \n",_loq_display_message);
  }

#ifndef AQ_MANAGER
  if (_log2file == TRUE && _log_filename != NULL) {   
    char time[TIMESTAMP_LENGTH];
    int fp = open(_log_filename, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
    if (fp != -1) {
      timestamp(time);
      if ( write(fp, time, strlen(time) ) == -1 ||
           write(fp, message, strlen(message) ) == -1 ) 
      {
        syslog(LOG_ERR, "Can't write to log file %s\n %s", _log_filename, message);
        fprintf (stderr, "Can't write to log file %s\n %s", _log_filename, message);
      }
      //write(fp, time, strlen(time) );
      //write(fp, message, strlen(message) );
      close(fp);
    } else {
      if (_daemonise == TRUE)
        syslog(LOG_ERR, "Can't open log file %s\n %s", _log_filename, message);
      else
        fprintf (stderr, "Can't open debug log %s\n %s", _log_filename, message);
    }
  }
#endif //AQ_MANAGER

  if (_daemonise == FALSE) {
    if (msg_level == LOG_ERR) {
      fprintf(stderr, "%s", message);
    } else {
#ifndef AD_DEBUG
      printf("%s", message);
#else
      struct timespec tspec;
      struct tm localtm;
      clock_gettime(CLOCK_REALTIME, &tspec);
      char timeStr[TIMESTAMP_LENGTH];
      strftime(timeStr, sizeof(timeStr), "%H:%M:%S", localtime_r(&tspec.tv_sec, &localtm));
      printf("%s.%03ld %s", timeStr, tspec.tv_nsec / 1000000L, message);
#endif
    }
  }
}

void daemonise (char *pidFile, void (*main_function) (void))
{
  FILE *fp = NULL;
  pid_t process_id = 0;
  pid_t sid = 0;

  _daemonise = true;
  
  /* Check we are root */
  if (getuid() != 0)
  {
    LOG(AQUA_LOG, LOG_ERR,"Can only be run as root\n");
    exit(EXIT_FAILURE);
  }

  int pid_file = open (pidFile, O_CREAT | O_RDWR, 0666);
  int rc = flock (pid_file, LOCK_EX | LOCK_NB);
  if (rc)
  {
    //if (EWOULDBLOCK == errno)
    //; // another instance is running
    //fputs ("\nAnother instance is already running\n", stderr);
    LOG(AQUA_LOG, LOG_ERR,"\nAnother instance is already running\n");
    exit (EXIT_FAILURE);
  }

  process_id = fork ();
  // Indication of fork() failure
  if (process_id < 0)
  {
    displayLastSystemError ("fork failed!");
    // Return failure in exit status
    exit (EXIT_FAILURE);
  }
  // PARENT PROCESS. Need to kill it.
  if (process_id > 0)
  {
    fp = fopen (pidFile, "w");

    if (fp == NULL)
      LOG(AQUA_LOG, LOG_ERR,"can't write to PID file %s",pidFile);
    else
      fprintf(fp, "%d", process_id);

    fclose (fp);
    LOG(AQUA_LOG, LOG_DEBUG, "process_id of child process %d \n", process_id);
    // return success in exit status
    exit (EXIT_SUCCESS);
  }
  //unmask the file mode
  umask (0);
  //set new session
  sid = setsid ();
  if (sid < 0)
  {
    // Return failure
    displayLastSystemError("Failed to fork process");
    exit (EXIT_FAILURE);
  }
  // Change the current working directory to root.
  if ( chdir ("/") == -1) {
    LOG(AQUA_LOG, LOG_ERR,"Can't set working dir to /");
  }
  // Close stdin. stdout and stderr
  close (STDIN_FILENO);
  close (STDOUT_FILENO);
  close (STDERR_FILENO);

  // this is the first instance
  (*main_function) ();

  return;
}

int count_characters(const char *str, char character)
{
    const char *p = str;
    int count = 0;

    do {
        if (*p == character)
            count++;
    } while (*(p++));

    return count;
}

bool text2bool(char *str)
{
  str = cleanwhitespace(str);
  if (strcasecmp (str, "YES") == 0 || strcasecmp (str, "ON") == 0)
    return TRUE;
  else
    return FALSE;
}

bool request2bool(char *str)
{
  str = cleanwhitespace(str);
  if (strcasecmp (str, "YES") == 0 || strcasecmp (str, "ON") == 0 || atoi(str) == 1)
    return TRUE;
  else
    return FALSE;
}

char *bool2text(bool val)
{
  if(val == TRUE)
    return "YES";
  else
    return "NO";
}

// (50°F - 32) x .5556 = 10°C
float degFtoC(float degF)
{
  return ((degF-32) / 1.8);
}
// 30°C x 1.8 + 32 = 86°F 
float degCtoF(float degC)
{
  return (degC * 1.8 + 32);
}


float timespec2float(const struct timespec *elapsed) {
  return ((float)elapsed->tv_nsec / 1000000000L) + elapsed->tv_sec;
}

#include <time.h>

void delay (unsigned int howLong) // Microseconds (1000000 = 1 second) miliseconds
{
  struct timespec sleeper, dummy ;

  sleeper.tv_sec  = (time_t)(howLong / 1000) ;
  sleeper.tv_nsec = (long)(howLong % 1000) * 1000000 ;

  nanosleep (&sleeper, &dummy) ;
}
/*
// Same as above but can pass 0.5
void ndelay (float howLong) // Microseconds (1000000 = 1 second) 
{
  struct timespec sleeper, dummy ;

  sleeper.tv_sec  = (time_t)((howLong*10) / 10000) ;
  sleeper.tv_nsec = (long)( ((int)(howLong*10)) % 10000) * 1000000 ;

  nanosleep (&sleeper, &dummy) ;
}
*/
char* stristr(const char* haystack, const char* needle) {
  do {
    const char* h = haystack;
    const char* n = needle;
    while (tolower((unsigned char) *h) == tolower((unsigned char ) *n) && *n) {
      h++;
      n++;
    }
    if (*n == 0) {
      return (char *) haystack;
    }
  } while (*haystack++);
  return 0;
}
/*
int ascii(char *destination, char *source) {
  unsigned int i;
  for(i = 0; i < strlen(source); i++) {
    //if ( source[i] >= 0 && source[i] < 128 )
    if ( source[i] >= 0 && source[i] < 127 )
      destination[i] = source[i];
    else
      destination[i] = ' ';

    if ( source[i]==126 ) {
      destination[i-1] = '<';
      destination[i] = '>';
    } 

  }
  destination[i] = '\0';
  return i;
}
*/
char *prittyString(char *str)
{
  char *ptr = str;
  char *end;
  bool lastspace=true;

  end = str + strlen(str) - 1;
  while(end >= ptr){
    //printf("%d %s ", *ptr, ptr);
    if (lastspace && *ptr > 96 && *ptr < 123) {
      *ptr = *ptr - 32;
      lastspace=false;
      //printf("to upper\n");
    } else if (lastspace == false && *ptr > 54 && *ptr < 91) {
      *ptr = *ptr + 32;
      lastspace=false;
      //printf("to lower\n");
    } else if (*ptr == 32) {
      lastspace=true;
      //printf("space\n");
    } else {
      lastspace=false;
      //printf("leave\n");
    }
    ptr++;
  }

  //printf("-- %s --\n", str);

  return str;
}



temperatureUOM getTemperatureUOM(const char *uom) {

  if (uom == NULL)
    return UNKNOWN;

  if (uom[0] == 194 && uom[1] == 176) { // Deg symbol is 2 chars '°'
    switch (uom[2]) {
      case 'F':
      case 'f':
        //printf("UOM %s is FAHRENHEIT\n", uom);
        return FAHRENHEIT;
      break;
      case 'C':
      case 'c':
         //printf("UOM %s is CELSIUS\n", uom);
        return CELSIUS;
      break;
    }
  }

  //printf("UOM %s is UNKNOWN\n", uom);
  return UNKNOWN;
}

bool isUomTemperature( const char *uom) {
  
  /*
  for (int i=0; i<strlen(uom); i++) {
    printf(" %d |",uom[i]);
  }
  printf("\n");
  */
  if (uom == NULL)
    return false;

  if (uom[0] == 194 && uom[1] == 176) { // Deg symbol is 2 chars '°' 
    //printf("UOM %s is temp\n", uom);
    return true;
  }

  if (strlen(uom) == 1) {
    switch (uom[0]) {
      case 'F':
      case 'f':
      case 'C':
      case 'c':
        //printf("UOM %s is temp\n", uom);
        return true;
      break;
    }
  }

  //printf("UOM %s is NOT temp %d %c\n", uom, uom[0], uom[0]);

  return false;
}
