//
// Created by dongyul on 10/24/22.
//
#include <XeleSecuritiesTraderApi.h>
#include <string>
#include <stdint.h>
#include <iostream>
#include <cstring>
#include "statistic_delay.h"

#define PRINT_INFO(fmt, ...) printf(fmt,  ##__VA_ARGS__); printf("\n")

/*
 * 此处是可以进行合约、买卖方向、价格的选择，当demo运行时选择了手动填时，需要按照提示手动输入这些参数，然后demo就会使用输入的合约、方向、价格报单
 * */
struct Securities{
  bool manual_input;
  std::string securities;
  char direction;
  double price;
  unsigned int volumn;
};

Securities g_manual_input_secutities;

XeleSecuritiesTraderApi* pUserApi1 = nullptr;

typedef uint64_t timing_t1;
static timing_t1 now_tsc00, now_tsc11, now_tsc22;

//demo维护的全局请求ID
unsigned int g_RequestID(0);
unsigned int g_RSPstart(0), g_RTNStart(0), g_RSPEnd(0), g_RTNEnd(0);
//demo希望操作的合约号
std::string g_SecuritiesID;
std::string g_accountID;
std::string g_password;
double g_fundamt;
char g_direction;
int g_node;
char g_market;

void init_g_param(char* argv[]){
  g_accountID = argv[1];
  g_password = argv[2];
  g_fundamt = 100.33;
  g_direction = XELE_TRANSFER_DIR_SS_2_SZ;
  g_node = *argv[3] - '0';
  g_market = *argv[4];
};

class TestOrderTime : public XeleSecuritiesTraderSpi{
  public:
  TestOrderTime(){
    PRINT_INFO("This Is Test Manager And Counter Interface");
  };
  ~TestOrderTime() override{
    PRINT_INFO("This Is Test Manager And Counter Interface End");
  };

  bool canFundTransfer = true;
  //表明具有柜台查询权限
  bool canQuery = false;
  //表明具有柜台报、撤单权限
  bool canOrder = false;
  //用户自己管理的本地最大编号，在每次登录柜台成功后，会得到当前柜台的本地最大编号，在报撤单时，可以使用该字段来进行报撤单管理
  unsigned int maxUserLocalID{};

  void testInsertOrderReturnDuration(){
      if(g_manual_input_secutities.manual_input){
      CXeleReqOrderInsertField orderInsertField{};
      memset(&orderInsertField,0,sizeof(CXeleReqOrderInsertField));
      orderInsertField.UserLocalID = maxUserLocalID++;
      orderInsertField.OrderType = XELE_LIMIT_PRICE_TYPE;
      orderInsertField.TimeCondition = XELE_TIMEINFORCE_TYPE_GFD;
      memcpy(orderInsertField.BusinessUnit,"demotest",8);
      memcpy(orderInsertField.SecuritiesID,g_manual_input_secutities.securities.c_str(),g_manual_input_secutities.securities.length());
      orderInsertField.Direction = g_manual_input_secutities.direction;
      orderInsertField.LimitPrice = g_manual_input_secutities.price;
      orderInsertField.Volume = g_manual_input_secutities.volumn;

      orderInsertField.SecuritiesType = '0';
      orderInsertField.Operway = API_OPERWAY;
    
      statistic_delay::start(1, g_RSPstart++);
      statistic_delay::start(5, g_RTNStart++);
      pUserApi1->reqInsertOrder(orderInsertField, g_RequestID++);
    }
  }

 private:
  void onRspInsertOrder(CXeleRspOrderInsertField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    statistic_delay::end(1, g_RSPEnd++);
  };

  ///报单回报
  void onRtnOrder(CXeleRtnOrderField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    statistic_delay::end(5, g_RTNEnd++);
    
    //等待所有的报单返回结束整个进程
    if(g_RTNEnd + 2 == g_RequestID){
        sleep(11);
        canFundTransfer = false;
    }
  };

  ///添加交易链路应答,当收到该回报时，标记艾科柜台报、撤单接口可用
  void onRspInitTrader(CXeleRspInitTraderField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    if(pRspInfo->ErrorID == 0){
      canOrder = true;
      PRINT_INFO("now can use order interface");
    } else{
      PRINT_INFO("create order link error,ErrID[%d],ErrMsg[%s]",pRspInfo->ErrorID,pRspInfo->ErrorMsg);
    }
  };

  ///艾科柜台登录应答,当需要管理中心接口可用，但是只需求艾科柜台查询接口可用时，收到该回报即可进行操作
  void onRspLogin(CXeleRspUserLoginField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    if(pRspInfo->ErrorID == 0){
      canQuery = true;
      maxUserLocalID = pRspField->MaxUserLocalID;
      maxUserLocalID += 1;
      PRINT_INFO("now can use query interface");
    } else{
      PRINT_INFO("login counter error,ErrID[%d],ErrMsg[%s]",pRspInfo->ErrorID,pRspInfo->ErrorMsg);
    }
  };

};


int main(int argc, char** argv) {

  if(argc != 5){
	PRINT_INFO("input accountID,passWord,node, market when start demo");
	PRINT_INFO("example: ./demo accountID passWord node market");
    return 0;
  }
  init_g_param(argv);
  PRINT_INFO("if you want manual input securities,direction,price,volumn,please manual input 'y/Y',if you want auto run,please manual input 'n/N'");
  char input;
  while (std::cin >> input){
    if (input == 'y' || input == 'Y') {
      PRINT_INFO("input securities:");
      std::cin >> g_manual_input_secutities.securities;
      PRINT_INFO("input direction:");
      std::cin >> g_manual_input_secutities.direction;
      PRINT_INFO("input price:");
      std::cin >> g_manual_input_secutities.price;
      PRINT_INFO("input volumn:");
      std::cin >> g_manual_input_secutities.volumn;
      g_manual_input_secutities.manual_input = true;
      break;
    } else if (input == 'n' || input == 'N') {
      PRINT_INFO("auto run");
      g_manual_input_secutities.manual_input = false;
      break;
    } else {
      PRINT_INFO("if you want manual input securities,direction,price,volumn,please manual input 'y/Y',if you want auto run,please manual input 'n/N'");
    }
  }

  //初始化延迟写入文件的类
  //1.时间开始statistic_delay::start(int type, int i);
  //2.结束开始statistic_delay::end(int type, int i);
  //type为文件名的前缀，start与end的i要一一对应
  //文件中count为统计数量，max为最大值，min为最小值，10%~100%统计的为各各段位的平均时间
  statistic_delay::init();

  //创建一个接收回报的类，该类继承自XeleSecuritiesTraderSpi，用来接收艾科柜台的回报响应
  auto *pTestOrderTime = new TestOrderTime();

  //调用createTradeApi接口，创建一个请求类对象，该对象用来向艾科柜台发送操作请求
  pUserApi1 = XeleSecuritiesTraderApi::createTraderApi();

  pUserApi1->registerSpi(pTestOrderTime);

  pUserApi1->reqLogin("./api_config.txt",g_accountID.data(),g_password.data(),
                           g_node, g_market, g_RequestID++);

  //判断创建是否成功
  if (!pUserApi1) {
    PRINT_INFO("Can't Create API");
    exit(0);
  }

  while (true) {
    if (pTestOrderTime->canOrder) {
      PRINT_INFO("canOrder");
      break;
    }
    sleep(1);
  }

  //每单发送的时间间隔为1毫秒
  for(; g_RequestID <= g_manual_input_secutities.volumn;){
    pTestOrderTime->testInsertOrderReturnDuration();
    usleep(1000);
  }

  while (true){
    sleep(1);
    if(!pTestOrderTime->canFundTransfer) {
      sleep(2);
      break;
    }
  }
  pUserApi1->release();
  delete pTestOrderTime;
  return 0;
}
