    //
//  EMVTransactionManagerVas.m
//  MyBluetoothTest
//
//  Created by Emmanuel Adeyemi on 1/13/16.
//  Copyright © 2016 Emmanuel Adeyemi. All rights reserved.
//

#import "EMVTransactionManagerVas.h"
#import "PaypadKeyManager.h"
#import "NSString+HexUtil.h"
#import "PinpadHelper.h"
#import "PurchaseResponse.h"

@implementation EMVTransactionManagerVas{
    BOOL online;
    int authResult;
}


#define TEST_EMV(title,function) \
if(!function){[self log:[NSString stringWithFormat:@"%@ failed: %@",title,error.localizedDescription]]; return;}; \
[self log:[NSString stringWithFormat:@"%@ succeeded\nEMV Status: %d",title,dtdev.emvLastStatus]];

#define TEST(title,function) \
if(!function){[self log:[NSString stringWithFormat:@"%@ failed: %@",title,error.localizedDescription]]; return;}; \
[self log:[NSString stringWithFormat:@"%@ succeeded",title]];

-(id)initWithDTDevices:(DTDevices *)dtdevices{
    
    if(self){
        dtdev = [DTDevices sharedDevice];
        [dtdev addDelegate:self];
    }
    
//    _requestMain = nil;
    
    self = [super init];
    
    return self;
}

-(void) doEMVTransaction:(EMVRequest *)request{
    
    NSError *error;
    NSString *str;
    
    _requestMain = request;
    
//    [logView setText:@""];
    
    [PinpadHelper initScreen:dtdev];
    
//    TEST(@"*** Init SmartCard module",[dtdev scInit:SLOT_MAIN error:&error]);
    
    TEST(@"*** Check SmartCard present",[dtdev scIsCardPresent:SLOT_MAIN error:&error]);
    
    NSData *atr=[dtdev scCardPowerOn:SLOT_MAIN error:&error];
    TEST(@"*** Power on SmartCard",atr);
    [self log:hexToString(@"ATR",(uint8_t *)atr.bytes,atr.length)];
    
    // This shows the please wait message on the PINPAD
    [PinpadHelper showWaiting:dtdev];
    
    TEST_EMV(@"*** Init EMV Kernel",[dtdev emvInitialise:&error]);
    
    
    _response = [[EMVResponse alloc] init];
    
    //    [self cryptoTest];
    [self emvSetString:TAG_ADD_TERM_CAPABILITIES value:@"600080A000" description:@"terminal capabilities"];
    [self emvSetString:TAG_SERIAL_NUMBER value:@"12345678" description:@"serial number"];
    [self emvSetString:TAG_TERMINAL_COUNTRY_CODE value:@"0566" description:@"terminal country code"];
    [self emvSetString:TAG_TERMINAL_TYPE value:@"22" description:@"terminal type"];
    [self emvSetString:TAG_TERMINAL_CAPABILITIES value:@"E0F8C8" description:@"terminal capabilities"];
    
    
    NSMutableArray *apps=[[NSMutableArray alloc] init];
    
    [apps addObject:createApp(@"A0 00 00 00 03 10 10", @"VISA CR/DB", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 03 20 10", @"VISA ELECTRON", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 04 10 10", @"MASTERCARD CR/DB", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 04 30 60", @"MAESTRO", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 04 99 99", @"MASTERCARD", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 03 40 10", @"VISA", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 03 50 10", @"VISA", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 04 20 10", @"MASTERCARD", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 04 30 10", @"MASTERCARD", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 04 40 10", @"MASTERCARD", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 04 50 10", @"MASTERCARD", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 03 20 20", @"V PAY", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 03 80 10", @"VISA PLUS", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 03 30 10", @"VISA INTERLINK", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 04 60 00", @"CIRRUS", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 25 01 00", @"AMERICAN EXPRESS", MATCH_PARTIAL_VISA)];
    [apps addObject:createApp(@"A0 00 00 00 25 00 00", @"AMERICAN EXPRESS", MATCH_PARTIAL_VISA)];
    [apps addObject:createApp(@"A0 00 00 00 65 10 10", @"JCB", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 10 30 30", @"UNKNOWN", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 99 90 90", @"UNKNOWN", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 05 00 01", @"MAESTRO UK", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 05 00 02", @"UK SOLO", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 01 52 30 10", @"DISCOVER", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 29 10 10", @"LINK ATM NETWORK", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 01 21 10 10", @"DANKORT", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 01 41 00 01", @"CoGeBan", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 00 42 10 10", @"CB CARD", MATCH_FULL)];
    [apps addObject:createApp(@"A0 00 00 03 71 00 01", @"VERSE", MATCH_PARTIAL_VISA)]; // This was added to match verve card
    
    
    TEST_EMV(@"*** Load EMV application list",[dtdev emvLoadAppList:apps selectionMethod:SELECTION_PSE includeBlockedAIDs:FALSE error:&error]);
    
    BOOL confirmationRequired;
    NSArray *commonApps=[dtdev emvGetCommonAppList:&confirmationRequired error:&error];
    TEST_EMV(@"*** Get common application list",commonApps);
    for(int i=0;i<commonApps.count;i++)
    {
        DTEMVApplication *emv=[commonApps objectAtIndex:i];
        [self log:[NSString stringWithFormat:@"\n%d. %@\n%@",i+1,emv.label,hexToString(@"ATR",(uint8_t *)emv.aid.bytes,emv.aid.length)]];
    }
    
    // This sets the transaction date;
    
    [self emvSetString:TAG_TRANSACTION_TIME value:[self getICCTime:NULL] description:@"transaction time"];
    
    // This sets the transaction time
    [self emvSetString:TAG_TRANSACTION_DATE value:[self getICCDate:NULL] description:@"transaction date"];
    
    [self emvSetString:TAG_TRANSACTION_SEQ_COUNTER value:@"0001" description:@"transaction counter"];
    
    if(commonApps.count>0)
    {
        DTEMVApplication *app=[commonApps objectAtIndex:0];
        TEST_EMV(@"*** Initial application processing",[dtdev emvInitialAppProcessing:app.aid error:&error]);
        
        TEST_EMV(@"*** Read application data",[dtdev emvReadAppData:nil error:&error]);
        
        str=[self emvGetString:TAG_APP_VERSION_NUMBER description:@"application number"];
    }
    
    str=[self emvGetString:TAG_ICC_APP_VERSION_NUMBER description:@"application icc number"];
    
    str=[self emvGetString:TAG_LANGUAGE_PREFERENCE description:@"language preference"];
    
    str=[self emvGetString:TAG_ISSUER_CODE_INDEX description:@"issuer code index"];
    
    ///////////////////////////////////////////////////////////////////////
    // TRANSACTION DATA PROCESSING
    ///////////////////////////////////////////////////////////////////////
    str=[self emvGetString:TAG_EFFECTIVE_DATE description:@"effective date"];
    
    str=[self emvGetString:TAG_EXPIRY_DATE description:@"expiry date"];
    
    [[NSUserDefaults standardUserDefaults] setObject:str forKey:@"MASKED_PAN"];

    // Tags with sensitive data
    str=[self emvGetString:TAG_PAN description:@"pan"];

    // This is used in storing the maskedPan
    [[NSUserDefaults standardUserDefaults] setObject:maskedPan(str) forKey:@"MASKED_PAN"];
    
    str = [self readTagAsString:TAG_PAN];
    
    NSString *pan = str;
    
    if([[str substringFromIndex:[str length] - 1] isEqualToString:@"F"]){
        pan = [str substringToIndex:[str length] - 1];
    }

    _cardData = [[EmvCardData alloc] init];
    
    // This prints out the pan in plain
    NSLog(@"PAN: %@",pan);
    
    [_response setPan:pan];
    
    NSData *panData = [dtdev emvGetDataAsBinary:TAG_PAN error:nil];
    
    [_cardData setPan:panData];
    
    str=[self emvGetString:TAG_PAN_SEQUENCE_NUMBER description:@"pan sequence number"];
    
    // This gets the card pan sequence no in clear
    str = [self readTagAsString:TAG_PAN_SEQUENCE_NUMBER];
    
    [_cardData setCardSequenceNo:str];
    
    str=[self emvGetString:TAG_TRACK2_EQUIVALENT_DATA description:@"track 2 equivalent data"];
    
    str = [self readTagAsString:TAG_TRACK2_EQUIVALENT_DATA];
    
    [_cardData setTrack2:str];
    
    if([[str substringFromIndex:[str length] - 1] isEqualToString:@"F"]){
        track2Data = [str substringToIndex:[str length] - 1];
    }
    
    str=[self emvGetString:TAG_ISSUER_COUNTRY_CODE description:@"issuer country code"];
    
    str=[self emvGetString:TAG_APP_USAGE_CONTROL description:@"app usage control"];
    
    [self log:@"\nSet EMV Tags"];
    [self emvSetString:TAG_TERM_ACTION_ONLINE value:@"DC4004F800" description:@"terminal action online"];
    [self emvSetString:TAG_TERM_ACTION_DENIAL value:@"0010000000" description:@"terminal action denial"];
    [self emvSetString:TAG_TERM_ACTION_DEFAULT value:@"DC4000A800" description:@"terminal action default"];
    [self emvSetString:TAG_DEFAULT_DDOL value:@"9F3704" description:@"default ddol"];
    [self emvSetString:TAG_DEFAULT_TDOL value:@"9F3704" description:@"default tdol"];
    [self emvSetString:TAG_TRANSACTION_TYPE value:@"00" description:@"transaction type"];
    
    NSString *transactionAmount = [NSString stringWithFormat:@"%012lu",(unsigned long)[_requestMain getMinorAmount]];
    
    [self emvSetString:TAG_AMOUNT_AUTHORISED_NUM value:transactionAmount description:@"amount authorized numeric"];
    
    [self emvSetString:TAG_AMOUNT_OTHER_NUM value:[NSString stringWithFormat:@"%012lu",(unsigned long)[_requestMain getOtherAmount]] description:@"amount authorized numeric"];
    
    [self emvSetString:TAG_AMOUNT_AUTHORISED_BINARY value:@"000007D0" description:@"amount authorized binary"];
    
    [self emvSetString:TAG_TRANSACTION_CURR_CODE value:@"0566" description:@"currency code"];
//    pinpad.emvSetDataAsString(EMVTags.TAG_ACCOUNT_TYPE, "???"); throwOnEMVError(pinpad);
    
    //postLog("Set bypass mode\n");
    //pinpad.emvSetBypassMode(Pinpad.EMV_BYPASS_CURRENT_METHOD_MODE); throwOnEMVError(pinpad);
    
    [self log:@"\nAuthentication"];
    TEST_EMV(@"EMV Authentication", [dtdev emvAuthentication:false error:&error]);
    
    [self log:@"\nProcess restrictions"];
    TEST_EMV(@"Process restrictions", [dtdev emvProcessRestrictions:&error]);
    
    [self log:@"\nSet EMV tags"];
    [self emvSetString:TAG_RISK_AMOUNT value:@"00000000" description:@"risk amount"];
    [self emvSetString:TAG_FLOOR_LIMIT_CURRENCY value:@"0566" description:@"floor limit currency"];
    [self emvSetString:TAG_TERMINAL_FLOOR_LIMIT value:@"00000000" description:@"floor limit"];
    [self emvSetString:TAG_THRESHOLD_VALUE value:@"00000000" description:@"threshold value"];
    [self emvSetString:TAG_TARGET_PERCENTAGE value:@"20" description:@"target percentage"];
    [self emvSetString:TAG_MAX_TARGET_PERCENTAGE value:@"80" description:@"max target percentage"];
    
    [self log:@"\nTerminal risk"];
    TEST_EMV(@"Terminal risk", [dtdev emvTerminalRisk:false error:&error]);
    
    str=[self emvGetString:TAG_TVR description:@"terminal verification result"];
    
    
    NSData *encrypted;
    //    encrypted=[dtdev emvGetTagsEncrypted3DES:stringToData(@"5F 34") keyID:2 uniqueID:0x12345678 error:&error];
    //    encrypted=[dtdev emvGetTagsEncrypted3DES:stringToData(@"57") keyID:2 uniqueID:0x12345678 error:&error];
//    if([dtdev ppadPINEntry:0 startY:2 timeout:30 echoChar:'*' message:[NSString stringWithFormat:@"Amount: %.2f\nEnter PIN:",1.99] error:&error])
//    {
//        [self log:@"\nPIN entry complete"];
//        
//        NSString *pin=[self ppadGetPINBlockUsingDUKPT:&error];
//        
//        NSLog(@"Encrypted Pin %@ ",pin);
//    }
    
    
    [self processPINEntry:request];
    
}

-(void) processPINEntry:(EMVRequest *) request{
    
    NSError *error;
    NSString *str;
    
    //_requestMain = request;
    
    NSLog(@"Amount %lu",(unsigned long)request.getMinorAmount);
    
    bool completed = false;
    
    do {
        [self log:@"\nGet authentication method"];
        TEST_EMV(@"Get authentication method", [dtdev emvGetAuthenticationMethod:&error]);
        
        // This shows the please wait message on the PINPAD
        [PinpadHelper showWaiting:dtdev];
        
        if (dtdev.emvLastStatus == EMV_AUTH_COMPLETED) {
            [self log:@"\n  EMV Status: AUTH COMPLETED"];
            completed = true;
        } else {
            
            authResult = -1;
            
            NSString *amount = [[NSUserDefaults standardUserDefaults] objectForKey:AMOUNT];

            if (dtdev.emvLastStatus == EMV_ONLINE_PIN) {
                [self log:@"\n  EMV Status: ONLINE PIN"];
                
                [self log:@"\nEnter PIN"];
                
                online = TRUE;
                
                
                
                NSMutableDictionary *dict = [NSMutableDictionary dictionary];
                // [dict setValue:@"initialization" forKey:@"operation"];
                [dict setValue:@"payment" forKey:@"operation"];
                [dict setValue:@"enterPin" forKey:@"status"];
                
                [PaypadFacade sendNotification:dict wait:YES error:NO];

                // This is called to enter pin to the pinpad
                [PinpadHelper enterPinASync:dtdev amount:amount];
                
                return;
                
            } else if (dtdev.emvLastStatus == EMV_OFFLINE_PIN_CIPHERED || dtdev.emvLastStatus == EMV_OFFLINE_PIN_PLAIN)
                {
                    online = false;
                    
                    [self log:@"\n  EMV Status: OFFLINE PIN (CIPHERED/PLAIN)"];
                    
                    [self log:@"\nEnter PIN"];
                    
                    
                    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
                    // [dict setValue:@"initialization" forKey:@"operation"];
                    [dict setValue:@"payment" forKey:@"operation"];
                    [dict setValue:@"enterPin" forKey:@"status"];
                    
                    [PaypadFacade sendNotification:dict wait:YES error:NO];

                    // This tells request for the enter pin
                    [PinpadHelper enterPinASync:dtdev amount:amount];
                    
                    return ;
                }
        }
    } while (!completed);
				
    [self log:@"\nRead EMV tags"];
    str=[self emvGetString:TAG_CH_VERIF_METHOD_RESULT description:@"TAG_CH_VERIF_METHOD_RESULT"];
    
    // This set the cryptogram verification result
    [_response setCvmResult:str];
    
    str=[self emvGetString:TAG_TVR description:@"terminal verification result"];
    
    ///////////////////////////////////////////////////////////////////////
    // APPLICATION TRANSACTION DECISION
    ///////////////////////////////////////////////////////////////////////
    // If card is into hot list
    // pinpad.emvUpdateTVR(...); throwOnEMVError(pinpad);
    TEST_EMV(@"Set authentication result", [dtdev emvMakeTransactionDecision:&error]);
    int emvStatus = dtdev.emvLastStatus;
    
    int reqCertificate = 0;
    switch (emvStatus) {
        case EMV_TRANSACTION_APPROVED: {
            [self log:@"\n  EMV Status: TRANSACTION APPROVED"];
            
            bool OFFLINE_POSIBLE = true;
            if (OFFLINE_POSIBLE) {
                [self emvSetString:TAG_AUTH_RESP_CODE value:@"Y1" description:@"auth _response code"];
                reqCertificate = CERTIFICATE_TC;
            } else {
                reqCertificate = CERTIFICATE_ARQC;
            }
            break;
        }
        case EMV_TRANSACTION_DENIED: {
            [self log:@"\n  EMV Status: TRANSACTION DENIED"];
            [self emvSetString:TAG_AUTH_RESP_CODE value:@"Z1" description:@"auth _response code"];
            reqCertificate = CERTIFICATE_AAC;
            break;
        }
        case EMV_TRANSACTION_ONLINE: {
            [self log:@"\n  EMV Status: TRANSACTION ONLINE"];
            reqCertificate = CERTIFICATE_ARQC;
            break;
        }
        default: {
        }
    }
    
    [self log:@"\nGenerate certificate (CDOL 1)"];
    TEST_EMV(@"Generate certificate", [dtdev emvGenerateCertificate:reqCertificate risk:CDOL_1 error:&error]);
    
    str=[self emvGetString:TAG_CRYPT_INFO_DATA description:@"TAG_CRYPT_INFO_DATA"];
    
    // This sets the cryptogram information data
    [_response setCryptogramInformationData:str];
    
    unsigned int cryptogramInfo;
    NSScanner *scanner = [NSScanner scannerWithString:str];
    [scanner scanHexInt:&cryptogramInfo];
    
    str=[self emvGetString:TAG_PAN description:@"pan"];
    
    str = [self readTagAsString:TAG_PAN];
    
    NSString *pan = str;
    
    if([[str substringFromIndex:[str length] - 1] isEqualToString:@"F"]){
        pan = [str substringToIndex:[str length] - 1];
    }
    
    _cardData = [[EmvCardData alloc] init];
    
    // This prints out the pan in plain
    NSLog(@"PAN: %@",pan);
    
    [_response setPan:pan];
    
    NSData *panData = [dtdev emvGetDataAsBinary:TAG_PAN error:nil];
    
    [_cardData setPan:panData];
    
    str=[self emvGetString:TAG_PAN_SEQUENCE_NUMBER description:@"pan sequence number"];
    
    // This gets the card pan sequence no in clear
    str = [self readTagAsString:TAG_PAN_SEQUENCE_NUMBER];
    
    [_cardData setCardSequenceNo:str];
    
    str=[self emvGetString:TAG_TRACK2_EQUIVALENT_DATA description:@"track 2 equivalent data"];
    
    str = [self readTagAsString:TAG_TRACK2_EQUIVALENT_DATA];
    
    [_cardData setTrack2:str];
    
    track2Data = str;
    
    if([[str substringFromIndex:[str length] - 1] isEqualToString:@"F"]){
        track2Data = [str substringToIndex:[str length] - 1];
    }
    
    str = [self emvGetString:TAG_APP_LABEL description:@"TAG_APP_LABEL"];

    [[NSUserDefaults standardUserDefaults] setObject:str forKey:@"TENDER_TYPE"];
    
    [_cardData setAppLabel:str];
    
    str = [self emvGetString:TAG_EXPIRY_DATE description:@"TAG_EXPIRY_DATE"];
    
    [_cardData setCardExpiryDate:str];
    
    str = [self emvGetString:TAG_CARDHOLDER_NAME description:@"TAG_CARDHOLDER_NAME"];

    [[NSUserDefaults standardUserDefaults] setValue:str forKey:@"CARD_HOLDER"];
    
    [_cardData setCardHolderName:str];
    
    str = [self emvGetString:0x4F description:@"APP_ID"];

    [[NSUserDefaults standardUserDefaults] setValue:str forKey:@"AID"];
    
    // This sets the application ID
    [_cardData setAID:str];
    
    str=[self emvGetString:TAG_TRANSACTION_DATE description:@"TAG_TRANSACTION_DATE"];
    
    [_response setTransDate:str];
    
    str=[self emvGetString:TAG_ATC description:@"TAG_ATC"];
    
    [_response setATC:str];
    
    str=[self emvGetString:TAG_APP_CRYPTOGRAM description:@"TAG_APP_CRYPTOGRAM"];
    
    // This sets the cryptogram
    [_response setCryptogram:str];
    
    str=[self emvGetString:TAG_ISSUER_APP_DAT description:@"TAG_ISSUER_APP_DAT"];
    
    [_response setIAD:str];
    
    str=[self emvGetString:TAG_TVR description:@"TAG_TVR"];
    
    // This sets the TVR
    [_response setTVR:str];
    
    str=[self emvGetString:TAG_TSI description:@"TAG_TSI"];
    
    [_response setTSI:str];
    
    str=[self emvGetString:TAG_AIP description:@"TAG_AIP"];
    
    [_response setAIP:str];
    
    str=[self emvGetString:TAG_UNPREDICTABLE_NUMBER description:@"TAG_UNPREDICTABLE_NUMBER"];
    
    [_response setUnpredictableNo:str];
    
    str=[self emvGetString:TAG_AMOUNT_AUTHORISED_NUM description:@"TAG_AMOUNT_AUTHORISED_NUM"];
    
    [_response setAmountAuthorised:str];
    
    str=[self emvGetString:TAG_AMOUNT_OTHER_NUM description:@"TAG_AMOUNT_OTHER_NUM"];
    
    [_response setAmountOther:str];
    
    str=[self emvGetString:TAG_APP_VERSION_NUMBER description:@"TAG_APP_VERSION_NUMBER"];
    
    [_response setAppVersionName:str];
    
    str=[self emvGetString:TAG_PAN_SEQUENCE_NUMBER description:@"TAG_PAN_SEQUENCE_NUMBER"];
    
    [_response setPanSequenceNo:str];
    
    str=[self emvGetString:TAG_TRANSACTION_TYPE description:@"TAG_TRANSACTION_TYPE"];
    
    [_response setTransactionType:str];
    
    str=[self emvGetString:TAG_TERMINAL_CAPABILITIES description:@"TAG_TERMINAL_CAPABILITIES"];
    
    [_response setTerminalCapabilities:str];
    
    str=[self emvGetString:TAG_TERMINAL_TYPE description:@"TAG_TERMINAL_TYPE"];
    
    [_response setTerminalType:str];
    
    str=[self emvGetString:TAG_TERMINAL_COUNTRY_CODE description:@"TAG_TERMINAL_COUNTRY_CODE"];
    
    [_response setTerminalCountryCode:str];
    
//    str=[self emvGetString:TAG_TERMINAL_COUNTRY_CODE description:@"TAG_TERMINAL_COUNTRY_CODE"];
//    
//    [_response setTerminalCountryCode:str];
    
    str=[self emvGetString:TAG_TRANSACTION_CURR_CODE description:@"TAG_TRANSACTION_CURR_CODE"];
    
    [_response setTransactionCurrencyCode:str];
    
    str=[self emvGetString:TAG_TRANSACTION_CATEGORY_CODE description:@"TAG_TRANSACTION_CATEGORY_CODE"];
    
    [_response setTransactionCategoryCode:str];
    
    str=[self emvGetString:TAG_TRANSACTION_CATEGORY_CODE description:@"TAG_TRANSACTION_CATEGORY_CODE"];
    
    [_response setTransactionCategoryCode:str];
    
    
    // This sets the card data in the _response variable
    [_response setEmvCardData:_cardData];
    
    
    // Check cryptogram information data after certificate generation.
    switch ((cryptogramInfo >> 6)) {
        case CERTIFICATE_AAC: {
            [self displayAlert:@"Error" message:@"Transaction is denied by kernel"];
            return;
        }
        case CERTIFICATE_TC: {
            if (reqCertificate < CERTIFICATE_TC) {
                [self displayAlert:@"Error" message:@"Transaction is denied (AC requested < TC)"];
            } else {
                [self displayAlert:@"Success" message:@"Transaction is offline authorized"];
            }
            return;
            break;
        }
        case CERTIFICATE_ARQC: {
            if (reqCertificate < CERTIFICATE_ARQC) {
                [self displayAlert:@"Error" message:@"Transaction is denied (AC requested < ARQC)"];
                return;
            } else {
                // Online processing
                [self processEMVOnlineTransaction];
            }
            break;
        }
        default: {
            [self displayAlert:@"Error" message:@"Invalid certificate data"];
            return;
        }
    }
    
    TEST_EMV(@"*** Deinit EMV Kernel",[dtdev emvDeinitialise:&error]);
    
    [PinpadHelper removeCard:dtdev];
    [PinpadHelper initScreen:dtdev];
    
}

/**
    This will be called to process the request online
 */
-(void) processEMVOnlineTransaction{
    
    NSDate *date = [NSDate date];
    
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    
    NSString *currentDate = [dateFormatter stringFromDate:date];
    
    NSString *pan = [_response getPan];
    
    NSString *accountType = [[NSUserDefaults standardUserDefaults] objectForKey:ACCOUNT_TYPE_FROM];
    
    NSString *processingCode = [NSString stringWithFormat:@"00%@00",accountType];
    
    NSString *transactionAmount = [NSString stringWithFormat:@"%012lu",(unsigned long)_requestMain.getMinorAmount];
    
    // This checks if there is a value for the stan in the storage
    NSString *stan = [[NSUserDefaults standardUserDefaults] objectForKey:STAN_VAS];
    
    if([stan length] == 0){
        stan = @"000005";
    }
    
    NSString *cardSequenceNo = [[_response getEmvCardData] getCardSequenceNo];
    
    if(cardSequenceNo.length == 2){
        cardSequenceNo = [NSString stringWithFormat:@"0%@",cardSequenceNo];
    }
    
    stan = [NSString stringWithFormat:@"%06d",[stan intValue]];
    
    long value = [stan intValue];
    
    value += 1;
    
    // [[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithFormat:@"%06ld",value] forKey:STAN_VAS];
    
    NSString *rrn = [[NSUserDefaults standardUserDefaults] objectForKey:RRN_VAS];
    
    // This checks if there is a value for the rrn in the storage
    if([rrn length] == 0){
        rrn = [NSString  stringWithFormat:@"%012lu",5L];
    }
    
    rrn = [NSString stringWithFormat:@"%012lld",[rrn longLongValue]];
    
    value = [rrn intValue];
    
    value += 1;
    
    // [[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithFormat:@"%012ld",value] forKey:RRN];
    
    [dateFormatter setDateFormat:@"MM"];
    NSString *month = [NSString stringWithFormat:@"%02d",[[dateFormatter stringFromDate:date] intValue]];
    
    [dateFormatter setDateFormat:@"dd"];
    NSString *day = [NSString stringWithFormat:@"%02d",[[dateFormatter stringFromDate:date] intValue]];
    
    NSString *localTransactionDate = [NSString stringWithFormat:@"%@%@",month,day];
    
    [dateFormatter setDateFormat:@"HHmmss"];
    NSString *localTransactionTime = [dateFormatter stringFromDate:date];
    
    NSString *transmissionDateTime = [NSString stringWithFormat:@"%@%@",localTransactionDate,localTransactionTime];
    
    NSString *cardExpiryDate;
    
    // This ensures that the exp
    if([[_response.getEmvCardData getCardExpiryDate] length] == 4){
        cardExpiryDate = [_response.getEmvCardData getCardExpiryDate];
    }else{
        cardExpiryDate = [[_response.getEmvCardData getCardExpiryDate] substringWithRange:NSMakeRange(0,4)];
    }
    
    NSString *merchantType = [[NSUserDefaults standardUserDefaults] objectForKey:MERCHANT_TYPE_VAS];
    NSString *cardAcceptorIdCode = [[NSUserDefaults standardUserDefaults] objectForKey:ACCEPTOR_ID_VAS];
    NSString *cardAcceptorName = [[NSUserDefaults standardUserDefaults] objectForKey:ACCEPTOR_NAME_VAS];
    
    NSString *terminalId = [[NSUserDefaults standardUserDefaults] objectForKey:TERMINAL_ID_VAS];
    
    if([terminalId length] <= 0){
        terminalId = @"2011225S";
    }
    
    int index;
    
    if([track2Data containsString:@"D"]){
        index = [track2Data rangeOfString:@"D"].location;
    }else{
        index = [track2Data rangeOfString:@"="].location;
    }
    
    NSString *temp = [track2Data substringWithRange:NSMakeRange(index + 1, 7)];
    
    NSString *serviceRestrictionCode = [temp substringFromIndex:4];
    
    /* Data used for Reversal in case of timeout */
//    String originaldataelements = String.format("%s%s%s%s%s", "0200", stan,
//                                                transmissionDateTime, "00000111129", "00000000000");
    
    originalDataElements = [NSString stringWithFormat:@"%@%@%@%@%@", @"0200", stan, transmissionDateTime,
                            @"00000111129", @"00000000000"];
    
    replacementAmounts = [NSString stringWithFormat:@"%@%@%@%@", transactionAmount,transactionAmount,@"C00000000",@"C00000000"];
    
//    replacementAmounts = String.format("%s%s%s%s",
//                                              transactionAmount, transactionAmount, "C00000000", "C00000000");
    
    NSString *currencyCode = _requestMain.getTransactionCurrencyCode;
    
    NSString *securityRelatedInformation = @"";
    NSString *messageReasonCode = @"";
    NSString *paymentInformation = @"";
    
    PurchaseRequest *request = [[PurchaseRequest alloc] init];
    [request setPan:pan];
    [request setProcessingCode:processingCode];
    [request setTransactionAmount:transactionAmount];
    [request setTransmissionDateTime:transmissionDateTime];
    [request setStan:stan];
    [request setLocalTransactionDate:localTransactionDate];
    [request setLocalTransactionTime:localTransactionTime];
    [request setCardExpirationDate:cardExpiryDate];
    [request setMerchantType:merchantType];
    [request setPosEntryMode:POSENTRYMODE];
    [request setCardSequenceNumber:cardSequenceNo];
    [request setPosConditionCode:POSCONDITIONCODE];
    [request setPosPinCaptureCode:POSPINCAPTURECODE];
    [request setTransactionFee:TRANSACTIONFEE];
    [request setAcquiringInstIdCode:ACQUIRINGINSTIDCODE];
    [request setTrack2Data:track2Data];
    [request setRetrievalReferenceNumber:rrn];
    [request setTerminalId:terminalId];
    [request setCardAcceptorIdCode:cardAcceptorIdCode];
    [request setCardAcceptorNameLocation:cardAcceptorName];
    [request setPinData:_response.pinData];
    [request setCurrencyCode:currencyCode];
    [request setIccData:[[self packICCDataJpos] stringByReplacingOccurrencesOfString:@" " withString:@""]];
    [request setPosDataCode:POSDATACODE];
    [request setServiceRestrictionCode:serviceRestrictionCode];
    [request setSecurityRelatedInformation:securityRelatedInformation];
    [request setAdditionalAmounts:_response.getAmountOther];
    [request setMessageReasonCode:messageReasonCode];
    [request setTransportData:TRANSPORT_DATA];
    [request setPaymentInformation:paymentInformation];
    [request setOriginalDataElements:originalDataElements];
    
    // Specify if default processing is permited
    bool OFFLINE_PROCESSING =true;
    // Simulate communication failure
    bool COMMUNICATION_FAILURE =false;
    
    int emvStatus = 0;
    int emvReqCer = 0;
    
    NSError *error;
    // TODO: Build & transmit online request to issuer
    
    NSString *authData = nil;
    NSString *authCode = nil;
    NSString *responseCode = nil;
    NSString *responseMessage = nil;

    if(COMMUNICATION_FAILURE)
    {
        [self log:@"\n  Connection failed"];
        
        if(OFFLINE_PROCESSING)
        {
            
            
            
            [self log:@"\n  Make default decision"];
            TEST_EMV(@"Make default decision",[dtdev emvMakeDefaultDecision:&error]);
            switch (dtdev.emvLastStatus) {
                case EMV_TRANSACTION_APPROVED: {
                    [self emvSetString:TAG_AUTH_RESP_CODE value:@"Y3" description:@"_response code"];
                    break;
                }
                case EMV_TRANSACTION_DENIED: {
                    [self emvSetString:TAG_AUTH_RESP_CODE value:@"Z3" description:@"_response code"];
                    break;
                }
                default: {
                }
            }
        } else {
            [self emvSetString:TAG_AUTH_RESP_CODE value:@"Z3" description:@"_response code"];
            emvStatus = EMV_TRANSACTION_DENIED;
        }
    } else {
        // Issuer authentication data is received.
        // pinpad.emvSetDataAsString(EMVTags.TAG_ISSUER_AUTH_DAT, "FDD6818EE93367463030"); throwOnEMVError(pinpad);
        // pinpad.emvAuthenticateIssuer(); throwOnEMVError(pinpad);
        
        // Script 71 is received
        // pinpad.emvSetDataAsBinary(EMVTags.TAG_ISSUER_SCRIPTS, ...); throwOnEMVError(pinpad);
        // pinpad.emvScriptProcessing(0x71);
        
        // Here we need to take decision from issuer host..
        
        // This connects to Nibss to process the transaction
        
        PurchaseResponse *presponse =  [NibssIsoProcessor process:request];
        
        
        authData = [[NSUserDefaults standardUserDefaults] objectForKey:AUTH_DATA];
        
        NSLog(@"AUTH DATA: %@", authData);
        
        if(authData.length > 0){
            NSError *error;
            
            [dtdev emvSetDataAsBinary:TAG_ISSUER_AUTH_DAT data:[authData stringToData] error:&error];
            
            if(error){
                // NSLog(@"Error while updating issuer data %@",[error localizedDescription]);
                // return ;
            }
            
            [dtdev emvAuthenticateIssuer:&error];
            
            if(error){
                // NSLog(@"Error while updating issuer data %@",[error localizedDescription]);
            }
            
            [[NSUserDefaults standardUserDefaults] setObject:nil forKey:AUTH_DATA];
        }
        
        NSString *script71 = [[NSUserDefaults standardUserDefaults] objectForKey:SCRIPT71];
        
        NSLog(@"AUTH DATA: %@", authData);
        
        if(script71.length > 0){
            NSError *error;
            
            [dtdev emvSetDataAsBinary:TAG_ISSUER_SCRIPTS data:[script71 hexStringToData] error:&error];
            
            if(error){
                // NSLog(@"Error while updating issuer data %@",[error localizedDescription]);
                // return ;
            }
            
            [dtdev emvScriptProcessing:0x71 error:&error];
            
            if(error){
                // NSLog(@"Error while updating issuer data %@",[error localizedDescription]);
            }
            
            [[NSUserDefaults standardUserDefaults] setObject:nil forKey:SCRIPT71];
        }
        
        responseMessage = presponse.responseMessage;
        
        responseCode = [[NSUserDefaults standardUserDefaults] objectForKey:RESPONSE_CODE];
        
        NSArray *messages = [responseMessage componentsSeparatedByString:@" "];
        
        NSMutableString *messageToShow = [[NSMutableString alloc] init];
        for (int i = 0; i < messages.count; i++) {
            
            NSString *tempStr = [messages objectAtIndex:i];
            
            if( i >= 0 && tempStr.length >= 10 )
                [messageToShow appendString:[NSString stringWithFormat:@" %@  \n",tempStr]];
            else
                [messageToShow appendString:[NSString stringWithFormat:@" %@ ",tempStr]];
        }
        
        
        // This is called to tidy up the transactions
        [[NSUserDefaults standardUserDefaults] setObject:@"XX" forKey:RESPONSE_CODE];
        
        [[NSUserDefaults standardUserDefaults] setObject:@"unauthenticated" forKey:AUTH_CODE];
        
        [[NSUserDefaults standardUserDefaults] setObject:nil forKey:AUTH_DATA];
        
        [[NSUserDefaults standardUserDefaults] setObject:nil forKey:SCRIPT71];
        
        [[NSUserDefaults standardUserDefaults] setObject:nil forKey:SCRIPT72];
        
        NSString *maskedPan = [[NSUserDefaults standardUserDefaults] objectForKey:@"MASKED_PAN"];
        NSString *chHolder =  [[NSUserDefaults standardUserDefaults] objectForKey:@"CARD_HOLDER"];
        NSString *aid =  [[NSUserDefaults standardUserDefaults] objectForKey:@"AID"];
        NSString *tender =  [[NSUserDefaults standardUserDefaults] objectForKey:@"TENDER_TYPE"];
        
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        // [dict setValue:@"initialization" forKey:@"operation"];
        [dict setValue:@"payment" forKey:@"operation"];
        [dict setValue:maskedPan forKey:@"pan"];
        [dict setValue:chHolder forKey:@"cardholder"];
        [dict setValue:[request getStan] forKey:@"stan"];
        [dict setValue:[request getRetrievalReferenceNumber] forKey:@"rrn"];
        [dict setValue:authCode forKey:@"auth"];
        [dict setValue:cardExpiryDate forKey:@"expiry"];
        [dict setValue:responseCode forKey:@"responsecode"];
        [dict setValue:responseMessage forKey:@"responsemessage"];
        [dict setValue:aid forKey:@"aid"];
        [dict setValue:[request getCardSequenceNumber] forKey:@"sequenceno"];
        [dict setValue:tender forKey:@"tendertype"];
        [dict setValue:tender forKey:@"accounttype"];

        [PaypadFacade sendNotification:dict wait:NO error:NO];

        if([responseCode isEqualToString:@"00"])
            [PinpadHelper showSuccessful:dtdev];
        else
            [PinpadHelper showMessage:dtdev message:messageToShow];
        
//        [PinpadHelper showMessage:dtdev message:@"  TRANSACTION \n   SUCCESSFUL"];
//        
//        [PinpadHelper clearScreen:dtdev];
//        
//        [PinpadHelper initScreen:dtdev];
        
        // appendWarning("  Issuer host denied transaction\n");
        // emvStatus = EMVStatusCodes.EMV_TRANSACTION_DENIED;
        [self log:@"\n  Connection established"];
        
        //TODO: Host approval procedure
        [self log:@"\n  ....."];
        
        [self log:@"\n  Issuer host approve transaction"];
        [self emvSetString:TAG_AUTH_RESP_CODE value:@"00" description:@"_response code"];
        
        emvStatus = EMV_TRANSACTION_APPROVED;
    }
    
    switch (emvStatus) {
        case EMV_TRANSACTION_APPROVED: {
            [self log:@"\n Generate TC certificate (CDOL 2)"];
            TEST_EMV(@"Generate certificate", [dtdev emvGenerateCertificate:CERTIFICATE_TC risk:CDOL_2 error:&error]);
            emvReqCer = CERTIFICATE_TC;
            break;
        }
        case EMV_TRANSACTION_DENIED: {
            [self log:@"\n Generate AAC certificate (CDOL 2)"];
            TEST_EMV(@"Generate certificate", [dtdev emvGenerateCertificate:CERTIFICATE_AAC risk:CDOL_2 error:&error]);
            emvReqCer = CERTIFICATE_AAC;
            break;
        }
        default: {
        }
    }

    NSString *script72 = [[NSUserDefaults standardUserDefaults] objectForKey:SCRIPT72];
    //
    if(script72.length > 0){
        NSError *error;
    
        [dtdev emvSetDataAsBinary:TAG_ISSUER_SCRIPTS data:[script72 hexStringToData] error:&error];
    
        if(error){
            NSLog(@"Error while updating issuer data %@",[error localizedDescription]);
            // return ;
        }
    
        [dtdev emvScriptProcessing:0x71 error:&error];
    
        if(error){
            // NSLog(@"Error while updating issuer data %@",[error localizedDescription]);
        }
    }

    
    switch (dtdev.emvLastStatus) {
        case EMV_SUCCESS:
            break;
        case EMV_ABORT_TRANSACTION:
        case EMV_ERROR_AC_PROCESS:
            [self displayAlert:@"Error" message:[NSString stringWithFormat:@"Transaction aborted with status: %d",dtdev.emvLastStatus]];
            return;
        case EMV_CDA_FAILED:
            [self displayAlert:@"Error" message:[NSString stringWithFormat:@"Transaction denied with status: %d",dtdev.emvLastStatus]];
            return;
        default:
            [self displayAlert:@"Error" message:[NSString stringWithFormat:@"Transaction denied with status: %d",dtdev.emvLastStatus]];
            return;
    }
    
    NSString *str=[self emvGetString:TAG_CRYPT_INFO_DATA description:@"TAG_CRYPT_INFO_DATA"];
    
    unsigned int cryptogramInfo;
    NSScanner *scanner = [NSScanner scannerWithString:str];
    [scanner scanHexInt:&cryptogramInfo];
    
    str=[self emvGetString:TAG_ATC description:@"TAG_ATC"];
    str=[self emvGetString:TAG_APP_CRYPTOGRAM description:@"TAG_APP_CRYPTOGRAM"];
    str=[self emvGetString:TAG_ISSUER_APP_DAT description:@"TAG_ISSUER_APP_DAT"];
    str=[self emvGetString:TAG_TVR description:@"TAG_TVR"];
    str=[self emvGetString:TAG_TSI description:@"TAG_TVR"];
    
    // Check cryptogram information data after certificate generation.
    switch ((cryptogramInfo >> 6)) {
        case CERTIFICATE_TC: {
            if (emvReqCer < CERTIFICATE_TC) {
                [self displayAlert:@"Error" message:@"Transaction is denied (AC requested < TC)"];
            } else {
                [self displayAlert:@"Success" message:@"Transaction is offline authorized"];
                break;
            }
        default: {
            [self displayAlert:@"Error" message:@"Transaction is denied"];
        }
        }
    }
    
    
    
}

-(NSString *)emvGetString:(int)tag description:(NSString *)description
{
    NSError *error;
    NSString *str;
    
    str=[dtdev emvGetDataAsString:tag error:&error];
    if(!str)
    {
        [self log:[NSString stringWithFormat:@"*** Get %@ failed: %@",description,error.localizedDescription]];
        return nil;
    }
    [self log:[NSString stringWithFormat:@"*** Get %@ succeeded\nEMV Status: %d",description,dtdev.emvLastStatus]];
    [self log:[NSString stringWithFormat:@"Value: %@",str]];
    
    return str;
}

-(void)log:(NSString *)text
{
    NSLog(@"%@",text);
    //logView.text=[logView.text stringByAppendingFormat:@"\n%@",text];
}

-(void)PINEntryCompleteWithError:(NSError *)error;
{
    if(error){
        NSLog(@"Error %@",[error localizedDescription]);
        [self endOperation];
        
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        // [dict setValue:@"initialization" forKey:@"operation"];
        [dict setValue:@"payment" forKey:@"operation"];
        [dict setValue:@"error" forKey:@"status"];
        
        [PaypadFacade sendNotification:dict wait:NO error:YES];

        [PinpadHelper clearScreen:dtdev];
        [PinpadHelper initScreen:dtdev];

        return ;
        
    }else{
        
        authResult == -1;

        if(online == TRUE){
            
            NSLog(@"This transaction is going online");
            
            NSString *pinData = [self pinDecryptMasterSession:&error];
            
            // This sets the PIN Data for online transaction
            [_response setPinData:pinData];
            
            NSLog(@"This is pin block %@",pinData);
            
            authResult = AUTH_RESULT_SUCCESS;
            
        }else{
            
            NSLog(@"This is an offline transaction");
            
            if (authResult == -1)
            {
                [self log:@"\nVerify PIN offline"];
                TEST_EMV(@"Verify PIN Offline", [dtdev emvVerifyPinOffline:&error]);
                if (dtdev.emvLastStatus == EMV_SUCCESS) {
                    authResult = AUTH_RESULT_SUCCESS;
                    
                    [PinpadHelper showValidPin:dtdev];
                    
                } else {
                    authResult = AUTH_RESULT_FAILURE;
                    
                    [PinpadHelper showInvalidPin:dtdev];
                }
            }
            
            NSLog(@"This transaction is offline");
        }
        
        TEST_EMV(@"Set authentication result", [dtdev emvSetAuthenticationResult:authResult error:&error]);
        
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        // [dict setValue:@"initialization" forKey:@"operation"];
        [dict setValue:@"payment" forKey:@"operation"];
        [dict setValue:@"processing" forKey:@"status"];
        
        [PaypadFacade sendNotification:dict wait:YES error:NO];

        [PinpadHelper clearScreen:dtdev];
        [PinpadHelper initScreen:dtdev];

        [self processPINEntry:_requestMain];

        return ;
    }
    
//    [self endOperation];
}

-(void)endOperation{
    
    NSLog(@"This is the end of operation");
    [PinpadHelper clearScreen:dtdev];
    
}

-(void)displayAlert:(NSString *) title message:(NSString *) message
{
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:nil
                                          cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
    [alert show];
    NSLog(@"%@ : %@",title, message);
}

-(bool)emvSetString:(int)tag value:(NSString *)value description:(NSString *)description
{
    NSError *error;
    
    bool r=[dtdev emvSetDataAsString:tag data:value error:&error];
    if(!r)
    {
        [self log:[NSString stringWithFormat:@"*** Set %@ failed: %@",description,error.localizedDescription]];
        return nil;
    }
    [self log:[NSString stringWithFormat:@"*** Set %@ succeeded\nEMV Status: %d",description,dtdev.emvLastStatus]];
    
    return r;
}

static NSString *hexToString(NSString * label, const void *data, size_t length)
{
    const char HEX[]="0123456789ABCDEF";
    char s[2000];
    for(int i=0;i<length;i++)
    {
        s[i*3]=HEX[((uint8_t *)data)[i]>>4];
        s[i*3+1]=HEX[((uint8_t *)data)[i]&0x0f];
        s[i*3+2]=' ';
    }
    s[length*3]=0;
    
    if(label)
        return [NSString stringWithFormat:@"%@(%d): %s",label,(int)length,s];
    else
        return [NSString stringWithCString:s encoding:NSASCIIStringEncoding];
}

static DTEMVApplication *createApp(NSString *aid, NSString *name, int match)
{
    DTEMVApplication *r= [[DTEMVApplication alloc] init];
    r.aid=stringToData(aid);
    r.label=[name copy];
    r.matchCriteria=match;
    
    return r;
}

static NSData *stringToData(NSString *text)
{
    NSMutableData *d=[NSMutableData data];
    text=[text lowercaseString];
    int count=0;
    uint8_t b=0;
    for(int i=0;i<text.length;i++)
    {
        b<<=4;
        char c=[text characterAtIndex:i];
        if(c<'0' || (c>'9' && c<'a') || c>'f')
        {
            b=0;
            count=0;
            continue;
        }
        if(c>='0' && c<='9')
            b|=c-'0';
        else
            b|=c-'a'+10;
        count++;
        if(count==2)
        {
            [d appendBytes:&b length:1];
            b=0;
            count=0;
        }
    }
    return d;
}

-(NSData *)getTagsEncryptedDUKPT:(NSData *)tags
{
    NSError *error;
    uint8_t decrypted[512];
    
    NSData *dukptPacket=[dtdev emvGetTagsEncryptedDUKPT:tags keyID:0 uniqueID:0x12345678 error:&error];
    
    if(dukptPacket)
    {
        //get the KSN, last 10 bytes
        NSData *ksn=[dukptPacket subdataWithRange:NSMakeRange(dukptPacket.length-10, 10)];
    
//        NSData *ksn = [dtdev ppadGetDUKPTKeyKSN:0 error:nil];
    
        //calculate the IPEK based on the BDK and serial number
        //insert your own BDK here and calculate the IPEK, for the demo we are using predefined BDK
        
        NSString *bdk = [[NSUserDefaults standardUserDefaults] objectForKey:BDK_LOCATION];
        
        NSData *bdkData = [bdk hexStringToData];
        
        uint8_t ipek[16] ;
        //the device specific ipek, will be derived from the BDK
        //derive ipek from bdk
//        dukptDeriveIPEK([bdkData bytes], ksn.bytes, ipek);
        
        NSString *dataKeyString = [[NSUserDefaults standardUserDefaults] objectForKey:DATA_KEY_LOCATION];
        
//        [hex]
        //generate the data key
        uint8_t dataKey[16];
        dukptCalculateDataKey(ksn.bytes, [bdkData bytes], dataKey);
        //decrypt using it
        size_t dataLen= dukptPacket.length-10;
//        trides_crypto(kCCDecrypt,0,tags,[dataKeyString length],decrypted,[dataKeyString hexStringToData]);
        trides_crypto(kCCDecrypt,0,dukptPacket.bytes,dataLen,decrypted,dataKey);
        
//        NSString* dd = [NSData dataWithBytes:decrypted length:<#(NSUInteger)#>]
        
        //packet structure:
        //random [4 bytes]
        //uniqueID [4 bytes]
        //payload len [2 bytes]
        //payload [variable]
        //crc16 ccit [2 bytes] FFFF based
        int payloadLen=(decrypted[4+4+0]<<8)|decrypted[4+4+1];
        if(payloadLen<dataLen)
        {
            //get the data
            NSData *result=[NSData dataWithBytes:&decrypted[4+4+2] length:payloadLen];
            //calculate crc
            uint16_t crcPacket=(decrypted[4+4+2+payloadLen+0]<<8)|decrypted[4+4+2+payloadLen+1];
            uint16_t crcCalculated=[self crc16:decrypted length:4+4+2+payloadLen crc16:0xFFFF];
            if(crcPacket==crcCalculated)
            {//valid packet
                NSLog(@"Decrypted packet: %@",result);
                
                NSString *pan = [self toHexString:[result bytes] length:[result length] space:false];
                
                NSLog(@"%@",pan);
                return result;
            }
        }
    }
    return nil;
}

-(NSString *)pinDecryptMasterSession:(NSError **)error
{
    //master/session assuming a trides_key is loaded at position 2
    NSData *decryptedData=nil;
    uint8_t trides_key[]={0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11};
    uint8_t random_key[]={0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22};
    uint8_t encrypted_random_key[16]={0};
    
//    trides_crypto(kCCEncrypt,kCCOptionECBMode,random_key,sizeof(random_key),encrypted_random_key,trides_key);
    
    NSString *pinKey = [[NSUserDefaults standardUserDefaults] objectForKey:TPK_VAS_LOCATION];
    
    NSData *pinKeyData = [pinKey hexStringToData];
    
    //try to get the encrypted data, it will work only if the keys are already set
    NSData *pinData=[dtdev ppadGetPINBlockUsingMasterSession:pinKeyData fixedKeyID:KEY_PIN_VAS_INDEX pinFormat:PIN_FORMAT_ISO0 error:error];
    
//    if(pinData)
//    {
//        uint8_t decrypted_pin_block[8]={0};
//        trides_crypto(kCCDecrypt,kCCOptionECBMode,pinData.bytes,pinData.length,decrypted_pin_block,random_key);
//        decryptedData=[NSData dataWithBytes:decrypted_pin_block length:sizeof(decrypted_pin_block)];
//        
//        return [self pinExtractISO1:decryptedData];
//    }
    
    if(pinData){
        // enters here if pin data is available
        
        return [self toHexString:[pinData bytes] length:[pinData length] space:false];
        
    }
    
    NSLog(@"Getting pin failed");
    return nil;
}

-(uint16_t)crc16:(const uint8_t *)data  length:(int)length crc16:(uint16_t)crc16
{
    if(length==0) return 0;
    int i=0;
    while(length--)
    {
        crc16=(uint8_t)(crc16>>8)|(crc16<<8);
        crc16^=*data++;
        crc16^=(uint8_t)(crc16&0xff)>>4;
        crc16^=(crc16<<8)<<4;
        crc16^=((crc16&0xff)<<4)<<1;
        i++;
    }
    return crc16;
}

-(NSString *)pinExtractISO1:(NSData *)data
{
    if(data==nil || data.length==0)
        return nil;
    
    const uint8_t *bytes=data.bytes;
    int pinLen=bytes[0]&0x0f;
    NSString *pin=[self toHexString:(uint8_t *)data.bytes length:data.length space:false];
    pin=[pin substringWithRange:NSMakeRange(2, pinLen)];
    NSLog(@"Decrypted pin: %@",pin);
    return pin;
}

-(NSString *)toHexString:(const void *)data length:(size_t)length space:(bool)space
{
    const char HEX[]="0123456789ABCDEF";
    char s[2000];
    
    int len=0;
    for(int i=0;i<length;i++)
    {
        s[len++]=HEX[((uint8_t *)data)[i]>>4];
        s[len++]=HEX[((uint8_t *)data)[i]&0x0f];
        if(space)
            s[len++]=' ';
    }
    s[len]=0;
    return [NSString stringWithCString:s encoding:NSASCIIStringEncoding];
}

-(void)viewDidLoad{

//    dtdev = [DTDevices sharedDevice];
//    [dtdev addDelegate:self];
//    [super viewDidLoad];
    
}

-(NSString*)readTagAsString:(int) tag{
    
//    NSError *error;

    NSString *tagStr = [NSString stringWithFormat:@"%x",tag];
    
    NSData *dd = [self getTagsEncryptedDUKPT:stringToData(tagStr)];
    
    NSArray *array = [TLV decodeTags:dd];
    
    NSUInteger val = 0;
    
    TLV *tlv = [array objectAtIndex:val ];
    
    NSData *data = [tlv data];
    
    NSString *dataStr = [self toHexString:[data bytes] length:[data length] space:false];
    
    NSLog(@"DATA %@", dataStr);
    
    return dataStr;
}


-(NSString *) getICCTime:(NSDate *) date{
    NSString *time = @"";
    
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"hhmmss"];
    
    NSDate *dateValue = date;
    
    if(date == NULL){
        dateValue = [NSDate date];
    }
    
    time = [formatter stringFromDate:dateValue];
    return time;
}

-(NSString *) getICCDate: (NSDate *) dateValue{
//    NSString *time = @"";
    
//    NSDate *date = [NSDate date];
    
//    NSCalendar *calender = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
//    
//    NSCalendarUnit units = NSCalendarUnitYear | NSCalendarUnitWeekOfMonth | NSCalendarUnitDay ;
    
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    
    NSDate *date = dateValue;
    
    if(date == NULL){
        date = [NSDate date];
    }
    
    // This gets the year from the date
    [formatter setDateFormat:@"yy"];
    NSString *year = [NSString stringWithFormat:@"%02d",[[formatter stringFromDate:date] intValue]];
    
    [formatter setDateFormat:@"MM"];
    NSString *month = [NSString stringWithFormat:@"%02d",[[formatter stringFromDate:date] intValue]];
    
    [formatter setDateFormat:@"dd"];
    NSString *day = [NSString stringWithFormat:@"%02d",[[formatter stringFromDate:date] intValue]];
    
    
    NSString *dateString = [NSString stringWithFormat:@"%@%@%@",year,month,day];
    
    NSLog(@"Date : %@",dateString);
    
    return dateString;
}

-(NSString *) packICCDataJpos{
    
    NSMutableString *iccData = [[NSMutableString alloc] init];
    
    NSString *temp = [[NSMutableString alloc] init];
    
    temp = [_response getCryptogram];
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"9F26%02x%@",temp.length / 2,temp]] mutableCopy];
    
    temp = [_response getCryptogramInformationData];
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"9F27%02x%@",temp.length / 2 ,temp]] mutableCopy];
    
    temp = [_response getIAD];
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"9F10%02x%@",temp.length / 2,temp]] mutableCopy];
    
    temp = [_response getUnpredictableNo];
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"9F37%02x%@",temp.length / 2,temp]] mutableCopy];
    
    temp = [_response getATC];
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"9F36%02x%@",temp.length / 2,temp]] mutableCopy];
    
    temp = [_response getTVR];
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"95%02x%@",temp.length / 2,temp]] mutableCopy];
    
    temp = [_response getTransDate];
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"9A%02x%@",temp.length / 2,temp]] mutableCopy];
    
    temp = [_response getTransactionType];
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"9C%02x%@",temp.length / 2,temp]] mutableCopy];
    
    temp = [_response getAmountAuthorised];
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"9F02%02x%@",temp.length / 2,temp]] mutableCopy];
    
    temp = [_response getAIP];
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"82%02x%@",temp.length / 2,temp]] mutableCopy];
    
    temp = [_response getTerminalCountryCode];
    
    if([temp length] == 3)
        temp = [NSString stringWithFormat:@"0%@",temp];
    
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"9F1A%02x%@",temp.length / 2,temp]] mutableCopy];
    
    temp = [_response getTransactionCurrencyCode];
    
    if([temp length] == 3)
        temp = [NSString stringWithFormat:@"0%@",temp];
    
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"5F2A%02x%@",temp.length / 2,temp]] mutableCopy];
    
    temp = [_response getCvmResult];
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"9F34%02x%@",temp.length / 2,temp]] mutableCopy];
    
    temp = [_response getTerminalCapabilities];
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"9F33%02x%@",temp.length / 2,temp]] mutableCopy];
    
    temp = [_response getTerminalType];
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"9F35%02x%@",temp.length / 2,temp]] mutableCopy];
    
    temp = [_response getAmountOther];
    iccData = [[iccData stringByAppendingString:[NSString stringWithFormat:@"9F03%02x%@",temp.length / 2,temp]] mutableCopy];
    
    return iccData;
}

static NSString* maskedPan(NSString *pan){
    
    if(pan.length <= 0)
        return nil;
    
    NSMutableString *temp = [[NSMutableString alloc]init];
    
    int last4Index = pan.length - 4;
    
    for (int i =0 ; i < pan.length ; i++){
        
        if(i < 6 || i >= last4Index )
            [temp appendString:[NSString stringWithFormat:@"%c",[pan characterAtIndex:i]]];
        else
            [temp appendString:@"*"];
    }
    
    return temp;
}

@end
