#ifndef CGI_POST_H
#define CGI_POST_H

//
// cgi_post is a simple class that converts CGI Post data on an istream
// into a C++ map.
//

#include <stdexcept>
#include <map>
#include <vector>
#include <string>
#include <iostream>

using namespace std;

namespace {

  // Remove trailing "delim" character.
  void chomp(string& str, char delim = '\n') {
    string::size_type delimPoint = str.rfind(delim);
    if( delimPoint != str.npos ) {
      str = str.substr(0, delimPoint);
    }
  }

  // Split() "str" at all "delim" characters and return results in a vector.
  vector<string> split(string str, char delim)
    {
      vector<string> results;
      string::size_type splitPoint = 0;
      string::size_type startOfNextString = 0;
      while( (splitPoint = str.find(delim, startOfNextString)) != str.npos ) {
        results.push_back(string(&str[startOfNextString], &str[splitPoint]));
        startOfNextString = splitPoint + 1;
      }
      results.push_back(string(&str[startOfNextString], &str[str.length()]));
      return results;
    }

  // Convert a hex character to an unsigned char.
  unsigned char hex_char_to_uchar(char ch)
    {
      if( ch >= '0' && ch <= '9' ) {
        return ch - '0';
      }
      if( ch >= 'a' && ch <= 'f' ) {
        return ch - 'a' + 10;
      }
      if( ch >= 'A' && ch <= 'F' ) {
        return ch - 'A' + 10;
      }
      throw invalid_argument("hex_char_to_uchar: invalid input");
    }

  // Convert a string representation of a hex value to an unsigned char.
  unsigned char hex_str_to_uchar(const string& str)
    {
      if( str.length() != 2 ) {
        throw invalid_argument("hex_str_to_uchar: Can only convert 2 digits "
                               "to an unsigned char.");
      }
      return hex_char_to_uchar(str[0]) * (unsigned char)16
             + hex_char_to_uchar(str[1]);
    }

  // URLs are sent across with a possibly large number of escape or control
  // sequences.  As the returned values are embedded in the URL, we need to
  // unescape the return values.
  string unescape(const string& str)
    {
      string rv;
      string::size_type len = str.length();
      for( unsigned i = 0 ; i < len ; ++i ) {
        switch( str[i] ) {
        case '+':
          rv += ' ';
          break;
        case '%': {
          // Hex escape sequences always come across as a percent sign
          // followed by two hex digits.
          ++i; // Consume the '%'.
          if( i + 1 < len ) {
            rv += hex_str_to_uchar(str.substr(i, 2));
            ++i; // Consume the first hex digit. The second hex digit
                 // will be consumed in the for loop.
          } else {
            throw invalid_argument("unescape: hex digits should come in pairs");
          }
        break;
        }
        default:
          rv += str[i];
          break;
        }
      }
      return rv;
    }
};

class cgi_post : public map<string, string>{
 public:

  typedef map<string,string>::pointer pointer;
  typedef map<string,string>::const_pointer const_pointer;
  typedef map<string,string>::reference reference;
  typedef map<string,string>::const_reference const_reference;
  typedef map<string,string>::iterator iterator;
  typedef map<string,string>::const_iterator const_iterator;
  typedef map<string,string>::reverse_iterator reverse_iterator;

  typedef map<string,string>::const_reverse_iterator
                                       const_reverse_iterator;

  typedef map<string,string>::size_type size_type;
  typedef map<string,string>::difference_type difference_type;
  //typedef map<string,string>::allocator_type allocator_type;

  cgi_post(istream& istrm = cin) {
    string value;
    while( getline(istrm, value, '&') ) {
      chomp(value);
      vector<string> elements = split(value, '=');
      if( elements.size() == 0 ) {
        throw invalid_argument("cgi_post: Unable to split value");
      } else if( elements.size() == 1 ) {
        throw invalid_argument("cgi_post: Too few elements after split");
      } else if( elements.size() > 2 ) {
        throw invalid_argument("cgi_post: Too many elements after split");
      }
      (*this)[elements[0]] = unescape(elements[1]);
    }
  }

 protected:
 private:
};

#endif  /*  CGI_POST_H  */

