/**
 *  GraphTFT plugin for the Video Disk Recorder 
 * 
 *  common.c - A plugin for the Video Disk Recorder
 *
 *  (c) 2004 Lars Tegeler, Sascha Volkenandt
 *  (c) 2006-2008 Jörg Wendel
 *
 * This code is distributed under the terms and conditions of the
 * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
 *
 * $Id: common.c,v 1.10 2007/12/03 19:58:20 root Exp $
 *
 **/

// includes

#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <syslog.h>
#include <sys/stat.h>
#include <regex.h>

#include "common.h"
#include "setup.h"

using std::string;

int logLevel = eloOff;
int logDevice = devSyslog;
cMutex logMutex;

//***************************************************************************
// Debug
//***************************************************************************

void tell(int eloquence, const char* format, ...)
{
   if (logLevel < eloquence)
      return ;

   const int sizeBuffer = 100000;
   char t[sizeBuffer+100];
   va_list ap;
   time_t  now;
   static FILE* lf = 0;

   cMutexLock lock(&logMutex);

   va_start(ap, format);

   switch (logDevice)
   {
      case devNone: break;

      case devStdOut:
      {
         vsnprintf(t + 21, sizeBuffer+21, format, ap);
         time(&now);
         strftime(t, sizeof t, "[graphTFT: %H:%M:%S]", localtime(&now));
         t[20] = ' ';
         printf("%s\033[K", t);

         break;
      }

      case devSyslog:
      {
         snprintf(t, sizeBuffer, "[graphTFT] ");
         vsnprintf(t+strlen(t), sizeBuffer-strlen(t), format, ap);

         syslog(LOG_DEBUG, "%s", t);

         break;
      }

      case devFile:
      {
         if (!lf)
            lf = fopen("/tmp/graphTFT.log", "a");

         if (lf) 
         {
            vsnprintf(t + 20, sizeBuffer+21, format, ap);
            time(&now);
            strftime(t, sizeof t, "%d.%m.%Y %H:%M:%S", localtime(&now));
            t[19] = ' ';
            fprintf(lf, "%s\n", t);
         }
            
         break;
      }
   }

   va_end(ap);
}

//***************************************************************************
// check if a file exists
//***************************************************************************

int fileExists(std::string filename) 
{
   struct stat file_stat;

   return (stat(filename.c_str(), &file_stat) == 0) ? true : false;
}

//***************************************************************************
// FIX ME: grrr, why somebody must habe an conversion, 
// some must not, and by some it wont work at all
//***************************************************************************

int isolat1ToUTF8(unsigned char* out, int *outlen, const unsigned char* in, int *inlen)
{
   unsigned char* outstart = out;
   const unsigned char* base = in;
   unsigned char* outend = out + *outlen;
   const unsigned char* inend;
   const unsigned char* instop;
   unsigned int c = *in;

   inend = in + (*inlen);
   instop = inend;
                   
   while (in < inend && out < outend - 1) 
   {
      if (c >= 0x80) 
      {
         *out++= ((c >>  6) & 0x1F) | 0xC0;
         *out++= (c & 0x3F) | 0x80;
         ++in;
         c = *in;
      }

      if (instop - in > outend - out) 
         instop = in + (outend - out); 

      while (c < 0x80 && in < instop) 
      {
         *out++ =  c;
         ++in;
         c = *in;
      }
   }  
 
   if (in < inend && out < outend && c < 0x80) 
   {
      *out++ =  c;
      ++in;
   }

   *outlen = out - outstart;
   *inlen = in - base;
   *out = 0;

   return success;
}

//***************************************************************************
// To UTF8
//***************************************************************************

int toUTF8(char* out, int outMax, const char* in)
{
   iconv_t cd;
   size_t ret;
   char* toPtr;
   char* fromPtr;
   size_t fromLen, outlen;

   const char* to_code = "UTF-8";
   const char* from_code = "ISO8859-1";

   if (!out || !in || !outMax)
      return fail;

   *out = 0;
   fromLen = strlen(in);
   
   if (!fromLen)
      return fail;
   
#if VDRVERSNUM >= 10509
   switch (I18nCurrentLanguage())
#else
   switch (Setup.OSDLanguage) 
#endif
   {
      case 11: from_code = "ISO8859-7";  break;
      case 13: 
      case 17: from_code = "ISO8859-2";  break;
      case 16: from_code = "ISO8859-5";  break;
      case 18: from_code = "ISO8859-15"; break;
      default: from_code = "ISO8859-1";  break;
	}

	cd = iconv_open(to_code, from_code);

   if (cd == (iconv_t)-1) 
      return fail;

   fromPtr = (char*)in;
   toPtr = out;
   outlen = outMax;

   ret = iconv(cd, &fromPtr, &fromLen, &toPtr, &outlen);

   *toPtr = 0;   
   iconv_close(cd);

   if (ret == (size_t)-1)
   {
      tell(0, "Converting [%s] from '%s' to '%s' failed", 
           fromPtr, from_code, to_code);

		return fail;
   }

   return success;
}

//***************************************************************************
// Left Trim
//***************************************************************************

char* Str::lTrim(char* buf)
{
   if (buf)
   {
      char *tp = buf;

      while (*tp && strchr("\n\r\t ",*tp)) 
         tp++;

      memmove(buf, tp, strlen(tp) +1);
   }
   
   return buf;
}

//*************************************************************************
// Right Trim
//*************************************************************************

char* Str::rTrim(char* buf)
{
   if (buf)
   {
      char *tp = buf + strlen(buf);

      while (tp >= buf && strchr("\n\r\t ",*tp)) 
         tp--;

      *(tp+1) = 0;
   }
   
   return buf;
}

//*************************************************************************
// All Trim
//*************************************************************************

char* Str::allTrim(char* buf)
{
   return lTrim(rTrim(buf));
}

//*************************************************************************
// Is Empyt
//*************************************************************************

int Str::isEmpty(const char* buf)
{
   if (buf && *buf)
      return no;

   return yes;
}

int Str::isBlank(const char* buf)
{
   int i = 0;

   while (buf[i])
   {
      if (buf[i] != ' ' && buf[i] != '\t')
         return no;

      i++;
   }

   return yes;
}

const char* Str::toStr(const char* s)
{
   static char* buf = 0;
   static unsigned int sizeBuf = 0;

   if (!s)
      return "";

   if (!buf || sizeBuf < strlen(s))
   {
      if (buf) free(buf);

      sizeBuf = strlen(s);
      buf = (char*)malloc(sizeBuf);
   }

   strcpy(buf, s);

   return buf;
}

const char* Str::toStr(bool value)
{
   static char buf[10+TB];

   sprintf(buf, "%s", value ? "1" : "0");

   return buf;
}

const char* Str::toStr(int value)
{
   static char buf[100+TB];

   sprintf(buf, "%d", value);

   return buf;
}

const char* Str::toStr(double value, int precision)
{
   static char buf[100+TB];

   sprintf(buf, "%.*f", precision, value);

   return buf;
}

//***************************************************************************
// Class LogDuration
//***************************************************************************

LogDuration::LogDuration(const char* aMessage, int aLogLevel)
{
   logLevel = aLogLevel;
   strcpy(message, aMessage);
   
   // at last !

   durationStart = cTimeMs::Now();
}

LogDuration::~LogDuration()
{
   tell(logLevel, "duration '%s' was (%dms)",
     message, cTimeMs::Now() - durationStart);
}

void LogDuration::show(const char* label)
{
   tell(logLevel, "elapsed '%s' at '%s' was (%dms)",
     message, label, cTimeMs::Now() - durationStart);
}

//**************************************************************************
//  Regular Expression Searching
//**************************************************************************

int rep(const char* string, const char* expression, Option options)
{
  const char* tmpA;
  const char* tmpB;

  return rep( string, expression, tmpA, tmpB, options);
}

int rep(const char* string, const char* expression, const char*& s_location, 
        Option options)
{
  const char* tmpA;

  return rep( string, expression, s_location, tmpA, options);
}


int rep(const char* string, const char* expression, const char*& s_location, 
        const char*& e_location, Option options)
{
   regex_t reg;
   regmatch_t rm;
   int status;
   int opt = 0;

   // Vorbereiten von reg fuer die Expressionsuche mit regexec
   // Flags:  REG_EXTENDED = Use Extended Regular Expressions
   //         REG_ICASE    = Ignore case in match.

   reg.re_nsub = 0;

   // Options umwandeln
   if (options & repUseRegularExpression)
     opt = opt | REG_EXTENDED;
   if (options & repIgnoreCase)
     opt = opt | REG_ICASE;
 
   if (regcomp( &reg, expression, opt) != 0)
     return fail;  

   // Suchen des ersten Vorkommens von reg in string

   status = regexec( &reg, string, 1, &rm, 0);
   regfree(&reg);

   if (status != 0) 
     return fail; 

   // Suche erfolgreich =>
   // Setzen der ermittelten Start- und Endpositionen

   s_location = (char*)(string + rm.rm_so);
   e_location = (char*)(string + rm.rm_eo);

   return success; 
}

