#ifndef _COOKIE_H
#define _COOKIE_H

#ifdef __WIN32__
#define COOKIE_WIN32
#else
#define COOKIE_UNIX
#endif

#include <ctime>
#include <cstdlib>
#include <cstdio>
#include <cstring>

#ifdef COOKIE_UNIX
#include <list>
using namespace std;
#endif

#ifdef COOKIE_WIN32
#include <list>
using namespace std;
#endif

inline int urlncode(char *src, char *dst, int size) {
   const char *ENCLIST = "!\"#$%&'(),:;<=>?{\\}~";
   const char *CONVERT = "0123456789ABCDEF";
   char *s,*t;
   int  used;
   int  n;
   int  a;

   t=dst;
   used=0;

   if(!dst)
      return 0;

   for(s=src;*s;s++) {
      if(used+1 >= size) {
         *t='\0';
         return 0;
      }

      if( *s==' ') {
         *t++ = '+';
         used++;
      }
      else if(strchr(ENCLIST,*s)) {
         if ( used+4 >= size ) {
            *t='\0';
            return 0;
         }

         *t++ = '%';
         a=0;
         n=(int)*s;
         while(n >= 16) {
            n -= 16;
            a++;
         }

         *t++ = CONVERT[a];
         *t++ = CONVERT[n];

         used += 3;
      }
      else {
         *t++ = *s;
         used++;
      }
   }

   *t='\0';
   return 1;
}

///////////////////////////////////
// Simple HTTP cooke utils.
// 
// Author : Matthew W. Coan
///////////////////////////////////
class Cookie {
   char * _name;
   char * _value;
   char * _domain;
   char * _path;
   bool _secure;
   time_t _maxAge;

public:
   Cookie() 
   : _name(0),
     _value(0),
     _domain(0),
     _path(0),
     _secure(false),
     _maxAge(-1)
   { }

   Cookie(char * name,     // Needed
          char * value,    // Needed
          char * domain,
          char * path,
          bool secure,
          time_t maxAge) 
   : _name(name),
     _value(value),
     _domain(domain),
     _path(path),
     _secure(secure),
     _maxAge(maxAge)
   { }

   ~Cookie() {
      if(_name)
         delete [] _name;
      if(_value)
         delete [] _value;
      if(_domain)
         delete [] _domain;
      if(_path)
         delete [] _path;
   }

   char * name() { return _name; }
   char * value() { return _value; }
   char * domain() { return _domain; }
   char * path() { return _path; }
   bool secure() { return _secure; }
   time_t maxAge() { return _maxAge; }

   void setCookie() {
      fprintf(stdout, "Set-Cookie: ");
      size_t size = strlen(_name);
      size_t encSize = size * 3;
      char * urlVal = new char[encSize+1];
      memset(urlVal, 0, encSize+1);
      urlncode(_name, urlVal, encSize);
      size = strlen(_value);
      encSize = size * 3;
      char * urlVal2 = new char[encSize+1];
      memset(urlVal2, 0, encSize+1);
      urlncode(_value, urlVal2, encSize);
      fprintf(stdout, "%s=%s", urlVal, urlVal2);
      delete [] urlVal;
      delete [] urlVal2;
      if(_domain)
         fprintf(stdout, "; domain=%s", _domain);
      if(_maxAge != -1) {
         // Compute date string
         struct tm * pTM = gmtime(&_maxAge);
         const size_t MAX = 1024;
         char buffer[MAX];
         memset(buffer, 0, MAX);
         strftime(buffer, MAX-1, "%a, %d-%b-%Y %H:%M:%S %Z", pTM);
         fprintf(stdout, "; expires=%s", buffer);
      }
      if(_path)
         fprintf(stdout, "; path=%s", _path);
      if(_secure)
         fprintf(stdout, "; secure");
      fprintf(stdout, "\r\n");
      fflush(stdout);
   }
};

typedef list< Cookie * > CookieListType;

void unescape_url(char *url) {
   size_t i,j; 
   for(i = 0,j=0; url[i]; i++,j++) {
      if((url[i]=url[j]) == '%') {
         url[i] = (url[j+1] >= 'A' ? ((url[j+1] & 0xdf) - 'A') + 10 : (url[j+1] - '0'));
         url[i] *= 16;
         url[i] += (url[j+2] >= 'A' ? ((url[j+2] & 0xdf) - 'A') + 10 : (url[j+2] - '0'));
         j += 2;
      }
      else if (url[i] == '+') {
         url[i] = ' ';
      }
   }
   url[i] = '\0';
}

char * _strdup(const char * ptr) {
   if(!ptr)
      return 0;
   size_t sz = strlen(ptr);
   char * ret = new char[sz+1];
   memset(ret, 0, sz+1);
   strncpy(ret, ptr, sz);
   return ret;
}

inline CookieListType * getCookieList() {
   char * cookieString = getenv("HTTP_COOKIE");
   if(cookieString == NULL)
      return 0;
   CookieListType * pList = new CookieListType();
   char * name = strtok(cookieString, ";");
   Cookie * pCookie;
   char * value;

   while(name != NULL) {
      value = strchr(name, '=');
      if(value != NULL) {
         *value = '\0';
         value++;
         name = _strdup(name);
         unescape_url(name);
         value = _strdup(value);
         unescape_url(value);
         pCookie = new Cookie(name, value, 0, 0, false, -1);
         pList->push_back(pCookie);
      }
      name = strtok(NULL, ";");
   }
   return pList;
}

inline void deleteCookieList(CookieListType * pList) {
   for(CookieListType::iterator ptr = pList->begin(); 
       ptr != pList->end(); ptr++) {
      Cookie * & pCookie = (*ptr);
      delete pCookie;
   }
   delete pList;
}

#endif /* _COOKIE_H */

