#include <assert.h>
#include <iostream>

#include "..\..\..\..\deprtmnt.h"
#include "..\..\..\..\errors.h"
#include "..\..\..\..\gate.h"
#include "..\..\..\..\sbkernel.h"

#ifndef WINCE
  const char* CALL_SB_KERNEL_NAME = "_call_sb_kernel";
#else
  const char* CALL_SB_KERNEL_NAME = "call_sb_kernel";
#endif

  int i = __LINE__;

  struct Error
  {
    DWORD Line;
    DWORD Code;
    std::string Text;

    Error(DWORD line, DWORD code, const char* text)
      : Line(line), Code(code), Text(text)
    {}
  };

#define throw_if_error(code, text) if (code) throw Error(__LINE__, code, text)

void initialize(Gate_Call_Sb_Kernel function)
{
  InArg in;
  OutArg out;
  struct_in0 InDetail;
  struct_out0 OutDetail;
  memset(&in, 0, sizeof(in));
  memset(&InDetail, 0, sizeof(InDetail));
  in.in_struct = &InDetail;
  InDetail.AppVersion = KERNEL_MINIMAL_VERSION;
  memset(&out, 0, sizeof(out));
  memset(&OutDetail, 0, sizeof(OutDetail));
  out.out_struct = &OutDetail;

  DWORD error = function(0,&in,&out);
  throw_if_error(error, "initialization");
  throw_if_error(out.ErrorCode, "initialization");
}

void make_full_report(Gate_Call_Sb_Kernel function)
{
  InArg in;
  OutArg out;
  memset(&in, 0, sizeof(in));
  memset(&out, 0, sizeof(out));
  DWORD error = function(KO_FULL_REPORT,&in,&out);
  throw_if_error(error, "make_full_report");
  throw_if_error(out.ErrorCode, "make_full_report");
  if(out.Flags&F_HAVE_DOCUMENT)
    std::cout << "Cheque ready to read. Please call function KO_GET_LAST_CHEQUE" << std::endl;
  if(out.Flags&F_HAVE_MONMSG)
    std::cout << "Monitoring message received. Please call function KO_GET_MONITORING_STATE" << std::endl;
}

void make_last_cheque(Gate_Call_Sb_Kernel function)
{
  InArg in;
  OutArg out;
  memset(&in, 0, sizeof(in));
  memset(&out, 0, sizeof(out));
  DWORD error = function(KO_LAST_CHEQUE,&in,&out);
  throw_if_error(error, "make_last_cheque");
  if (out.ErrorCode == SPS_BAD_REQUEST)
  {
    std::cout << "Last cheque was not found." << std::endl;
    return;
  }
  throw_if_error(out.ErrorCode, "make_last_cheque");
  if(out.Flags&F_HAVE_DOCUMENT)
    std::cout << "Cheque ready to read. Please call function KO_GET_LAST_CHEQUE" << std::endl;
  if(out.Flags&F_HAVE_MONMSG)
    std::cout << "Monitoring message received. Please call function KO_GET_MONITORING_STATE" << std::endl;
}

void show_report_menu(Gate_Call_Sb_Kernel function)
{
  InArg in;
  OutArg out;
  memset(&in, 0, sizeof(in));
  memset(&out, 0, sizeof(out));
  DWORD error = function(KO_SHOW_REPORT_MENU,&in,&out);
  throw_if_error(error, "show_report_menu");
  throw_if_error(out.ErrorCode, "show_report_menu");
  if(out.Flags&F_HAVE_DOCUMENT)
    std::cout << "Cheque ready to read. Please call function KO_GET_LAST_CHEQUE" << std::endl;
  if(out.Flags&F_HAVE_MONMSG)
    std::cout << "Monitoring message received. Please call function KO_GET_MONITORING_STATE" << std::endl;
}

void test_pinpad(Gate_Call_Sb_Kernel function)
{
  InArg in;
  OutArg out;
  struct_in13 in13;
  struct_out13 out13;

  memset(&in,0,sizeof(in));
  memset(&out,0,sizeof(out));
  memset(&in13,0,sizeof(in13));
  memset(&out13,0,sizeof(out13));

  in.in_struct = &in13;
  out.out_struct = &out13;
  DWORD error = function(KO_TEST_PINPAD,&in,&out);
  throw_if_error(error, "test_pinpad");
  throw_if_error(out.ErrorCode, "test_pinpad");
  std::cout << "Serial number '" << out13.SN << "'" << std::endl;
  std::cout << "UPOS " << int(out13.Version) << "." << int(out13.Release) << "." << int(out13.Build) << std::endl;
}

void make_short_report(Gate_Call_Sb_Kernel function)
{
  InArg in;
  OutArg out;
  memset(&in, 0, sizeof(in));
  memset(&out, 0, sizeof(out));
  DWORD error = function(KO_SHORT_REPORT,&in,&out);
  throw_if_error(error, "make_short_report");
  throw_if_error(out.ErrorCode, "make_short_report");
  if(out.Flags&F_HAVE_DOCUMENT)
    std::cout << "Cheque ready to read. Please call function KO_GET_LAST_CHEQUE" << std::endl;
  if(out.Flags&F_HAVE_MONMSG)
    std::cout << "Monitoring message received. Please call function KO_GET_MONITORING_STATE" << std::endl;
}

void get_operation_info(Gate_Call_Sb_Kernel function, unsigned operation_number)
{
  InArg in;
  OutArg out;
  struct_in_reserved7 in13;
  struct_out_reserved7 out13;

  memset(&in,0,sizeof(in));
  memset(&out,0,sizeof(out));
  memset(&in13,0,sizeof(in13));
  memset(&out13,0,sizeof(out13));

  in13.size = sizeof(in13);
  in13.RecordID = operation_number;
  out13.size = sizeof(out13);
  in.Reserved = &in13;
  out.Reserved = &out13;
  DWORD error = function(KO_GET_OPERATION_INFO,&in,&out);
  throw_if_error(error, "get_operation_info");
  if (out.ErrorCode == ERR_PIL_NO_FILES)
  {
    std::cout << "Has no operations today." << std::endl;
    return;
  }

  throw_if_error(out.ErrorCode, "get_operation_info");

  std::cout << "Operation with record ID " << operation_number << std::endl;
  std::cout << "TermNum: " << out13.TermNum << std::endl;
  std::cout << "MerchNum: " << out13.MerchNum << std::endl;
  std::cout << "Amount: " << out13.Record.Amount << std::endl;
  std::cout << "CardName: " << out13.Record.CardName << std::endl;
  std::cout << "ClientCard: " << out13.Record.ClientCard << std::endl;
  std::cout << "ClientExpiryDate: " << out13.Record.ClientExpiryDate << std::endl;
  std::cout << "RRN: " << out13.Record.RRN << std::endl;
  std::cout << "Operation date:" << out13.Record.TrxDate << std::endl;
  std::cout << "Operation time:" << out13.Record.TrxTime << std::endl;
}

void get_terminal_and_merchant_ids(Gate_Call_Sb_Kernel function)
{
  InArg in;
  OutArg out;
  struct_out_reserved7 out7;

  memset(&in,0,sizeof(in));
  memset(&out,0,sizeof(out));
  memset(&out7,0,sizeof(out7));

  out7.size = sizeof(out7);
  out.Reserved = &out7;
  DWORD error = function(KO_GET_TERMINAL_AND_MERCHENT_ID,&in,&out);
  throw_if_error(error, "get_terminal_and_merchant_ids");
  throw_if_error(out.ErrorCode, "get_terminal_and_merchant_ids");

  std::cout << "Terminal ID " << out7.TermNum << std::endl;
  std::cout << "Merchant ID " << out7.MerchNum << std::endl;
}

bool de_initialize(Gate_Call_Sb_Kernel function)
{
  InArg in;
  OutArg out;
  memset(&in,0,sizeof(in));
  memset(&out,0,sizeof(out));

  if(function && !function(1, &in, &out))
      return true;

  return false;
}


int main (int argc, char** argv)
{
  setlocale(LC_ALL, "russian_Russia.1251");
  HMODULE dll = LoadLibrary("gate.dll");
  if (!dll)
  {
    std::cerr << "gate.dll not found";
    return -1;
  }

  try {
    Gate_Call_Sb_Kernel gate_Call_Sb_Kernel = (Gate_Call_Sb_Kernel)GetProcAddress(dll, CALL_SB_KERNEL_NAME);
    if (!gate_Call_Sb_Kernel)
    {
      std::cerr << "function _call_sb_kernel in gate.dll not found.";
      return -2;
    }

    /* 0 */ initialize(gate_Call_Sb_Kernel);
    /* 7000 */ make_full_report(gate_Call_Sb_Kernel);
    /* 7001 */ make_last_cheque(gate_Call_Sb_Kernel);
    /* 7002 */ show_report_menu(gate_Call_Sb_Kernel);
    /* 7003 */ test_pinpad(gate_Call_Sb_Kernel);
    /* 7004 */ make_short_report(gate_Call_Sb_Kernel);
    /* 7006 */ get_operation_info(gate_Call_Sb_Kernel, 0);
    /* 7007*/  get_terminal_and_merchant_ids(gate_Call_Sb_Kernel);

    /* 1*/ de_initialize(gate_Call_Sb_Kernel);
  }
  catch(const Error& error)
  {
    std::cerr << "Error '" << error.Code << ":" << error.Text.c_str() << "' on line " << error.Line << std::endl;
  }

  FreeLibrary(dll);
  return 0;
}
