#ifndef RFC2047_CPP
#define RFC2047_CPP

#ifdef ESP32

#include "RFC2047.h"

RFC2047::RFC2047(){}


void RFC2047::rfc2047Decode(char *d, const char *s, size_t dlen){

  const char *p, *q;
  size_t n;
  int found_encoded = 0;

  dlen--; /* save room for the terminal nul */

  while (*s && dlen > 0)
  {
    if ((p = strstr (s, "=?")) == NULL ||
	(q = strchr (p + 2, '?')) == NULL ||
	(q = strchr (q + 1, '?')) == NULL ||
	(q = strstr (q + 1, "?=")) == NULL)
    {
      /* no encoded words */
      if (d != s)
	strfcpy (d, s, dlen + 1);
      return;
    }

    if (p != s)
    {
      n = (size_t) (p - s);
      /* ignore spaces between encoded words */
      if (!found_encoded || strspn (s, " \t\r\n") != n)
      {
	if (n > dlen)
	  n = dlen;
	if (d != s)
	  memcpy (d, s, n);
	d += n;
	dlen -= n;
      }
    }

    rfc2047DecodeWord (d, p, dlen);
    found_encoded = 1;
    s = q + 2;
    n = strlen (d);
    dlen -= n;
    d += n;
  }
  *d = 0;

}

void RFC2047::rfc2047DecodeWord(char *d, const char *s, size_t dlen){
  
  char *p = safe_strdup (s);
  char *pp = p;
  char *pd = d;
  size_t len = dlen;
  int enc = 0, filter = 0, count = 0, c1, c2, c3, c4;

  while ((pp = strtok (pp, "?")) != NULL)
  {
    count++;
    switch (count)
    {
      case 2:
	if (strcasecmp (pp, Charset) != 0)
	{
	  filter = 1;
	}
	break;
      case 3:
	if (toupper (*pp) == 'Q')
	  enc = ENCQUOTEDPRINTABLE;
	else if (toupper (*pp) == 'B')
	  enc = ENCBASE64;
	else
	  return;
	break;
      case 4:
	if (enc == ENCQUOTEDPRINTABLE)
	{
	  while (*pp && len > 0)
	  {
	    if (*pp == '_')
	    {
	      *pd++ = ' ';
	      len--;
	    }
	    else if (*pp == '=')
	    {
	      *pd++ = (hexval(pp[1]) << 4) | hexval(pp[2]);
	      len--;
	      pp += 2;
	    }
	    else
	    {
	      *pd++ = *pp;
	      len--;
	    }
	    pp++;
	  }
	  *pd = 0;
	}
	else if (enc == ENCBASE64)
	{
	  while (*pp && len > 0)
	  {
	    c1 = base64val(pp[0]);
	    c2 = base64val(pp[1]);
	    *pd++ = (c1 << 2) | ((c2 >> 4) & 0x3);
	    if (--len == 0) break;
	    
	    if (pp[2] == '=') break;

	    c3 = base64val(pp[2]);
	    *pd++ = ((c2 & 0xf) << 4) | ((c3 >> 2) & 0xf);
	    if (--len == 0)
	      break;

	    if (pp[3] == '=')
	      break;

	    c4 = base64val(pp[3]);
	    *pd++ = ((c3 & 0x3) << 6) | c4;
	    if (--len == 0)
	      break;

	    pp += 4;
	  }
	  *pd = 0;
	}
	break;
    }
    pp = 0;
  }
  safe_free (&p);
  if (filter)
  {

      pd = d;
      while (*pd)
      {
	if (!IsPrint (*pd))
	  *pd = '?';
	pd++;
      }
  }
  return;
}


void *RFC2047::safe_calloc (size_t nmemb, size_t size)
{
  void *p;

  if (!nmemb || !size)
    return NULL;
  if (!(p = calloc (nmemb, size)))
  {
    //out of memory
    return NULL;
  }
  return p;
}

void *RFC2047::safe_malloc (unsigned int siz)
{
  void *p;

  if (siz == 0)
    return 0;
  if ((p = (void *) malloc (siz)) == 0)
  {
    //out of memory
    return NULL;
  }
  return (p);
}

void RFC2047::safe_realloc (void **p, size_t siz)
{
  void *r;

  if (siz == 0)
  {
    if (*p)
    {
      free (*p);
      *p = NULL;
    }
    return;
  }

  if (*p)
    r = (void *) realloc (*p, siz);
  else
  {
    r = (void *) malloc (siz);
  }

  if (!r)
  {
    //out of memory
    return;
  }

  *p = r;
}

void RFC2047::safe_free (void *ptr)
{ 
  void **p = (void **)ptr;
  if (*p)
  { 
    free (*p);
    *p = 0;
  }
}

char *RFC2047::safe_strdup (const char *s)
{
  char *p;
  size_t l;

  if (!s || !*s) return 0;
  l = strlen (s) + 1;
  p = (char *)safe_malloc (l);
  memcpy (p, s, l);
  return (p);
}

#endif //ESP32

#endif //RFC2047_CPP