/**
 * @file clientDemo.h
 * @author root
 * @brief tcp连接、业务处理、接口调用以及回报处理
 * @date 25-7-16
 *
 * 详细的文件描述或注意事项可以放在这里。
 */
#ifndef SECURITY_TRADER_API_TRADECLIENTDEMO_H
#define SECURITY_TRADER_API_TRADECLIENTDEMO_H

#include "iostream"
#include "unistd.h"
#include "sys/epoll.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "arpa/inet.h"
#include "thread"
#include "map"
#include "atomic"

#include <global.h>
#include <responseHandler.h>

/// 报单协议标识
#define PROTOCOL_ID 0x5f
#define MESSAGE_BUF_SIZE  1024

/// 发送事件, 用来控制eventLoop中的发送
/// Use To Control EPOLLOUT
struct SendEvent {
  int socket;
  int socketType;
  int messageID;
  uint32_t messageLength;
};

/// 回应消息处理单元
struct ResponseHandlerItem {
  /// 消息ID
  int messageID;
  /// 回应处理对象
  responseHandler *handler;
};

/// 交易客户端Demo, 只实现了必要的心跳机制, OrderWarm机制这里没实现
class ClientDemo {
 public:
  ClientDemo();
  ~ClientDemo();
  /// Ban Copy Construct
  ClientDemo(const ClientDemo&) = delete;
  /// Ban Opeartor=
  ClientDemo& operator=(const ClientDemo&) = delete;

 public:
  /// tcp连接
  int connectManager();
  int connectCounterQuery();
  int connectCounterTrade();
  int connect(uint16_t port, std::string ip, TcpType type);
  /// 启动接口
  int run();
  /// 功能接口
  void TestManagerLogin();
  void TestCounterLogin();
  void TestInitTrade();
  void TestOrderInsert();
  void TestOrderAction();
  void TestOrderQuery();
  void TestCounterLogout();
  void TestManagerLogout();
  /// tcp disconnect
  void disConnectTcp() {
    for (auto socket : m_socketMap) {
      if(socket.second != 0) {
        PRINT_INFO("socketType %d, socket %d", socket.first, socket.second);
        shutdown(socket.second, SHUT_RDWR);
      }
    }
  }
  bool isRunning() const {
    return m_isRunning;
  }

  void setRunning(bool flag){
    m_isRunning = flag;
  }

  bool isManagerLogin() const;
  bool isCounterQueryLogin() const;
  bool isCounterTradeLogin() const;

 private:
  void createFairHead(char *buf, TcpType tcpType, uint8_t messageId, uint16_t messageLen);
  void createSendEvent(int messageId, uint32_t messageLen, int socketType);

 private:
  void init();
  /// 心跳处理线程, 交易链路, 客户端和服务端需要互相发送心跳
  void heartBeatThread();
  /// 主要逻辑处理线程
  void eventLoop();
  /// 发送事件处理接口
  void handleSendEvent(SendEvent *sndEvent);

  /// 接收事件处理的相关接口
  size_t recvMessage(int fd);
  size_t readn(int fd, void *buf, size_t n);
  int doResponse(epoll_event event);

  /// 因为epoll ET模式, 只支持非阻塞文件描述符, 封装了一下socket的connect
  int connectNoBlocking(int fd, struct sockaddr *addr, socklen_t addrLen);

 private:
  /// 全局epoll fd
  int m_epfd;

  /// 消息发送缓冲区
  char m_sendBufManager[MESSAGE_BUF_SIZE];
  char m_sendBufCounterQuery[MESSAGE_BUF_SIZE];
  char m_sendBufCounterTrade[MESSAGE_BUF_SIZE];
  /// 心跳报文发送缓冲区
  char m_sHeartBtBufManager[MESSAGE_BUF_SIZE];
  char m_sHeartBtBufCounterQuery[MESSAGE_BUF_SIZE];
  char m_sHeartBtBufCounterTrade[MESSAGE_BUF_SIZE];
  /// 接收缓冲区
  char m_recvBuf[MESSAGE_BUF_SIZE];

  /// Each Seconds inc
  /// 在心跳处理线程中每隔一秒++, 到达发送间隔后需要发送心跳报文到服务端
  uint32_t m_sndHeatBtCtManager;
  uint32_t m_sndHeatBtCtCounterQuery;
  uint32_t m_sndHeatBtCtCounterTrade;
  /// 在心跳处理线程中每隔一秒++; 主要逻辑处理线程中, 收到服务端报文, 会重置成0；当到达心跳超时时间, 需要主动断开连接
  std::atomic<uint32_t> m_timoutCountManager;
  std::atomic<uint32_t> m_timoutCountCounterQuery;
  std::atomic<uint32_t> m_timoutCountCounterTrade;
  /// 心跳线程启动标记
  bool m_managerHeartbeatStart;
  bool m_counterQueryHeartbeatStart;
  bool m_counterTradeHeartbeatStart;
  /// 连接是否登录成功标记
  bool m_managerLogin;
  bool m_counterQueryLogin;
  bool m_counterTradeLogin;

  /// Thread Object
  std::thread m_heatBtThread;
  std::thread m_mainEvThread;

  /// 交易客户端运行状态
  /// Client Running Status
  std::atomic<bool> m_isRunning;

  /// 响应报文处理接口集
  /// ResponseHandler Map
  std::map<int, responseHandler*> m_counterHandlers;
  std::map<int, responseHandler*> m_managerHandlers;

  /*
   * 下面的数据结构只是为了demo服务，实际使用时请选择合适的数据结构
   * */
  /// key is socketType, value is socket
  /// 存储socket类型和fd的对应关系
  std::map<int, int > m_socketMap;
  /// key si socket, value is SendEvent
  /// 存储fd和当前fd下需要处理的事件
  std::map<int, SendEvent> m_socketEventMap;
};

#endif //SECURITY_TRADER_API_TRADECLIENTDEMO_H
