#ifndef HP34COMM_H
#define HP34COMM_H

#include <mbed.h>
#include <CircularBuffer.h>

/***** HP 34970A communication class ***/

#define MAX_ERRS 10
#define MAX_BUFF 16
#define BUF_SIZE 16



class HPSerial {

public:
  typedef struct _CMD
  {
    uint8_t cmd;
    uint8_t size;
    char value[MAX_BUFF+1];
    unsigned long id;
  } CMD;

  HPSerial(PinName tx, PinName rx);

  bool cmd_available(void);
  bool pop(CMD& cmd);
  bool cmd_buf_full(void);
  unsigned int nerrors(uint8_t errorno);

  void startup(void);
  void send(const uint8_t *buf, uint8_t size);
  void sendkey(uint8_t keycode);

private:
  void pushCmd(uint8_t cmd, uint8_t size, char *payload);
  void rxIrq(void);
  void timeout(void);
  void set_timer(Kernel::Clock::duration_u32 v=0ms) {
    timeouter.detach();
    if (v > 0ms)
      timeouter.attach(callback(this, &HPSerial::timeout), v);
  }

 private:
  UnbufferedSerial serial;
  uint8_t buf[BUF_SIZE];
  uint8_t head;
  CircularBuffer<CMD, 32> cmdbuf;
  unsigned long ncmd;
  unsigned int errs[MAX_ERRS];
  Ticker timeouter;

 public:
  // global state machine
  typedef enum {
    GSTATE_STARTING,
    GSTATE_IDLE,
    GSTATE_TX,
    GSTATE_RX,
    NUM_GSTATES} gstate_t;
  gstate_t gstate() {return cur_gstate;};
  /* gstate_t do_start(); */
  /* gstate_t do_send(); */
  /* gstate_t do_receive(); */

  /* typedef gstate_t(HPSerial::*gstatemethod)(); */
  /* statemethod const gstate_table[NUM_GSTATES] = { */
  /*     &HPSerial::do_start, */
  /*     &HPSerial::do_send, */
  /*     &HPSerial::do_receive, */
  /* }; */

  /* gstate_t run_gstate(gstate_t cur_state); */
 private:
  gstate_t cur_gstate;

 public:
  // transmission state machine
  typedef enum {
    STATE_IDLE,
    STATE_COMMAND,
    STATE_PAYLOAD_SIZE,
    STATE_PAYLOAD,
    STATE_SENDING,
    STATE_EOT,
    NUM_STATES} state_t;
  state_t state() {return cur_state;};

 private:
  typedef struct state_data {
    uint8_t cmd;
    uint8_t size;
    uint8_t pos;
    char payload[MAX_BUFF];
  } state_data_t;

  void _startup(void);
  state_t do_state_initial(uint8_t c=0x00);
  state_t do_state_command(uint8_t c);
  state_t do_state_payload_size(uint8_t c);
  state_t do_state_payload(uint8_t c);
  state_t do_state_sending(uint8_t c=0x00);
  state_t do_state_eot(uint8_t c=0x55);

  void send_ack(uint8_t c);

  typedef state_t(HPSerial::*statemethod)(uint8_t c);
  static  statemethod state_table[NUM_STATES];
  state_t run_state(state_t cur_state, uint8_t c);

  state_t cur_state;
  state_data_t tr_data;
};


#endif
