#ifndef VK_PRIVATE_H
#define VK_PRIVATE_H

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

enum vk_proto_state {
  VKPStateIdle,				// Nothing to do
  VKPStateDTR,				// DTR line is off, polling for coming up
  VKPStateCommandSent,			// <DLE> <STX> <command> <DLE> <ETX> sent, expecting <DLE> <ACK>
  VKPStateExecuteSent,			// <DLE> <ENQ> sent, expecting response
  VKPStateResponseReceiving,		// <DLE> <STX> received, reading in response
  VKPStateBCCReceiving,			// <DLE> <ETX> received, need checksum next
};

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

struct vk_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 vk_afl afl[8];
};

struct vk_driver {
  //
  // Protocol layer
  //
  enum vk_proto_state vkp_state;		// Protocol transaction state machine
  unsigned int vkp_tries;			// How many times we already tried to execute a command

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

  unsigned int vkp_reslen;			// Number of bytes in response buffer
  unsigned int vkp_resbcc;			// Checksum calculation during receive
  int vkp_dle_seen;				// Last byte processed was a <DLE>

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

  unsigned int vkp_trace;			// Enable debugging

  unsigned char vkp_cmd1, vkp_cmd2;		// Keep copy of command code, for sanity checks
  unsigned char vkp_buf[256+16];		// Raw data buffer (One max APDU + a few status/command bytes)

  //
  // Sequencer
  //
  void *vks_data;				// Application data

  enum vk_reader_type vks_type;			// Kind of reader

  uint16_t vks_pollms;				// Poll interval
  uint16_t vks_last_sense;			// Last polled state of sensors
  uint16_t vks_seq_state;			// Sequencer state machine
  uint16_t vks_seq_upcoming;			// State being requested by application
  uint16_t vks_index;				// Used to walk data during state processing
  uint16_t vks_subindex;			// Ditto

  int vks_last_error;				// Last reported error

  struct vk_trackdata vks_tracks[3];		// Data of three tracks

  uint16_t vks_chip_address;			// Read/write address inside chip

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

  unsigned int vks_chip_length;			// Length of chip access

  struct vk_reader_version vks_version;		// Firmware version information

  struct vk_emv_data vks_emv;			// Internal buffer for EMV card information

  union {
    unsigned char vks_chip_buffer[VK_CHIP_BUFSIZE]; // Internal buffer for memory access
    struct {
      unsigned int vks_typedetect;		// Used for reader type detection heuristics
      unsigned int vks_backoff;			// Exponential backoff during reinit
      unsigned int vks_tries;			// Initialization tries count
      unsigned int vks_pin;			// Pin code for memory chip cards
      int vks_set_tries;			// Set tries to this after successful pin auth
    };
  };

  uint16_t vks_chip_country_code;		// Terminal country code

  uint8_t vks_chip_flags;			// Flags for chip access

  uint8_t vks_state;				// Application visible state

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

  uint8_t vks_insflags;				// Flags for insert state

  uint8_t vks_last_err;				// Last negative ERR field value
  uint8_t vks_last_res;				// Last positive RES field value

  uint8_t vks_has_card_data;			// Flag that magstripe data is buffered

  uint8_t vks_chiptype;				// Chip type requested by application

  uint8_t vks_chip_protocol;			// T0 byte from ATR

  uint8_t vks_upcoming_processing;		// Next job will be a processing job
  uint8_t vks_processing;			// True while processing an application command
};

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

// Initialize data structure
void vk_proto_init(struct vk_driver *vkp);

// Start/Stop driver
void vk_proto_start(struct vk_driver *vkp);
void vk_proto_stop(struct vk_driver *vkp);

// Send command
//
// Command must have been placed into vkp_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 vk_proto_send_command(struct vk_driver *vkp, 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 vk_proto_abort_command(struct vk_driver *vkp);

// Abort current command.
// If a command is pending, int will be aborted immediately.
// vk_proto_command_complete() will NOT be called anymore.
void vk_proto_abort_command(struct vk_driver *vkp);

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

// Command execution finished
void vk_seq_command_complete(struct vk_driver *vkp);

// Command execution failed
void vk_seq_command_failed(struct vk_driver *vkp, int error_code);

// Starts a new line in debug trace output
void vk_seq_trace_header(struct vk_driver *vkp);

/****************************************************************************************************************/
//
// Inline functions
//
VK_INLINE int vk_has_card_data(struct vk_driver *vkp) {
  return vkp->vks_has_card_data;
}

VK_INLINE int vk_is_online(struct vk_driver *vkp) {
  return vkp->vks_online;
}

VK_INLINE int vk_is_ready(struct vk_driver *vkp) {
  return vkp->vks_online && !vkp->vks_processing;
}

VK_INLINE int vk_is_processing(struct vk_driver *vkp) {
  return vkp->vks_online && (vkp->vks_processing || vkp->vks_upcoming_processing);
}

VK_INLINE const struct vk_trackdata *vk_get_tracks(struct vk_driver *vkp) {
  return vkp->vks_tracks;
}

VK_INLINE enum vk_reader_type vk_reader_type(struct vk_driver *vkp) {
  return vkp->vks_type;
}

VK_INLINE enum vk_state vk_state(struct vk_driver *vkp) {
  if (vkp->vks_processing) return vkProcessing;
  return (enum vk_state)vkp->vks_state;
}

VK_INLINE void *vk_get_chip_data(struct vk_driver *vkp) {
  return vkp->vks_chip_buffer;
}

VK_INLINE unsigned int vk_get_chip_length(struct vk_driver *vkp) {
  return vkp->vks_chip_length;
}

VK_INLINE void vk_set_chip_length(struct vk_driver *vkp, unsigned int length) {
  vkp->vks_chip_length = length;
}

VK_INLINE unsigned int vk_sensors(struct vk_driver *vkp) {
  return vkp->vks_last_sense;
}

VK_INLINE const struct vk_reader_version *vk_get_version(struct vk_driver *vkp) {
  return &vkp->vks_version;
}

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