/*----------------------------------------------------------------------------*/
/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
/* Open Source Software - may be modified and shared by FRC teams. The code   */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project.                                                               */
/*----------------------------------------------------------------------------*/

#pragma once

#include <stdint.h>

#include "hal/Encoder.h"

namespace hal {

bool GetEncoderBaseHandle(HAL_EncoderHandle handle,
                          HAL_FPGAEncoderHandle* fpgaEncoderHandle,
                          HAL_CounterHandle* counterHandle);

class Encoder {
 public:
  friend bool GetEncoderBaseHandle(HAL_EncoderHandle handle,
                                   HAL_FPGAEncoderHandle* fpgaEncoderHandle,
                                   HAL_CounterHandle* counterHandle);

  Encoder(HAL_Handle digitalSourceHandleA,
          HAL_AnalogTriggerType analogTriggerTypeA,
          HAL_Handle digitalSourceHandleB,
          HAL_AnalogTriggerType analogTriggerTypeB, bool reverseDirection,
          HAL_EncoderEncodingType encodingType, int32_t* status);
  ~Encoder();

  // CounterBase interface
  int32_t Get(int32_t* status) const;
  int32_t GetRaw(int32_t* status) const;
  int32_t GetEncodingScale(int32_t* status) const;
  void Reset(int32_t* status);
  double GetPeriod(int32_t* status) const;
  void SetMaxPeriod(double maxPeriod, int32_t* status);
  bool GetStopped(int32_t* status) const;
  bool GetDirection(int32_t* status) const;

  double GetDistance(int32_t* status) const;
  double GetRate(int32_t* status) const;
  void SetMinRate(double minRate, int32_t* status);
  void SetDistancePerPulse(double distancePerPulse, int32_t* status);
  void SetReverseDirection(bool reverseDirection, int32_t* status);
  void SetSamplesToAverage(int32_t samplesToAverage, int32_t* status);
  int32_t GetSamplesToAverage(int32_t* status) const;

  void SetIndexSource(HAL_Handle digitalSourceHandle,
                      HAL_AnalogTriggerType analogTriggerType,
                      HAL_EncoderIndexingType type, int32_t* status);

  int32_t GetFPGAIndex() const { return m_index; }

  int32_t GetEncodingScale() const { return m_encodingScale; }

  double DecodingScaleFactor() const;

  double GetDistancePerPulse() const { return m_distancePerPulse; }

  HAL_EncoderEncodingType GetEncodingType() const { return m_encodingType; }

 private:
  void SetupCounter(HAL_Handle digitalSourceHandleA,
                    HAL_AnalogTriggerType analogTriggerTypeA,
                    HAL_Handle digitalSourceHandleB,
                    HAL_AnalogTriggerType analogTriggerTypeB,
                    bool reverseDirection, HAL_EncoderEncodingType encodingType,
                    int32_t* status);

  HAL_FPGAEncoderHandle m_encoder = HAL_kInvalidHandle;

  HAL_CounterHandle m_counter = HAL_kInvalidHandle;

  int32_t m_index = 0;

  double m_distancePerPulse = 1.0;

  HAL_EncoderEncodingType m_encodingType;

  int32_t m_encodingScale;
};

}  // namespace hal
