#ifndef _AmMimeBody_h_
#define _AmMimeBody_h_

#include "AmArg.h"

#include <string>
#include <list>
#include <map>

using std::string;
using std::list;
using std::map;

struct AmContentType
{
  struct Param
  {

    enum Type {
      UNPARSED=0,
      BOUNDARY,
      OTHER
    };

    Type   type;
    string name;
    string value;

    Param(const string& name, const string& value)
      : type(UNPARSED), name(name), value(value) {}

    int parseType();
  };

  typedef list<Param*> Params;

  string type;
  string subtype;
  Params params;
  Param* mp_boundary;

  AmContentType();
  AmContentType(const AmContentType& ct);
  ~AmContentType();

  const AmContentType& operator = (const AmContentType& r_ct);

  int  parse(const string& ct);
  int  parseParams(const char* c, const char* end);

  void setType(const string& t);
  void setSubType(const string& st);

  bool isType(const string& t) const;
  bool isSubType(const string& st) const;
  bool hasContentType(const string& content_type) const;

  /** get content-type without any parameters */
  string getStr() const;

  /** get content-type with parameters */
  string getHdr() const;

  /** Clear and free param list */
  void clearParams();

  /** set a random boundary string */
  void resetBoundary();
};

class AmMimeBody
: public AmObject
{
public:
  typedef list<AmMimeBody*>  Parts;

private:
  AmContentType  ct;
  string         hdrs;
  unsigned int   content_len;
  unsigned char* payload;
  
  Parts parts;

  void clearParts();
  void clearPart(Parts::iterator position);
  void clearPayload();

  int parseMultipart(const unsigned char* buf, unsigned int len);
  int findNextBoundary(unsigned char** beg, unsigned char** end);
  int parseSinglePart(unsigned char* buf, unsigned int len);

  void convertToMultipart();
  void convertToSinglepart();

public:
  /** Empty constructor */
  AmMimeBody();

  /** Deep-copy constructor */
  AmMimeBody(const AmMimeBody& body);

  /** Destuctor */
  ~AmMimeBody();

  /** Deep copy operator */
  const AmMimeBody& operator = (const AmMimeBody& r_body);

  /** Parse a body (single & multi-part) */
  int  parse(const string& content_type, 
	     const unsigned char* buf, 
	     unsigned int len);

  /** Set the payload of this body */
  void setPayload(const unsigned char* buf, unsigned int len);

  /** Set part headers (intended for sub-parts)*/
  void setHeaders(const string& hdrs);

  /** 
   * Add a new part to this body, possibly
   * converting to multi-part if necessary.
   * @return a pointer to the new empty part.
   */
  AmMimeBody* addPart(const string& content_type);

  /** 
   * Delete a body part, converting resulting body to single-part if necessary.
   */
  int deletePart(const string& content_type);

  /** Get content-type without any parameters */
  string getCTStr() const { return ct.getStr(); }

  /** Get content-type with parameters */
  string getCTHdr() const { return ct.getHdr(); }
  
  /** @return the list of sub-parts */
  const Parts& getParts() const { return parts; }

  /** @return the sub-part headers */
  const string& getHeaders() const { return hdrs; }

  /**
   * @return a pointer to the payload of this part.
   *         in case of multi-part, NULL is returned.
   */
  const unsigned char* getPayload() const { return payload; }

  /**
   * @return the payload length of this part.
   *         in case of multi-part, 0 is returned.
   */
  unsigned int   getLen() const { return content_len; }

  /** @return true if no payload assigned and no sub-parts available */
  bool empty() const;

  /** @return true if this part has the provided content-type */
  bool isContentType(const string& content_type) const;

  /** 
   * @return a pointer to a part of the coresponding 
   *         content-type (if available).
   *         This could be a pointer to this body.
   */
  AmMimeBody* hasContentType(const string& content_type);

  /** 
   * @return a const pointer to a part of the coresponding 
   *         content-type (if available).
   *         This could be a pointer to this body.
   */
  const AmMimeBody* hasContentType(const string& content_type) const;

  /**
   * Print the body including sub-parts suitable for sending
   * within the body of a SIP message.
   */
  void print(string& buf) const;

  const AmContentType &getContentType() { return ct; }
  void setContentType(const AmContentType &_ct) { ct = _ct; }
  void addPart(const AmMimeBody &part) { parts.push_back(new AmMimeBody(part)); }
};

#endif
