/**
* @brief
websocket客户端类

* @version
  V 1.0.0

* @author

* @date
  创建时间:  2018-08-14\n

* @note
  2018-08-14  创建类。\n

* @warning

* @bug
  
*/

#pragma once

#include <string>
#include <mutex>
#include <condition_variable>
#include <map>
#include <atomic>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/prettywriter.h>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include "sio_client.h"
#include "jsonBuilder.h"
#include "sio_message.h"

namespace sys
{
	//消息处理函数类型
	typedef boost::function<void( int, std::string const&, sio::message::ptr const&, bool, sio::message::list & )> MSG_HANDLE_FUNC_TYPE;

	class wsClient
	{
	public:
		std::map<std::string, MSG_HANDLE_FUNC_TYPE> __MsgFuncList;//消息处理函数列表(key是命令,value是处理函数)
	private:
		sio::client __wsclient;//websocket客户端
		int __ID;//唯一标识
		std::string __uri;//连接字符串
		bool __Connected;//是否连接上服务器端
		bool __SktOpened;//Socket是否已打开
		bool __Logined;//是否已登录成功
		std::condition_variable_any __cond;//条件变量
		std::mutex __lock;//锁(配合__cond)
		std::string __LastError;//最新错误
		std::recursive_mutex __send_lock;//发送锁
		jsonBuilder __jsBuilder;//json构造器
		std::atomic<unsigned int> __connet_time;		//连接时间
		std::atomic<unsigned int> __recv_ping_time;	//接受ping消息的时间
	protected:
		/**
		* @brief		重置状态函数。
		*/
		void _reset();

		/**
		* @brief		连接成功回调函数。
		*/
		void _on_connected();

		/**
		* @brief	连接关闭回调函数。
		* @param  [in] sio::client::close_reason const& reason 关闭的原因\n
		*/
		void _on_close( sio::client::close_reason const& reason );

		/**
		* @brief	重连回调函数。
		* @param  [in] unsigned p1 ???\n
		* @param  [in] unsigned p2 ???\n
		*/
		void _on_reconnect( unsigned p1, unsigned p2 );

		/**
		* @brief		失败回调函数。
		*/
		void _on_fail();

		/**
		* @brief		socket打开成功回调函数。
		*/
		void _on_socket_opened();

		/**
		* @brief 收到服务器端CALL命令回调函数。
		* @param  [in] std::string const& name  触发的名字\n
		* @param  [in] sio::message::ptr const& data  收到的消息数据\n
		* @param  [in] bool need_ack  是否需要应答\n
		* @param  [in] sio::message::list &ack_resp  应答消息数据\n
		*/
		void _OnCallMessage( std::string const& name, sio::message::ptr const& data, bool need_ack, sio::message::list &ack_resp );

		/**
		* @brief	收到服务器端登录应答命令回调函数。
		* @param  [in] std::string const& name  触发的名字\n
		* @param  [in] sio::message::ptr const& data  收到的消息数据\n
		* @param  [in] bool need_ack  是否需要应答\n
		* @param  [in] sio::message::list &ack_resp  应答消息数据\n
		*/
		void _OnLoginResponse( std::string const& name, sio::message::ptr const& data, bool need_ack, sio::message::list &ack_resp );

        sio::message::ptr light_ctrl_to_sio_message(const std::string& val);
	public:
		wsClient();
		~wsClient();

		std::shared_ptr<wsClient> clone();

		/**
		* @brief	初始化函数。
		* @param  [in] int ID  当前客户端的唯一标识\n
		* @param  [in] const std::string & uri  连接串\n
		* @param  [in] const std::map<std::string, MSG_HANDLE_FUNC_TYPE>& MsgFuncList  消息处理函数\n
		*/
		void init( int ID, const std::string & uri, const std::map<std::string, MSG_HANDLE_FUNC_TYPE>& MsgFuncList );
		/**
		* @brief	获得连接字符串函数。
		* @return 获得的连接字符串\n
		*/
		std::string get_uri();
		/**
		* @brief	连接函数。
		* @param  [in] int time_out  超时(单位:秒),默认是3秒\n
		* @return 连接服务器是否成功\n
		* @retval  0  成功\n
        * @retval  -1  失败\n
		*/
		int connect( int time_out = 3 );

		/**
		* @brief	;登录函数。
		*/
		void login();

		/**
		* @brief	发送数据函数。
		* @param  [in] const std::string & Cmd  要发送的命令\n
		* @param  [in] const std::string& Data  要发送的数据\n
		*/
		void send( const std::string & Cmd, const std::string & Data );

		/**
		* @brief	获得ID函数。
		*/
		int GetID();

		/**
		* @brief	获得是否已连接服务器端函数。
		*/
		bool IsConnected();
		/**
		* @brief 获得Socket是否已打开函数。
		* @return Socket是否已打开\n
		*/
		bool IsSktOpened();
		/**
		* @brief	获得是否已登录成功函数。
		* @return 是否已登录成功\n
		*/
		bool IsLogined();
		/**
		* @brief	关闭函数。
		*/
		void close();
		/**
		* @brief	获得最新错误函数。
		*/
		std::string GetLastError();

		/**
		* @brief	获取删除ping的时间。
		*/
		int GetPingTime() const ;

        /*
         * @brief   重连
         * */
        void reconnect();

        sio::message::ptr to_sio_message(const std::string& val);
        sio::message::ptr call_to_sio_message(const std::string& val);  // 呼叫类型
        sio::message::ptr from_json(const rapidjson::Value& val);
        sio::message::ptr from_array(const rapidjson::Value& val);
	};
}