#ifndef XK_PRIVATE_H
#define XK_PRIVATE_H

#include <stdint.h>

/****************************************************************************************************************/

struct xk_afl {
  uint8_t sfi;
  uint8_t first;
  uint8_t last;
  uint8_t offline;
};

struct xk_emv_data {
  // AID: Application ID
  uint8_t aidlen;
  uint8_t aid[39];

  // PDOL: Processing options data object list
  uint8_t pdollen;
  uint8_t pdol[39];

  // AIP: Application interchange profile
  uint16_t aip;

  // AFL: Application file locator
  uint8_t aflcnt;
  struct xk_afl afl[8];

  // IBAN: Account number
  uint8_t ibanlen;
  uint8_t iban[23];

  // BIC: Bank identifier
  uint8_t biclen;
  uint8_t bic[39];
};

#define XKP_LEADIN    32       // Space for low level driver to put a prefix
#define XKP_LEADOUT   80       // Space for low level driver to put a suffix
#define XKP_BUFSIZE   (256+16) // One max APDU + a few status/command bytes

#define XKP_TXBUFFER(x) (&((x)->xkp_txbuffer[XKP_LEADIN]))
#define XKP_RXBUFFER(x) (&((x)->xkp_rxbuffer[XKP_LEADIN]))

struct xk_driver {
  //
  // Protocol layer
  //
  unsigned int xkp_state;			// Protocol transaction state machine
  unsigned int xkp_tries;			// How many times we already tried to execute a command

  unsigned int xkp_txlen;			// Number of bytes in transmit buffer

  unsigned int xkp_reslen;			// Length of response
  unsigned int xkp_residx;			// Number of bytes in response buffer
  unsigned int xkp_reschk;			// Checksum calculation during receive

  unsigned int xkp_response_timeout;		// Timeout between command start and response.

  unsigned int xkp_trace;			// Enable debugging

  union {
    unsigned char xkp_dle_seen;			// [VK] DLE character received
    struct {
      unsigned char xkp_address;                // [HS] Protocol address
      unsigned char xkp_format;                 // [HS] Protocol format
      unsigned char xkp_subtype;                // [HS] Reader subtype
    };
  };

  unsigned char xkp_cmd1, xkp_cmd2;		// Keep copy of command code, for sanity checks

  //
  // Sequencer
  //
  void *xks_data;				// Application data

  enum xk_protocol_type xks_proto;		// Protocol selection
  enum xk_interface_mode xks_imode;             // Serial, USB, ...
  enum xk_reader_type xks_type;			// Kind of reader

  uint16_t xks_pollms;				// Poll interval
  uint16_t xks_sense;				// Current polled state of sensors
  uint16_t xks_last_sense;			// Previous polled state of sensors
  uint16_t xks_seq_state;			// Sequencer state machine
  uint16_t xks_seq_upcoming;			// State being requested by application
  uint16_t xks_aid_index;			// Used to walk data during state processing
  uint16_t xks_index;				// Used to walk data during state processing
  uint16_t xks_subindex;			// Ditto

  int xks_last_error;				// Last reported error

  struct xk_trackdata xks_tracks[3];		// Data of three tracks

  union {
    struct vk_reader_version vks_version;	// Firmware version information
    struct sk_reader_version sks_version;	// Firmware version information
    struct hs_reader_version hss_version;	// Firmware version information
  };

  struct xk_emv_data xks_emv;			// Internal buffer for EMV card information

  union {
    unsigned char xks_chip_buffer[XK_CHIP_BUFSIZE]; // Internal buffer for memory access
    struct {
      unsigned int xks_typedetect;		// Used for reader type detection heuristics
      unsigned int xks_backoff;			// Exponential backoff during reinit
      unsigned int xks_tries;			// Initialization tries count
      unsigned int xks_pin;			// Pin code for memory chip cards
      int xks_set_tries;			// Set tries to this after successful pin auth
      unsigned int xks_tracks_to_be_queried;	// Flags which tracks have reported read data
    };
  };

  uint8_t xks_state;				// Application visible state

  uint8_t xks_online;				// True while reader is properly working, false = Need INIT run

  uint8_t xks_insflags;				// Flags for insert state

  uint8_t xks_last_err;				// Last negative ERR field value
  uint8_t xks_last_res;				// Last positive RES field value

  uint8_t xks_has_card_data;			// Flag that magstripe data is buffered

  uint8_t xks_upcoming_processing;		// Next job will be a processing job
  uint8_t xks_processing;			// True while processing an application command

  uint8_t xks_running;				// Flag is start() has been called

  uint8_t xks_driver_flags;			// Driver specific

  uint8_t xks_chip_socket;			// 0=Card, 1=1st SAM, 2=2nd SAM, etc...
  uint8_t xks_last_chip_socket;			// Last selected socket (for readers requiring an explicit switch command)

  uint8_t xks_last_chip_type[5];                // Last chip type seen in socket. Avoid unnecessary unpower commands.

  uint8_t xks_txtries;                          // Command repetition tries

  uint16_t xks_chip_address;			// Read/write address inside chip

  union {
    uint16_t xks_adf_start;			// Start of application data file in buffer
    uint16_t xks_chip_clains;			// For extended chip read/write, the CLA and INS codes
  };

  unsigned int xks_chip_length;			// Length of chip access

  uint16_t xks_chip_country_code;		// Terminal country code
  uint16_t xks_chip_flags;			// Flags for chip access
  uint16_t xks_chip_flags_tmp;			// Flags for chip access
  uint8_t xks_chip_type;			// Chip type requested by application
  uint8_t xks_chip_protocol;			// Reader driver specific
  uint8_t xks_green_state;                      // Cached green LED state
  uint8_t xks_red_state;                        // Cached red LED state

  // Data buffers
  unsigned char xkp_txbuffer[XKP_LEADIN+XKP_BUFSIZE+XKP_LEADOUT];
  unsigned char xkp_rxbuffer[XKP_LEADIN+XKP_BUFSIZE+XKP_LEADOUT];
};

#define LED_OFF    0
#define LED_ON     1
#define LED_FLASH  2

/****************************************************************************************************************/
//
// Calls from upper layer to XK protocol driver
//

// Initialize data structure
void xk_proto_init(struct xk_driver *xkp);

// Start/Stop driver
void xk_proto_start(struct xk_driver *xkp);
void xk_proto_stop(struct xk_driver *xkp);

// Send command
//
// Command must have been placed into xkp_txbuf
//
// Timeout COMMAND -> <DLE> <ACK> is fixed to 5.02s as specified by Leadus.
// Timeout <DLE> <ENQ> -> RESPONSE is specified in <timeout> parameter (ms).
//
// On success, 0 is returned and command executes asynchronously.
// After completion or failure, the callback is called.
//
// On failure, an error code is returned (see below).
//
// If <timeout> is zero, uses the default timeout as recommended by Leadus.
//
int xk_proto_send_command(struct xk_driver *xkp, unsigned int len, unsigned int timeout);

// Abort current command.
// If a command is pending, int will be aborted immediately.
// commandComplete() will NOT be called anymore.
void xk_proto_abort_command(struct xk_driver *xkp);

// Abort current command.
// If a command is pending, int will be aborted immediately.
// xk_proto_command_complete() will NOT be called anymore.
void xk_proto_abort_command(struct xk_driver *xkp);

/****************************************************************************************************************/
//
// Call from protocol driver to sequencer layer
//

// Starts a new line in debug trace output
void xk_seq_trace_header(struct xk_driver *xkp);

/****************************************************************************************************************/
//
// Inline functions
//
XK_INLINE int xk_has_card_data(struct xk_driver *xkp) {
  return xkp->xks_has_card_data;
}

XK_INLINE int xk_is_online(struct xk_driver *xkp) {
  return xkp->xks_online;
}

XK_INLINE int xk_is_ready(struct xk_driver *xkp) {
  return xkp->xks_online && !xkp->xks_processing;
}

XK_INLINE int xk_is_processing(struct xk_driver *xkp) {
  return xkp->xks_online && (xkp->xks_processing || xkp->xks_upcoming_processing);
}

XK_INLINE const struct xk_trackdata *xk_get_tracks(struct xk_driver *xkp) {
  return xkp->xks_tracks;
}

XK_INLINE enum xk_reader_type xk_reader_type(struct xk_driver *xkp) {
  return xkp->xks_type;
}

XK_INLINE enum xk_state xk_state(struct xk_driver *xkp) {
  if (xkp->xks_processing) return xkProcessing;
  return (enum xk_state)xkp->xks_state;
}

XK_INLINE void *xk_get_chip_data(struct xk_driver *xkp) {
  return xkp->xks_chip_buffer;
}

XK_INLINE unsigned int xk_get_chip_length(struct xk_driver *xkp) {
  return xkp->xks_chip_length;
}

XK_INLINE void xk_set_chip_length(struct xk_driver *xkp, unsigned int length) {
  xkp->xks_chip_length = length;
}

XK_INLINE unsigned int xk_sensors(struct xk_driver *xkp) {
  return xkp->xks_last_sense;
}

XK_INLINE enum xk_protocol_type xk_get_protocol(struct xk_driver *xkp) {
  return xkp->xks_proto;
}

XK_INLINE enum xk_interface_mode xk_get_interface_mode(struct xk_driver *xkp) {
  return xkp->xks_imode;
}

/****************************************************************************************************************/
#endif
