#pragma once
#include "../acl_cpp_define.hpp"
#include "HttpServlet.hpp"

#ifndef ACL_CLIENT_ONLY

namespace acl
{

class websocket;
class session;
class HttpServletRequest;
class HttpServletResponse;

class ACL_CPP_API WebSocketServlet : public HttpServlet
{
public:
	WebSocketServlet(void);

	/**
	 * 构造函数
	 * @param stream {socket_stream*} 当在 acl_master 服务器框架控制下
	 *  运行时，该参数必须非空；当在 apache 下以 CGI 方式运行时，该参数
	 *  设为 NULL；另外，该函数内部不会关闭流连接，应用应自行处理流对象
	 *  的关闭情况，这样可以方便与 acl_master 架构结合
	 * @param session {session*} 每一个 HttpServlet 对象一个 session 对象
	 */
	WebSocketServlet(socket_stream* stream, session* session);

	/**
	 * 构造函数
	 * @param stream {socket_stream*} 当在 acl_master 服务器框架控制下
	 *  运行时，该参数必须非空；当在 apache 下以 CGI 方式运行时，该参数
	 *  设为 NULL；另外，该函数内部不会关闭流连接，应用应自行处理流对象
	 *  的关闭情况，这样可以方便与 acl_master 架构结合
	 * @param memcache_addr {const char*}
	 */
	WebSocketServlet(socket_stream* stream,
		const char* memcache_addr = "127.0.0.1:11211");

	/**
	 * HttpServlet 对象开始运行，接收 HTTP 请求，并回调以下 doXXX 虚函数，
	 * 该函数首先会调用 start 过程，然后根据 start 的返回结果及请求/响应
	 * 对象是否要求保持长连接来决定是否需要与客户端保持长连接.
	 */

	virtual ~WebSocketServlet(void);

	// @override
	bool doRun(void);

	// @override
	bool doRun(session& session, socket_stream* stream = NULL);

	// @override
	bool doRun(const char* memcached_addr, socket_stream* stream);

	/**
	 * 发送二进制数据.
	 * @param buf {const char *} 发送的数据
	 * @param len {int} buf 数据长度
	 * @return {bool} 错误 false, 否则 true
	 */
	bool sendBinary(const char *buf, int len);

	/**
	 * 发送文本数据.
	 * @param text {const char *} 发送的数据
	 * @return {bool} 错误 false, 否则 true
	 */
	bool sendText(const char *text);

	/**
	 * 发送pong 消息.
	 * @param buffer {const char *} 发送的数据
	 * @return {bool} 错误 false, 否则 true
	 */
	bool sendPong(const char *buffer = NULL);

	/**
	 * 发送pong 消息.
	 * @param buffer {const char *} 发送的数据
	 * @return {bool} 错误 false, 否则 true
	 */
	bool sendPing(const char *buffer = NULL);

protected:
	/**
	 * websocket 关闭消息回调
	 */
	virtual void onClose(void) {}

	/**
	 * websocket ping 消息回调.
	 * @param payload_len {unsigned long long} 消息数据总长度
	 * @param finish {bool} 本数据包是否最后一个
	 * @return {bool} false 断开连接。
	 */
	virtual bool onPing(unsigned long long payload_len, bool finish) = 0;

	/**
	 * websocket pong 消息回调.
	 * @param payload_len {unsigned long long} 消息数据总长度
	 * @param finish {bool} 本数据包是否最后一个
	 * @return {bool} false 断开连接。
	 */
	virtual bool onPong(unsigned long long payload_len, bool finish) = 0;

	/**
	 * websocket ping 消息回调.
	 * @param payload_len {unsigned long long} 消息数据总长度
	 * @param text {bool } true 表示为文本数据, 否则是 二进制数据。
	 * @param finish {bool} 本数据包是否最后一个
	 * @return {bool} false 断开连接。
	 */
	virtual bool onMessage(unsigned long long payload_len,
			bool text, bool finish) = 0;

	/**
	 * 子类可以循环调用此方法获得数据帧的数据体，直至返回 <= 0 为止
	 * @param buf {size_t*} 数据缓冲区用来存放结果数据
	 * @param size {size_t} buf 缓冲区大小
	 * @return {int} 读到的数据长度，分以下三种情形：
	 *   0: 表示数据帧正常读完
	 *  -1: 表示读出错
	 *  >0: 表示读到的数据，应再次调用本方法以便读余下的数据
	 */
	int readPayload(void* buf, size_t size);

	/**
	 * 返回 websocket 对象，如果返回 NULL 表示还未建立 websocket 连接
	 * @return {websocket*}
	 */
	websocket* get_websocket(void) const
	{
		return ws_;
	}

private:
	// @override
	bool doWebSocket(HttpServletRequest&, HttpServletResponse&);

private:
	websocket *ws_;
	int   opcode_;
};

} // namespace acl

#endif // ACL_CLIENT_ONLY
