///////////////////////////////////////////////////////////////////////////////
//                                                                             
//  Copyright (C) 2008-2012  Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>     
//                                                                             
//  See accompanying file COPYING.TXT file for licensing details.
//
///////////////////////////////////////////////////////////////////////////////
#define CPPCMS_SOURCE
#include <cppcms/http_content_type.h>
#include "http_protocol.h"
#include <algorithm>
#include <string.h>

namespace cppcms { namespace http {
	struct content_type::data {
		std::string type;
		std::string subtype;
		std::string media_type;
		std::map<std::string,std::string> parameters;
	};
	content_type::content_type()
	{
	}
	content_type::content_type(content_type const &other) :
		d(other.d)
	{
	}
	content_type::~content_type()
	{
	}
	content_type const &content_type::operator=(content_type const &other)
	{
		d=other.d;
		return *this;
	}
	std::string content_type::type() const
	{
		if(d)
			return d->type;
		return std::string();
	}
	std::string content_type::subtype() const
	{
		if(d)
			return d->subtype;
		return std::string();
	}
	std::string content_type::media_type() const
	{
		if(d)
			return d->media_type;
		return std::string();
	}
	bool content_type::is_form_urlencoded() const
	{
		if(d)
			return d->media_type == "application/x-www-form-urlencoded";
		return false;
	}
	bool content_type::is_multipart_form_data() const
	{
		if(d)
			return d->media_type == "multipart/form-data";
		return false;
	}
	std::string content_type::charset() const
	{
		return parameter_by_key("charset");
	}
	std::map<std::string,std::string> content_type::parameters() const
	{
		if(d) {
			return d->parameters;
		}
		return std::map<std::string,std::string>();
	}
	bool content_type::parameter_is_set(std::string const &key) const
	{
		if(!d)
			return false;
		if(d->parameters.find(key)==d->parameters.end())
			return false;
		return true;
	}
	std::string content_type::parameter_by_key(std::string const &key) const
	{
		if(!d)
			return std::string();
		std::map<std::string,std::string>::const_iterator p;
		p = d->parameters.find(key);
		if( p == d->parameters.end())
			return std::string();
		return p->second;
	}
	void content_type::parse(char const *begin,char const *end)
	{
		begin = protocol::skip_ws(begin,end);	
		if(begin == end)
			return;
		char const *type_end = protocol::tocken(begin,end);
		if(type_end == begin)
			return;
		std::string type(begin,type_end);
		begin = type_end;
		if(begin == end || *begin++ != '/')
			return;
		type_end = protocol::tocken(begin,end);
		if(type_end == begin)
			return;
		std::string subtype(begin,type_end);
		begin = type_end;
		std::transform(type.begin(),type.end(),type.begin(),protocol::ascii_to_lower);
		std::transform(subtype.begin(),subtype.end(),subtype.begin(),protocol::ascii_to_lower);
		d->type = type;
		d->subtype = subtype;
		d->media_type = type + "/" + subtype;
		while(begin!=end) {
			begin = protocol::skip_ws(begin,end);
			if(begin == end || *begin++ != ';' || (begin=protocol::skip_ws(begin,end))==end)
				return;
			char const *param_end = protocol::tocken(begin,end);
			if(param_end == begin)
				return;
			std::string param(begin,param_end);
			std::transform(param.begin(),param.end(),param.begin(),protocol::ascii_to_lower);
			begin = protocol::skip_ws(param_end,end);
			if(begin==end || *begin++!='=')
				return;
			begin = protocol::skip_ws(begin,end);
			if(begin==end)
				return;
			std::string value;
			if(*begin == '"') {
				char const *tmp = begin;
				value = protocol::unquote(tmp,end);
				if(tmp == begin)
					return;
				begin = tmp;
			}
			else {
				char const *tmp = protocol::tocken(begin,end);
				if(tmp == begin)
					return;
				value.assign(begin,tmp);
				begin = tmp;
			}
			d->parameters.insert(std::make_pair(param,value));
		}
	}
	content_type::content_type(char const *begin,char const *end) :
		d(new content_type::data())
	{
		parse(begin,end);
	}
	content_type::content_type(char const *s) :
		d(new content_type::data())
	{
		char const *begin = s;
		char const *end = s + strlen(s);
		parse(begin,end);
	}
	content_type::content_type(std::string const &ct) :
		d(new content_type::data())
	{
		char const *begin = ct.c_str();
		char const *end = begin + ct.size();
		parse(begin,end);
	}
} // http
} // cppcms
