//
//  ViewController.m
//  ABC-ObjectiveC-TestApp
//
//  Created by AB Circle on 21/8/2023.
//  Copyright © 2023-2024 AB Circle. All rights reserved.
//
#import <ABCSmartCardIO/ABCSmartCardIO-Swift.h>
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

//Global 1 terminal factory
ABCircleTerminalFactory *_abcTerminalFactory = nil;
// Global integer for scan timeout
NSInteger                _scanDuration = 5;

/// Starts a Bluetooth scan in search of CardTerminals using the global timeout & no filters
bool scanBluetooth(void)
{
    bool result = false;
    
    if (_abcTerminalFactory != nil) {
        NSError* err = nil;
        
        [_abcTerminalFactory scanBluetoothWithDurationInSeconds:_scanDuration error:&err];
        if (err.code != 0) {
            NSLog(@"Domain:%@ Code:%ld Message:%@ Reason:%@",err.domain,(long)err.code, _abcTerminalFactory.lastStatusDescription, err.localizedDescription);
        }
        result = (err.code == 0);
    }
    else {
        NSLog(@"scanBluetooth: ABCTerminalFactory not initialised!");
    }
    return result;
}

/// Get the serial number of the entered terminal
/// - Parameter terminal: CardTerminal
NSString* getSerialNumber(CardTerminal* terminal)
{
    NSString* serialNumber = nil;
        
    if (terminal != nil)
    {
        NSError* err = nil;
        
        serialNumber = [terminal getSerialNumberAndReturnError:&err];
        if (err.code > 0) {
            NSLog(@"Domain:%@ Code:%ld Message:%@ Reason:%@",err.domain,(long)err.code, terminal.lastStatusDescription, err.localizedDescription);
        }
    }
    else {
        NSLog(@"getSerialNumber: CardTerminal not initialised!");
    }
    return serialNumber;
}

/// Get the firmware version of the entered terminal
/// - Parameter terminal: CardTerminal
NSString* getFirmwareVersion(CardTerminal* terminal)
{
    NSString* firmwareVersion = nil;
        
    if (terminal != nil) {
        NSError* err = nil;
        
        firmwareVersion = [terminal getFirmwareVersionAndReturnError:&err];
        if (err.code > 0) {
            NSLog(@"Domain:%@ Code:%ld Message:%@ Reason:%@",err.domain,(long)err.code, terminal.lastStatusDescription, err.localizedDescription);
        }
    }
    else {
        NSLog(@"getFirmwareVersion: CardTerminal not initialised!");
    }
    return firmwareVersion;
}

/// Connects to a card on the terminal if present
/// - Parameter terminal: CardTerminal
Card* connectTerminal(CardTerminal* terminal)
{
    Card* card = nil;
    
    if (terminal != nil) {
        if (terminal.isCardPresent) {
            NSError *err = 0;
            
            card = [terminal connectWithProtocolString :@"*" error:&err];
            if (err.code > 0) {
                NSLog(@"Domain:%@ Code:%ld Message:%@ Reason:%@",err.domain,(long)err.code, terminal.lastStatusDescription, err.localizedDescription);
            }
        }
        else {
            NSLog(@"connectTerminal: No card present on terminal");
        }
    }
    else {
        NSLog(@"connectTerminal: CardTerminal not initialised!");
    }
    return card;
}

/// Gets a channel to a Card in order to send an APDU
/// - Parameter card: Card
CardChannel* getChannel(Card* card)
{
    CardChannel* channel = nil;
    
    if (card != nil)
    {
        NSError* err = nil;
        
        channel = [card getBasicChannelAndReturnError:&err];
        if (err.code > 0) {
            NSLog(@"getChannel: Domain:%@ Code:%ld Message:%@ Reason:%@",err.domain,(long)err.code, card.lastStatusDescription, err.localizedDescription);
        }
    }
    else {
        NSLog(@"getChannel: Card not initialised!");
    }
    return channel;
}

/// Sends an APDU to a card (if valid)
/// - Parameter card: Card
void sendApdu(Card* card)
{
    if (card != nil)
    {
        NSError* err = nil;
        CardChannel *channel = getChannel(card);
        
        
        if (channel) {
            CommandAPDU *commandApdu = [[CommandAPDU alloc] initWithApduString:@"FF CA 00 00 00" error:&err];
            
            if (commandApdu && err.code == 0) {
                NSLog(@"sendApdu: Sending command: %@", commandApdu.toString);
                
                // Transmit command and get response
                ResponseAPDU *responseAPDU = [channel transmitWithCommand:commandApdu error:&err];
                if (responseAPDU && err.code == 0)
                {
                    NSLog(@"sendApdu: Response received: %@", responseAPDU.toString);
                }
                else {
                    if (responseAPDU && err.code > 0) {
                        NSLog(@"sendApdu: Domain:%@ Code:%ld Message:%@ Reason:%@",err.domain,(long)err.code, responseAPDU.lastStatusDescription, err.localizedDescription);
                    }
                    else {
                        NSLog(@"sendApdu: could not get ResponseAPDU");
                    }
                }
            }
            else {
                if (commandApdu && err.code > 0) {
                    NSLog(@"sendApdu: Domain:%@ Code:%ld Message:%@ Reason:%@",err.domain,(long)err.code, commandApdu.lastStatusDescription, err.localizedDescription);
                }
                else {
                    NSLog(@"sendApdu: could not create CommandAPDU");
                }
            }
        }
        else {
            NSLog(@"sendApdu: Channel not initialised!");
        }
    }
    else {
        NSLog(@"sendApdu: Card not initialised!");
    }
}

/// Disconnects from a card
/// - Parameter card: Connected Card
void disconnectCard(Card* card)
{
    if (card != nil) {
        NSError* err;
        
        [card disconnectWithReset:false error:&err];
        if (err.code > 0) {
            NSLog(@"Domain:%@ Code:%ld Message:%@ Reason:%@",err.domain,(long)err.code, card.lastStatusDescription, err.localizedDescription);
        }
    }
    else {
        NSLog(@"sendApdu: Card not initialised!");
    }
}

/// Shows the card UID if card is present on the entered terminal
/// - Parameter terminal: CardTerminal
void showCardInformation(CardTerminal* terminal)
{
    Card *card = nil;
    
    card = connectTerminal(terminal);
    if (card != nil) {
        sendApdu(card);
        disconnectCard(card);
    }
}

/// Find all terminals after a bluetooth scan.
/// Since discovery tends to be a bit slow in some cases (e.g. just after app startup), in case we only find 1 terminal we try again after a short delay.
/// We give up after the retrying x times until the scan duration has passed.
NSArray<CardTerminal *> *findTerminals(void)
{
    NSArray<CardTerminal *> *terminals = nil;
    bool stop    = false;
    int  retries = 0;

    while (!stop && retries < _scanDuration) {
        terminals = _abcTerminalFactory.terminals.list;
        if (terminals.count == 0)
        {
            sleep(1);
            retries++;
        }
        else if (terminals.count == 1)
        {
            sleep(1);
        }
        else {
            stop = true;
        }
    }
    return terminals;
}

/// Starts a bluetooth scan and displays some information on any terminal found in range.
void scanForTerminalsAndShowInfo(void)
{
    if (scanBluetooth())
    {
        NSArray<CardTerminal *> *terminals = findTerminals();
        if (terminals.count > 0) {
            NSUInteger ix = 0;
            NSUInteger max = terminals.count;
            
            CardTerminal *terminal = nil;
            do {
                terminal = terminals[ix];
                
                NSLog(@"Name: %@", terminal.getName);
                NSLog(@"FW version: %@", getFirmwareVersion(terminal));
                NSLog(@"Serial Number: %@", getSerialNumber(terminal));
                showCardInformation(terminal);
                ix++;
            }
            while (ix < max);
        }
        else {
            NSLog(@"No terminals found...");
        }
    }

}

/// Starts a thread for performing a bluetooth scan and displays some information on any terminal found in range.
void startScanTerminalThread(void)
{
    dispatch_queue_t abcQueue = dispatch_queue_create("ABC.Queue", NULL);
    dispatch_async(abcQueue, ^{
        scanForTerminalsAndShowInfo();
    });
}

- (void)viewDidLoad {
    [super viewDidLoad];

    // Do any additional setup after loading the view.
    
    //Get the singleton instance of ABCircleTerminalFactory
    _abcTerminalFactory = [ABCircleTerminalFactory getDefault];
    
    if (_abcTerminalFactory != nil)
    {
        /// Display some information on the framework in use
        NSLog(@"Uses %@ v%@", FrameworkInformation.displayName, FrameworkInformation.buildNumber);
    }
    else {
        NSLog(@"Error, could not obtain ABCircleTerminalFactory!");
    }
}

/// Scan button handler. Starts a CardTerminal scan and shows some information of any terminals detected
/// - Parameter sender: UIButton
- (IBAction)onButtonScanTouchUpInside:(UIButton *)sender {
    startScanTerminalThread();
}

@end
