///////////////////////////////////////////////////////////////////////////////
//                                                                             
//  Copyright (C) 2008-2012  Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>     
//                                                                             
//  See accompanying file COPYING.TXT file for licensing details.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef CPPCMS_IMPL_CGI_API_H
#define CPPCMS_IMPL_CGI_API_H

#include <booster/noncopyable.h>
#include <booster/shared_ptr.h>
#include <booster/enable_shared_from_this.h>
#include <booster/aio/buffer.h>
#include <vector>
#include <map>
#include <booster/callback.h>
#include <booster/system_error.h>
#include <cppcms/http_context.h>

#include <cppcms/defs.h>
#include <cppcms/config.h>
#include "string_map.h"

namespace booster {
	namespace aio { 
		class io_service;
		class acceptor;
		class stream_socket;
	}
}


namespace cppcms {
	class service;
	class application;
	namespace http {
		class context;
		class request;
		class response;
	}


namespace impl {
	class multipart_parser;
namespace cgi {

	typedef booster::callback<void(booster::system::error_code const &e)> handler;
	typedef booster::callback<void(booster::system::error_code const &e,size_t)> io_handler;
	typedef booster::callback<void()> callback;
	typedef cppcms::http::context::handler ehandler;

	class connection;
	class acceptor : public booster::noncopyable {
	public:
		virtual void async_accept() = 0;
		virtual booster::aio::acceptor &socket() = 0;
		#ifndef CPPCMS_WIN32
		virtual booster::shared_ptr<cppcms::http::context> accept(int fd) = 0;
		#endif
		virtual void sndbuf(int v) = 0;
		virtual void rcvbuf(int v) = 0;
		virtual void stop() = 0;
		virtual ~acceptor(){}
	};

	class CPPCMS_API connection : 
		public booster::noncopyable,
		public booster::enable_shared_from_this<connection>
	{
	public:
		connection(cppcms::service &srv);
		virtual ~connection();
		cppcms::service &service();
	
		void async_prepare_request(	http::context *context,
						ehandler const &on_post_data_ready);

		void async_write_response(	http::response &response,
						bool complete_response,
						ehandler const &on_response_written);
		
		void aync_wait_for_close_by_peer(callback const &on_eof);

		std::string getenv(std::string const &key)
		{
			return env_.get_safe(key.c_str());
		}
		char const *cgetenv(char const *key)
		{
			return env_.get_safe(key);
		}
		std::string getenv(char const *key) 
		{
			return env_.get_safe(key);
		}
		virtual std::map<std::string,std::string> const &getenv()
		{
			if(map_env_.empty() && env_.begin()!=env_.end()) {
				for(string_map::iterator p=env_.begin();p!=env_.end();++p) {
					map_env_[p->key]=p->value;
				}
			}
			return map_env_;
		}
		bool is_reuseable();
	
		std::string last_error();
	

		/****************************************************************************/

		// These are abstract member function that should be implemented by
		// actual protocol like FCGI, SCGI, HTTP or CGI
	public:
		bool has_pending();
		virtual bool nonblocking_write(booster::aio::const_buffer const &buf,bool eof,booster::system::error_code &e);
		virtual void async_write(booster::aio::const_buffer const &buf,bool eof,handler const &h);
		virtual bool write(booster::aio::const_buffer const &buf,bool eof,booster::system::error_code &e);

		virtual void on_async_write_start() = 0;
		virtual void on_async_write_progress(bool completed) = 0;
		virtual void do_eof() = 0;
		virtual booster::aio::const_buffer format_output(booster::aio::const_buffer const &in,bool completed,booster::system::error_code &e) = 0;
		virtual bool write_to_socket(booster::aio::const_buffer const &in,booster::system::error_code &e);
		virtual booster::aio::io_service &get_io_service() = 0;
	protected:
		void append_pending(booster::aio::const_buffer const &new_data);

		virtual booster::aio::stream_socket &socket() = 0;


		virtual void async_read_headers(handler const &h) = 0;
		virtual bool keep_alive() = 0;

		// Concept implementation headers		
		
		virtual void async_read_some(void *,size_t,io_handler const &h) = 0;
		virtual void on_async_read_complete() {}
		virtual void async_read_eof(callback const &h) = 0;

		/****************************************************************************/

	protected:
		
		string_pool pool_;
		string_map env_;
		std::vector<char> pending_output_;

		booster::shared_ptr<connection> self();
		void async_read(void *,size_t,io_handler const &h);
	private:

		struct reader;
		struct cgi_forwarder;
		struct async_write_binder;
		struct async_write_handler;

		friend struct reader;
		friend struct writer;
		friend struct async_write_binder;
		friend struct async_write_handler;
		friend struct cgi_forwarder;

		void set_error(ehandler const &h,std::string s);
		void on_headers_read(booster::system::error_code const &e,http::context *,ehandler const &h);
		void load_content(http::context *,ehandler const &h);
		void on_some_content_read(booster::system::error_code const &e,size_t n,http::context *,ehandler const &h);
		void handle_eof(callback const &on_eof);
		void handle_http_error(int code,http::context *context,ehandler const &h);
		void handle_http_error_eof(booster::system::error_code const &e,int code,ehandler const &h); 

		cppcms::service *service_;
		std::string async_chunk_;
		std::string error_;
		bool request_in_progress_;

		std::map<std::string,std::string> map_env_;

		booster::intrusive_ptr<async_write_binder> cached_async_write_binder_;

	};


} // cgi
} // impl
} // cppcms

#endif
