#include <windows.h>
#include <iostream>

#include "..\..\..\..\pilot_nt.h"
#include "..\..\..\..\deprtmnt.h"
#include "..\..\..\..\cardtype.h"
#include "..\..\..\..\errors.h"

#ifndef WINCE  
  #define DDL_NAME "pilot_nt.dll"
#else
  #define DDL_NAME L"pilot_nt.dll"
#endif




bool IsCardHolderWantsToPayByCash()
{
  return false; // change it to true is customer wants to pay by cash
}

//read vas
void read_vas(std::string  &hashFromReadCard)
{
   std::cout << "Let's read google vas!" << std::endl;
   //allocate context
   CONTEXT_PTR ctx=ctxAlloc();

   //set card type 
   ctxSetInt(ctx, PAR_CARD_TYPE, /*CT_VAS_AND_MS*/ 0);

   int error = ReadCardContext(ctx);

   std::cout << "ReadCardContext result=" << error << std::endl;

   if (error==ERR_OK)
   {
     std::cout << "Retrive context vars..." << std::endl;
     int ivalue;
     char buf[128];

     error = ctxGetInt(ctx, PAR_LLT_ID, &ivalue);
     if (error==ERR_OK)
     {
       std::cout << "Loyalty Id = " << ivalue << std::endl;
       error = ctxGetString(ctx, PAR_LLT_DATA, buf, sizeof(buf));
       if (error==ERR_OK)
       {
         std::cout << "Loyalty data = " << buf << std::endl;
       }
     }
     else
     {
       std::cout << "Loyalty Id is not found in context" << std::endl;
     }

     error = ctxGetString(ctx, PAR_PAN, buf, sizeof(buf));
     if (error==ERR_OK)
     {
       std::cout << "PAN = " << buf << std::endl;
     }
     else
     {
       std::cout << "PAN is not found in context" << std::endl;
     }

     error = ctxGetString(ctx, PAR_HASH, buf, sizeof(buf));
     if (error==ERR_OK)
     {
       hashFromReadCard=buf;
       std::cout << "HASH = " << buf << std::endl;
     }
     else
     {
       std::cout << "HASH is not found in context" << std::endl;
     }

     error = ctxGetInt(ctx, PAR_CARD_TYPE, &ivalue);
     if (error==ERR_OK)
     {
       std::cout << "Card type = " << ivalue << std::endl;
     }
     else
     {
       std::cout << "Card type is not found in context" << std::endl;
     }

     error = ctxGetInt(ctx, PAR_OWN_CARD, &ivalue);
     if (error==ERR_OK)
     {
       std::cout << "Own card = " << ivalue << std::endl;
     }
     else
     {
       std::cout << "Own card is not found in context" << std::endl;
     }

     error = ctxGetInt(ctx, PAR_VAS_LENGTH, &ivalue);
     if (error==ERR_OK)
     {
       std::cout << "vas xml length = " << ivalue << std::endl;

       char* vas=new char [ivalue+1];
       error = ctxGetString(ctx, PAR_VAS_STRING, vas, ivalue+1);
       if (error == ERR_OK)
       {
        std::cout << "vas xml string " << std::endl << vas << std::endl;
       }
       else
       {
         std::cout << "vas xml string is not found in context" << std::endl;
       }
       delete [] vas;
     }
     else
     {
       std::cout << "vas length is not found in context" << std::endl;
     }
   }
   ctxFree(ctx);
}

extern HMODULE LoadPilotNt(const char* name, const char** nameOfTheUncknownFunction);
extern void    FreePilotNt(HMODULE dll);


#ifndef WINCE
int main (int argc, char** argv)
#else
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
#endif
{
  //HMODULE dll = LoadLibrary(DDL_NAME);
  const char* nameOfTheUncknownFunction=NULL;
  HMODULE dll = LoadPilotNt(DDL_NAME, &nameOfTheUncknownFunction);
  if (!dll) 
  {
    if (!nameOfTheUncknownFunction)
    {
      std::cerr << "pilot_nt.dll not found";
      return -1;
    }
    else
    {
      std::cerr << "function " << nameOfTheUncknownFunction << " in " << DDL_NAME << " not found";
      return -2;
    }    
  }

  CONTEXT_PTR ctx=NULL;

  try {
    std::string hashFromReadCard;
    //read vas
    read_vas(hashFromReadCard);


    //read vas and pay
    auth_answer14 argument;
    DWORD amount1 = 99900;
    DWORD amount2 = 90000;    
    std::string hash;
    DWORD result;


    memset(&argument, 0, sizeof(argument));
    //allocate context
    CONTEXT_PTR ctx=ctxAlloc();

    //unkoment case 1 or case 2 lines if we want perform pay with hash
    //if (!hashFromReadCard.empty())
    //{ 
    //  case 1
    //  argument.ans.TType = OP_PURCHASE;
    //  argument.ans.Amount = amount1;
    //  std::cout << "Perform payment with hash as track2 argument..." << std::endl;
    //  result = card_authorize15(hashFromReadCard.c_str(), &argument, NULL, 0, ctx);
    //  if (argument.ans.Check)
    //    ::GlobalFree((HGLOBAL)argument.ans.Check);
    //  case 2
    //  or we can do this folk    
    //  argument.ans.TType = OP_PURCHASE;
    //  argument.ans.Amount = amount1;
    //  //set hash, skiping the first 'H'
    //  strcpy(argument.Hash, hashFromReadCard.c_str()+1);
    //  //and call card_authorize15
    //  std::cout << "Perform payment with hash inside auth_answer14..." << std::endl;
    //  result = card_authorize15(NULL, &argument, NULL, 0, ctx);
    //  if (argument.ans.Check)
    //    ::GlobalFree((HGLOBAL)argument.ans.Check);
    //}
    
    memset(&argument,0,sizeof(argument));
    argument.ans.TType = OP_PURCHASE;
    argument.ans.Amount = amount1;
    argument.ans.CType=CT_VAS_AND_PAY;
    std::cout << "Let's try to pay!" << std::endl;


    result = card_authorize15(NULL,&argument, NULL, 0, ctx);
    // after call 'argument' contains card hash which required for second call
    if (result == 4353)
    {
      /*
      check the reason for the response code 4353
      It can be: 
      1. The card was issued by Sberbank. 
      2. The card corresponds to some kind of loyalty program. 
      3. Was read the GooglePay VAS 
      */
      
      //check that the vas is present in response
      int length=0;
      result = ctxGetInt(ctx, PAR_VAS_LENGTH, &length);
      if (result==ERR_OK && length)
      {
        std::cout << "Vas found in response. length = %d" << length << std::endl;
        char* vas=new char[length+1];
        result = ctxGetString(ctx, PAR_VAS_STRING, vas, length+1);
        if (result==ERR_OK)
        {
          std::cout << "vas xml string " << std::endl << vas << std::endl;
        }
        delete [] vas;
      }

      //Check card issuer

      hash = std::string(argument.Hash);

      if (argument.SberOwnCard)
      {
        std::cout << "Card was issued by Sberbank. argument.SberOwnCard=" << argument.SberOwnCard<< std::endl;
        std::cout << "Using an external library and the hash ";
        std::cout << hash.c_str() << " we can query the balance and to write off the Spasibo bonuses"<<std::endl;
      }
      else
      {
        std::cout << "This card was issued by third-party bank. argument.SberOwnCard=" << argument.SberOwnCard<< std::endl;
      }

      //check the loyalty card. In general, it can be a bank card of Sberbank with loyalty data   
      int lltId=0;
      result = ctxGetInt(ctx, PAR_LLT_ID, &lltId);
      if (result==ERR_OK && lltId)
      {
        std::cout << "Loyalty programm number = " << lltId << std::endl;
        std::cout << "Loyalty value = " << argument.Track3 << std::endl;
      }
      else
      {
        std::cout << "Loyalty not found" << std::endl;
      }
 
      hash = std::string(argument.Hash); // < -- card hash is here now      

      if (IsCardHolderWantsToPayByCash())
      {
        PilotCloseReader();
      }
      else
      {
        // KKM code can change amount
        argument.ans.Amount = amount2;
        argument.ans.CType=0;

        //set hash
        strcpy(argument.Hash, hash.c_str());

        ctxClear(ctx);
        std::cout << "Perform the second payment phase for the amount = " << amount2 << std::endl;
        result = card_authorize15(NULL, &argument, NULL, 0, ctx);

        //free cheque
        if (argument.ans.Check)
          ::GlobalFree((HGLOBAL)argument.ans.Check);
      }
    }
  
    std::cout << "Operation completed with code '" << result << "'!" << std::endl;
  }
  catch(...)
  {

  }

  ctxFree(ctx);

  FreePilotNt(dll);
  return 0;
}

