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

#include "..\..\..\..\errors.h"
#include "..\..\..\..\gate.h"
#include "..\..\..\..\deprtmnt.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 perform_totals(Gate_Call_Sb_Kernel function)
{
  InArg in;
  OutArg out;
  struct_in_cl6 in6;
  struct_out_cl6 out6;
  struct_in_reserved6 extra;

  memset(&in,0,sizeof(in));
  memset(&out,0,sizeof(out));
  memset(&in6,0,sizeof(in6));
  memset(&out6,0,sizeof(out6));
  memset(&extra,0,sizeof(extra));

  extra.size = sizeof(extra);
  in6.Currency = 0;
  in.in_struct = &in6;
  in.Reserved = &extra;
  out.out_struct = &out6;

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

void transaction_commit(Gate_Call_Sb_Kernel function)
{
  InArg in;
  OutArg out;
  struct_in_cl6 in6;
  struct_out_cl6 out6;
  struct_in_reserved6 extra;

  memset(&in,0,sizeof(in));
  memset(&out,0,sizeof(out));
  memset(&in6,0,sizeof(in6));
  memset(&out6,0,sizeof(out6));
  memset(&extra,0,sizeof(extra));

  extra.size = sizeof(extra);
  in6.Currency = 0;
  in.in_struct = &in6;
  in.Reserved = &extra;
  out.out_struct = &out6;

  DWORD error = function(KO_COMMIT_TRANSACTION, &in, &out);
  throw_if_error(error, "transaction_commit");
  if (out.ErrorCode == ERR_INVALID_TRX_DATA)
  {
    std::cout << "Suspended operation not found." << std::endl;
    return;
  }

  throw_if_error(out.ErrorCode, "transaction_commit");
}

void perform_x_totals(Gate_Call_Sb_Kernel function)
{
  InArg in;
  OutArg out;
  struct_in_cl6 in6;
  struct_out_cl6 out6;
  struct_in_reserved6 extra;

  memset(&in,0,sizeof(in));
  memset(&out,0,sizeof(out));
  memset(&in6,0,sizeof(in6));
  memset(&out6,0,sizeof(out6));
  memset(&extra,0,sizeof(extra));

  extra.size = sizeof(extra);
  in6.Currency = 0;
  in.in_struct = &in6;
  in.Reserved = &extra;
  out.out_struct = &out6;

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

void transaction_suspend(Gate_Call_Sb_Kernel function)
{
  InArg in;
  OutArg out;
  struct_in_cl6 in6;
  struct_out_cl6 out6;
  struct_in_reserved6 extra;

  memset(&in,0,sizeof(in));
  memset(&out,0,sizeof(out));
  memset(&in6,0,sizeof(in6));
  memset(&out6,0,sizeof(out6));
  memset(&extra,0,sizeof(extra));

  extra.size = sizeof(extra);
  in6.Currency = 0;
  in.in_struct = &in6;
  in.Reserved = &extra;
  out.out_struct = &out6;

  DWORD error = function(KO_SUSPEND_TRANSACTION, &in, &out);
  throw_if_error(error, "transaction_suspend");
  if (out.ErrorCode == ERR_INVALID_TRX_DATA)
  {
    std::cout << "Transaction is not in progress." << std::endl;
    return;
  }
  throw_if_error(out.ErrorCode, "transaction_suspend");
}

void transaction_rollback(Gate_Call_Sb_Kernel function)
{
  InArg in;
  OutArg out;
  struct_in_cl6 in6;
  struct_out_cl6 out6;
  struct_in_reserved6 extra;

  memset(&in,0,sizeof(in));
  memset(&out,0,sizeof(out));
  memset(&in6,0,sizeof(in6));
  memset(&out6,0,sizeof(out6));
  memset(&extra,0,sizeof(extra));

  extra.size = sizeof(extra);
  in6.Currency = 0;
  in.in_struct = &in6;
  in.Reserved = &extra;
  out.out_struct = &out6;

  DWORD error = function(KO_SUSPEND_TRANSACTION, &in, &out);
  throw_if_error(error, "transaction_rollback");
  if (out.ErrorCode == ERR_INVALID_TRX_DATA)
  {
    std::cout << "Suspended operation not found." << std::endl;
    return;
  }

  throw_if_error(out.ErrorCode, "transaction_rollback");
}

void add_marchant_cheque(Gate_Call_Sb_Kernel function)
{
  InArg in;
  OutArg out;
  struct_in_cl6 in6;
  struct_out_cl6 out6;
  struct_in_reserved6 extra;

  memset(&in,0,sizeof(in));
  memset(&out,0,sizeof(out));
  memset(&in6,0,sizeof(in6));
  memset(&out6,0,sizeof(out6));
  memset(&extra,0,sizeof(extra));

  extra.size = sizeof(extra);
  in6.Currency = 0;
  in.in_struct = &in6;
  in.Reserved = &extra;

  extra.dwQueryRequestID = 0xDEADBEEF;
  char cheque[62*1024];
  extra.MerchantCheque = cheque;

  out.out_struct = &out6;

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

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);
    int action = 0;
    std::cout << "Please input action number:";
    std::cin >> action;
    switch(action) {
    case 6000:
      perform_totals(gate_Call_Sb_Kernel);
      break;
    case 6002:
      perform_x_totals(gate_Call_Sb_Kernel);
      break;
    case 6003:
      transaction_suspend(gate_Call_Sb_Kernel);
      break;
    case 6001:
      transaction_commit(gate_Call_Sb_Kernel);
      break;
    case 6004:
      transaction_rollback(gate_Call_Sb_Kernel);
      break;
    case 6005:
      add_marchant_cheque(gate_Call_Sb_Kernel);
      break;
    default:
      std::cerr << "Unknown action " << action;
    }

    /* 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;
}
