#ifndef CPPCMS_HTP_CONNECTION_SCGI_H
#define CPPCMS_HTP_CONNECTION_SCGI_H
#include "http_connection.h"
#include "asio_config.h"
namespace cppcms { namespace http {

template<typename Socket>
class scgi_connection : public connection {
	aio::streambuf buf_;
	std::vector<char> data_;
	std::map<std::string,std::string> env_;
	Socket socket_;

	void on_done(error_code const &e,boost::fuction<void(bool>) callback)
	{
		callback(bool(e));
	}
	int check_size(aio::buffer &buf)
	{
		std::istream s(&buf);
		size_t n;
		s>>n;
		if(s.failbit())
			return -1;
		return n;
	}
	bool parse_env(std::vector<char> const &s)
	{
		if(s.back()!=',')
			return false;
		std::vector<char>::const_iterator b,e,p;
		b=s.begin(); e=s.begin()+s.size()-1;
		while(b!=e) {
			p=std::find(b,e,0);
			if(p==e)
				return false;
			std::string key(b,p);
			b=p+1;
			p=std::find(b,e,0);
			if(p==e)
				return false;
			std::string val(b,p);
			b=p+1;
			env_.insert(std::make_pair(key,val));
		}
		if(env_.find("CONTENT_LENGTH")==env_.end())
			return false;
		return true;
	}
public:
	virtual bool keep_alive() { return false; }

	virtual size_t read(void *buffer,size_t s)
	{
		return aio::read(socket_,aio::buffer(buffer,s));
	}
	virtual size_t write(void const *buffer,size_t s)
	{
		return aio::write(socket_,aio::buffer(buffer,s));
	}
	virtual bool prepare()
	{
		try {
			aio::read_until(socket_,buf_,':');
			int n=check_size(buf);
			if(n<0)
				return false;
			data_.resize(n+1,0);
			if(aio::read(socket_,aio::buffer(data))!=n+1)
				return false;
			if(!parse_env(data))
				return false;

		}
		catch(std::exception const &e) {
			return false;
		}
		return true;
	}
	virtual aio::io_service &io_service()
	{
		return socket_.io_service();
	}
	virtual void async_read(void *buffer,size_t s,boost::function<void(bool)> c)
	{
		aio::async_read(socket_,aio::buffer(buffer,s),
			boost::bind(&scgi_connection<Socket>::on_done,shared_from_this(),aio::placeholders::error,c));
	}
	virtual void async_write(void const *buffer,size_t s,boost::function<void(bool)> c)
	{
		aio::async_write(socket_,aio::buffer(buffer,s),
			boost::bind(&scgi_connection<Socket>::on_done,shared_from_this(),aio::placeholders::error,c));
	}
	virtual void async_prepare(boost::function<void(bool)> c)
	{
		aio::async_read_until(socket_,buf_,':',
			boost::bind(&scgi_connection<Socket>::on_net_read,shared_from_this(),aio::placeholders::error,c));
	}
private:
	void on_net_read(error_code const &e,boost::function<void(bool)> c)
	{
		if(e) {
			c(false);
			return;
		}
		int n=check_size(buf);
		if(n<0) {
			c(false) ;
			return;
		}
		data_.resize(n+1,0);
		aio::async_read(socket_,aio::buffer(data_),
			boost::bind(&scgi_connection<Socket>::on_env_read,aio::placeholders::error,c));
	}
	void on_env_read(error_code const &e,boost::function<void(bool)>)
	{
		if(e) {
			c(false);
			return;
		}
		if(!parse_env(data)) {
			c(false);
			return;
		}
		c(true);
	}
public:
	virtual std::string getenv(std::string const &key)
	{
		std::map<std::string,std::string>::iterator p;
		p=env_.find(key);
		if(p==env_.end())
			return std::string();
		return p->second;
	}

}; // connection_scgi

} } 
#endif
