#ifndef ENCODER_H
#define ENCODER_H

//----------------------------------
// Includes and forward declarations
//----------------------------------
#include <pkgconf/system.h>
#include <pkgconf/io_encoder.h>

#include <cyg/infra/cyg_type.h>
#include <cyg/io/io.h>
#include <cyg/io/devtab.h>
#include <cyg/io/encoderio.h>
#include <cyg/hal/drv_api.h>

//-----------------
// Type definitions
//-----------------
typedef struct encoder_channel encoder_channel;
typedef struct encoder_funs    encoder_funs;

//--------------------------------------------------------------------------------
// Callback functions for allowing low level device driver to access higher layers
//--------------------------------------------------------------------------------
typedef struct {
  void (* init)(encoder_channel * a_channel, bool a_force);
  void (* latch)(encoder_channel * a_channel, cyg_uint16 a_count);
}encoder_callbacks_t;

#define ENCODER_CALLBACKS(_l, _init, _latch)                    \
  encoder_callbacks_t _l = {                                    \
    _init,                                                      \
    _latch                                                      \
}

extern encoder_callbacks_t encoder_callbacks;

//----------------------------------------------
// Private data that describe an encoder channel
//----------------------------------------------
struct encoder_channel {
  encoder_funs *         m_funs;
  encoder_callbacks_t *  m_callbacks;
  void *                 m_dev_priv;

  cyg_int32              m_position;    // Even with 5 pulses / micron, this yields a range of +/- 400m
                                        // 32 bits is more than enough
  cyg_int32              m_latch_position;

  cyg_uint16             m_initial_count;
  cyg_uint16             m_previous_count;

  bool                   m_latched;
  ENCODER_LATCH_MODE     m_latchmode;
};

#define ENCODER_CHANNEL(_l,                                      \
                        _funs,                                   \
                        _dev_priv,                               \
                        _init_count)                             \
  encoder_channel _l = {                                         \
    &_funs,                                                      \
    &encoder_callbacks,                                          \
    _dev_priv,                                                   \
    _init_count,                                                 \
    0,                                                           \
    _init_count,                                                 \
    _init_count,                                                 \
    false,                                                       \
    ENCODER_LATCH_MODE_DEFAULT                                   \
  }

//------------------------------
// Low level interface functions
//------------------------------
struct encoder_funs {
  bool (* reset)(encoder_channel * a_channel);
  void (* latch_index)(encoder_channel * a_channel);
  void (* latch_probe)(encoder_channel * a_channel);
  void (* stop_latch)(encoder_channel * a_channel);
  cyg_uint16 (* read_position)(encoder_channel * a_channel);
};

#define ENCODER_FUNS(_l,                                         \
                     _reset,                                     \
                     _latch_index,                               \
                     _latch_probe,                               \
                     _stop_latch,                                \
                     _read_position)                             \
  encoder_funs _l = {                                            \
    _reset,                                                      \
    _latch_index,                                                \
    _latch_probe,                                                \
    _stop_latch,                                                 \
    _read_position                                               \
  }

//---------------------------------
// Define the device driver entries
//---------------------------------
extern cyg_devio_table_t encoder_devio;

#endif // ENCODER_H
