
#include "HAL/Analog.hpp"

#include "Port.h"
#include "HAL/HAL.hpp"
#include "ChipObject.h"
#include "HAL/cpp/Synchronized.hpp"
#include "HAL/cpp/Resource.hpp"
#include "NetworkCommunication/AICalibration.h"
#include "NetworkCommunication/LoadOut.h"

static const long kTimebase = 40000000; ///< 40 MHz clock
static const long kDefaultOversampleBits = 0;
static const long kDefaultAverageBits = 7;
static const float kDefaultSampleRate = 50000.0;
static const uint32_t kAnalogInputPins = 8;
static const uint32_t kAnalogOutputPins = 2;

static const uint32_t kAccumulatorNumChannels = 2;
static const uint32_t kAccumulatorChannels[] = {0, 1};

struct AnalogPort {
  Port port;
  tAccumulator *accumulator;
};

bool analogSampleRateSet = false;
MUTEX_ID analogRegisterWindowSemaphore = NULL;
tAI* analogInputSystem = NULL;
tAO* analogOutputSystem = NULL;
uint32_t analogNumChannelsToActivate = 0;

// Utility methods defined below.
uint32_t getAnalogNumActiveChannels(int32_t *status);
uint32_t getAnalogNumChannelsToActivate(int32_t *status);
void setAnalogNumChannelsToActivate(uint32_t channels);

bool analogSystemInitialized = false;

/**
 * Initialize the analog System.
 */
void initializeAnalog(int32_t *status) {
  if (analogSystemInitialized) return;
  analogRegisterWindowSemaphore = initializeMutexRecursive();
  analogInputSystem = tAI::create(status);
  analogOutputSystem = tAO::create(status);
  setAnalogNumChannelsToActivate(kAnalogInputPins);
  setAnalogSampleRate(kDefaultSampleRate, status);
  analogSystemInitialized = true;
}

/**
 * Initialize the analog input port using the given port object.
 */
void* initializeAnalogInputPort(void* port_pointer, int32_t *status) {
  initializeAnalog(status);
  Port* port = (Port*) port_pointer;

  // Initialize port structure
  AnalogPort* analog_port = new AnalogPort();
  analog_port->port = *port;
  if (isAccumulatorChannel(analog_port, status)) {
    analog_port->accumulator = tAccumulator::create(port->pin, status);
  } else analog_port->accumulator = NULL;

  // Set default configuration
  analogInputSystem->writeScanList(port->pin, port->pin, status);
  setAnalogAverageBits(analog_port, kDefaultAverageBits, status);
  setAnalogOversampleBits(analog_port, kDefaultOversampleBits, status);
  return analog_port;
}

/**
 * Initialize the analog output port using the given port object.
 */
void* initializeAnalogOutputPort(void* port_pointer, int32_t *status) {
  initializeAnalog(status);
  Port* port = (Port*) port_pointer;

  // Initialize port structure
  AnalogPort* analog_port = new AnalogPort();
  analog_port->port = *port;
  return analog_port;
}


/**
 * Check that the analog module number is valid.
 *
 * @return Analog module is valid and present
 */
bool checkAnalogModule(uint8_t module) {
  return module == 1;
}

/**
 * Check that the analog output channel number is value.
 * Verify that the analog channel number is one of the legal channel numbers. Channel numbers
 * are 0-based.
 *
 * @return Analog channel is valid
 */
bool checkAnalogInputChannel(uint32_t pin) {
  if (pin >= 0 && pin < kAnalogInputPins)
    return true;
  return false;
}

/**
 * Check that the analog output channel number is value.
 * Verify that the analog channel number is one of the legal channel numbers. Channel numbers
 * are 0-based.
 *
 * @return Analog channel is valid
 */
bool checkAnalogOutputChannel(uint32_t pin) {
  if (pin >= 0 && pin < kAnalogOutputPins)
    return true;
  return false;
}

void setAnalogOutput(void* analog_port_pointer, double voltage, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;

  uint16_t rawValue = (uint16_t)(voltage / 5.0 * 0x1000);

  if(voltage < 0.0) rawValue = 0;
  else if(voltage > 5.0) rawValue = 0x1000;

  analogOutputSystem->writeMXP(port->port.pin, rawValue, status);
}

double getAnalogOutput(void* analog_port_pointer, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;

  uint16_t rawValue = analogOutputSystem->readMXP(port->port.pin, status);

  return rawValue * 5.0 / 0x1000;
}

/**
 * Set the sample rate.
 *
 * This is a global setting for the Athena and effects all channels.
 *
 * @param samplesPerSecond The number of samples per channel per second.
 */
void setAnalogSampleRate(double samplesPerSecond, int32_t *status) {
  // TODO: This will change when variable size scan lists are implemented.
  // TODO: Need float comparison with epsilon.
  //wpi_assert(!sampleRateSet || GetSampleRate() == samplesPerSecond);
  analogSampleRateSet = true;

  // Compute the convert rate
  uint32_t ticksPerSample = (uint32_t)((float)kTimebase / samplesPerSecond);
  uint32_t ticksPerConversion = ticksPerSample / getAnalogNumChannelsToActivate(status);
  // ticksPerConversion must be at least 80
  if (ticksPerConversion < 80) {
    if ((*status) >= 0) *status = SAMPLE_RATE_TOO_HIGH;
    ticksPerConversion = 80;
  }

  // Atomically set the scan size and the convert rate so that the sample rate is constant
  tAI::tConfig config;
  config.ScanSize = getAnalogNumChannelsToActivate(status);
  config.ConvertRate = ticksPerConversion;
  analogInputSystem->writeConfig(config, status);

  // Indicate that the scan size has been commited to hardware.
  setAnalogNumChannelsToActivate(0);
}

/**
 * Get the current sample rate.
 *
 * This assumes one entry in the scan list.
 * This is a global setting for the Athena and effects all channels.
 *
 * @return Sample rate.
 */
float getAnalogSampleRate(int32_t *status) {
  uint32_t ticksPerConversion = analogInputSystem->readLoopTiming(status);
  uint32_t ticksPerSample = ticksPerConversion * getAnalogNumActiveChannels(status);
  return (float)kTimebase / (float)ticksPerSample;
}

/**
 * Set the sample rate on the module.
 *
 * This is a global setting for the module and effects all channels.
 *
 * @param module The module to use
 * @param samplesPerSecond The number of samples per channel per second.
 */
void setAnalogSampleRateWithModule(uint8_t module, double samplesPerSecond, int32_t *status) {
  if (checkAnalogModule(module)) {
	setAnalogSampleRate(samplesPerSecond, status);
  } else {
	// XXX: Set error status
  }
}

/**
 * Get the current sample rate on the module.
 *
 * This assumes one entry in the scan list.
 * This is a global setting for the module and effects all channels.
 *
 * @param module The module to use
 * @return Sample rate.
 */
float getAnalogSampleRateWithModule(uint8_t module, int32_t *status) {
  if (checkAnalogModule(module)) {
	return getAnalogSampleRate(status);
  } else {
	return -1; // XXX: Set error status
  }
}


/**
 * Set the number of averaging bits.
 *
 * This sets the number of averaging bits. The actual number of averaged samples is 2**bits.
 * Use averaging to improve the stability of your measurement at the expense of sampling rate.
 * The averaging is done automatically in the FPGA.
 *
 * @param analog_port_pointer Pointer to the analog port to configure.
 * @param bits Number of bits to average.
 */
void setAnalogAverageBits(void* analog_port_pointer, uint32_t bits, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;
  analogInputSystem->writeAverageBits(port->port.pin, bits, status);
}

/**
 * Get the number of averaging bits.
 *
 * This gets the number of averaging bits from the FPGA. The actual number of averaged samples is 2**bits.
 * The averaging is done automatically in the FPGA.
 *
 * @param analog_port_pointer Pointer to the analog port to use.
 * @return Bits to average.
 */
uint32_t getAnalogAverageBits(void* analog_port_pointer, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;
  uint32_t result = analogInputSystem->readAverageBits(port->port.pin, status);
  return result;
}

/**
 * Set the number of oversample bits.
 *
 * This sets the number of oversample bits. The actual number of oversampled values is 2**bits.
 * Use oversampling to improve the resolution of your measurements at the expense of sampling rate.
 * The oversampling is done automatically in the FPGA.
 *
 * @param analog_port_pointer Pointer to the analog port to use.
 * @param bits Number of bits to oversample.
 */
void setAnalogOversampleBits(void* analog_port_pointer, uint32_t bits, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;
  analogInputSystem->writeOversampleBits(port->port.pin, bits, status);
}


/**
 * Get the number of oversample bits.
 *
 * This gets the number of oversample bits from the FPGA. The actual number of oversampled values is
 * 2**bits. The oversampling is done automatically in the FPGA.
 *
 * @param analog_port_pointer Pointer to the analog port to use.
 * @return Bits to oversample.
 */
uint32_t getAnalogOversampleBits(void* analog_port_pointer, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;
  uint32_t result = analogInputSystem->readOversampleBits(port->port.pin, status);
  return result;
}

/**
 * Get a sample straight from the channel on this module.
 *
 * The sample is a 12-bit value representing the -10V to 10V range of the A/D converter in the module.
 * The units are in A/D converter codes.  Use GetVoltage() to get the analog value in calibrated units.
 *
 * @param analog_port_pointer Pointer to the analog port to use.
 * @return A sample straight from the channel on this module.
 */
int16_t getAnalogValue(void* analog_port_pointer, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;
  int16_t value;
  checkAnalogInputChannel(port->port.pin);

  tAI::tReadSelect readSelect;
  readSelect.Channel = port->port.pin;
  readSelect.Averaged = false;

  {
    Synchronized sync(analogRegisterWindowSemaphore);
    analogInputSystem->writeReadSelect(readSelect, status);
    analogInputSystem->strobeLatchOutput(status);
    value = (int16_t) analogInputSystem->readOutput(status);
  }

  return value;
}

/**
 * Get a sample from the output of the oversample and average engine for the channel.
 *
 * The sample is 12-bit + the value configured in SetOversampleBits().
 * The value configured in SetAverageBits() will cause this value to be averaged 2**bits number of samples.
 * This is not a sliding window.  The sample will not change until 2**(OversamplBits + AverageBits) samples
 * have been acquired from the module on this channel.
 * Use GetAverageVoltage() to get the analog value in calibrated units.
 *
 * @param analog_port_pointer Pointer to the analog port to use.
 * @return A sample from the oversample and average engine for the channel.
 */
int32_t getAnalogAverageValue(void* analog_port_pointer, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;
  int16_t value;
  checkAnalogInputChannel(port->port.pin);

  tAI::tReadSelect readSelect;
  readSelect.Channel = port->port.pin;
  readSelect.Averaged = true;

  {
    Synchronized sync(analogRegisterWindowSemaphore);
    analogInputSystem->writeReadSelect(readSelect, status);
    analogInputSystem->strobeLatchOutput(status);
    value = (int16_t) analogInputSystem->readOutput(status);
  }

  return value;
}

/**
 * Get a scaled sample straight from the channel on this module.
 *
 * The value is scaled to units of Volts using the calibrated scaling data from GetLSBWeight() and GetOffset().
 *
 * @param analog_port_pointer Pointer to the analog port to use.
 * @return A scaled sample straight from the channel on this module.
 */
float getAnalogVoltage(void* analog_port_pointer, int32_t *status) {
  int16_t value = getAnalogValue(analog_port_pointer, status);
  uint32_t LSBWeight = getAnalogLSBWeight(analog_port_pointer, status);
  int32_t offset = getAnalogOffset(analog_port_pointer, status);
  float voltage = LSBWeight * 1.0e-9 * value - offset * 1.0e-9;
  return voltage;
}

/**
 * Get a scaled sample from the output of the oversample and average engine for the channel.
 *
 * The value is scaled to units of Volts using the calibrated scaling data from GetLSBWeight() and GetOffset().
 * Using oversampling will cause this value to be higher resolution, but it will update more slowly.
 * Using averaging will cause this value to be more stable, but it will update more slowly.
 *
 * @param analog_port_pointer Pointer to the analog port to use.
 * @return A scaled sample from the output of the oversample and average engine for the channel.
 */
float getAnalogAverageVoltage(void* analog_port_pointer, int32_t *status) {
  int32_t value = getAnalogAverageValue(analog_port_pointer, status);
  uint32_t LSBWeight = getAnalogLSBWeight(analog_port_pointer, status);
  int32_t offset = getAnalogOffset(analog_port_pointer, status);
  uint32_t oversampleBits = getAnalogOversampleBits(analog_port_pointer, status);
  float voltage = ((LSBWeight * 1.0e-9 * value) / (float)(1 << oversampleBits)) - offset * 1.0e-9;
  return voltage;
}

/**
 * Convert a voltage to a raw value for a specified channel.
 *
 * This process depends on the calibration of each channel, so the channel
 * must be specified.
 *
 * @todo This assumes raw values.  Oversampling not supported as is.
 *
 * @param analog_port_pointer Pointer to the analog port to use.
 * @param voltage The voltage to convert.
 * @return The raw value for the channel.
 */
int32_t getAnalogVoltsToValue(void* analog_port_pointer, double voltage, int32_t *status) {
  if (voltage > 10.0) {
    voltage = 10.0;
    *status = VOLTAGE_OUT_OF_RANGE;
  }
  if (voltage < -10.0) {
    voltage = -10.0;
    *status = VOLTAGE_OUT_OF_RANGE;
  }
  uint32_t LSBWeight = getAnalogLSBWeight(analog_port_pointer, status);
  int32_t offset = getAnalogOffset(analog_port_pointer, status);
  int32_t value = (int32_t) ((voltage + offset * 1.0e-9) / (LSBWeight * 1.0e-9));
  return value;
}

/**
 * Get the factory scaling least significant bit weight constant.
 * The least significant bit weight constant for the channel that was calibrated in
 * manufacturing and stored in an eeprom in the module.
 *
 * Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9)
 *
 * @param analog_port_pointer Pointer to the analog port to use.
 * @return Least significant bit weight.
 */
uint32_t getAnalogLSBWeight(void* analog_port_pointer, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;
  uint32_t lsbWeight = FRC_NetworkCommunication_nAICalibration_getLSBWeight(0, port->port.pin, status); // XXX: aiSystemIndex == 0?
  return lsbWeight;
}

/**
 * Get the factory scaling offset constant.
 * The offset constant for the channel that was calibrated in manufacturing and stored
 * in an eeprom in the module.
 *
 * Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9)
 *
 * @param analog_port_pointer Pointer to the analog port to use.
 * @return Offset constant.
 */
int32_t getAnalogOffset(void* analog_port_pointer, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;
  int32_t offset = FRC_NetworkCommunication_nAICalibration_getOffset(0, port->port.pin, status); // XXX: aiSystemIndex == 0?
  return offset;
}


/**
 * Return the number of channels on the module in use.
 *
 * @return Active channels.
 */
uint32_t getAnalogNumActiveChannels(int32_t *status) {
  uint32_t scanSize = analogInputSystem->readConfig_ScanSize(status);
  if (scanSize == 0)
    return 8;
  return scanSize;
}

/**
 * Get the number of active channels.
 *
 * This is an internal function to allow the atomic update of both the
 * number of active channels and the sample rate.
 *
 * When the number of channels changes, use the new value.  Otherwise,
 * return the curent value.
 *
 * @return Value to write to the active channels field.
 */
uint32_t getAnalogNumChannelsToActivate(int32_t *status) {
  if(analogNumChannelsToActivate == 0) return getAnalogNumActiveChannels(status);
  return analogNumChannelsToActivate;
}

/**
 * Set the number of active channels.
 *
 * Store the number of active channels to set.  Don't actually commit to hardware
 * until SetSampleRate().
 *
 * @param channels Number of active channels.
 */
void setAnalogNumChannelsToActivate(uint32_t channels) {
  analogNumChannelsToActivate = channels;
}

//// Accumulator Stuff

/**
 * Is the channel attached to an accumulator.
 *
 * @return The analog channel is attached to an accumulator.
 */
bool isAccumulatorChannel(void* analog_port_pointer, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;
  for (uint32_t i=0; i < kAccumulatorNumChannels; i++) {
    if (port->port.pin == kAccumulatorChannels[i]) return true;
  }
  return false;
}

/**
 * Initialize the accumulator.
 */
void initAccumulator(void* analog_port_pointer, int32_t *status) {
  setAccumulatorCenter(analog_port_pointer, 0, status);
  resetAccumulator(analog_port_pointer, status);
}

/**
 * Resets the accumulator to the initial value.
 */
void resetAccumulator(void* analog_port_pointer, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;
  if (port->accumulator == NULL) {
    *status = NULL_PARAMETER;
    return;
  }
  port->accumulator->strobeReset(status);
}

/**
 * Set the center value of the accumulator.
 *
 * The center value is subtracted from each A/D value before it is added to the accumulator. This
 * is used for the center value of devices like gyros and accelerometers to make integration work
 * and to take the device offset into account when integrating.
 *
 * This center value is based on the output of the oversampled and averaged source from channel 1.
 * Because of this, any non-zero oversample bits will affect the size of the value for this field.
 */
void setAccumulatorCenter(void* analog_port_pointer, int32_t center, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;
  if (port->accumulator == NULL) {
    *status = NULL_PARAMETER;
    return;
  }
  port->accumulator->writeCenter(center, status);
}

/**
 * Set the accumulator's deadband.
 */
void setAccumulatorDeadband(void* analog_port_pointer, int32_t deadband, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;
  if (port->accumulator == NULL) {
    *status = NULL_PARAMETER;
    return;
  }
  port->accumulator->writeDeadband(deadband, status);
}

/**
 * Read the accumulated value.
 *
 * Read the value that has been accumulating on channel 1.
 * The accumulator is attached after the oversample and average engine.
 *
 * @return The 64-bit value accumulated since the last Reset().
 */
int64_t getAccumulatorValue(void* analog_port_pointer, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;
  if (port->accumulator == NULL) {
    *status = NULL_PARAMETER;
    return 0;
  }
  int64_t value = port->accumulator->readOutput_Value(status);
  return value;
}

/**
 * Read the number of accumulated values.
 *
 * Read the count of the accumulated values since the accumulator was last Reset().
 *
 * @return The number of times samples from the channel were accumulated.
 */
uint32_t getAccumulatorCount(void* analog_port_pointer, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;
  if (port->accumulator == NULL) {
    *status = NULL_PARAMETER;
    return 0;
  }
  return port->accumulator->readOutput_Count(status);
}

/**
 * Read the accumulated value and the number of accumulated values atomically.
 *
 * This function reads the value and count from the FPGA atomically.
 * This can be used for averaging.
 *
 * @param value Pointer to the 64-bit accumulated output.
 * @param count Pointer to the number of accumulation cycles.
 */
void getAccumulatorOutput(void* analog_port_pointer, int64_t *value, uint32_t *count, int32_t *status) {
  AnalogPort* port = (AnalogPort*) analog_port_pointer;
  if (port->accumulator == NULL) {
    *status = NULL_PARAMETER;
    return;
  }
  if (value == NULL || count == NULL) {
    *status = NULL_PARAMETER;
    return;
  }

  tAccumulator::tOutput output = port->accumulator->readOutput(status);

  *value = output.Value;
  *count = output.Count;
}


struct trigger_t {
  tAnalogTrigger* trigger;
  AnalogPort* port;
  uint32_t index;
};
typedef struct trigger_t AnalogTrigger;

static Resource *triggers = NULL;

void* initializeAnalogTrigger(void* port_pointer, uint32_t *index, int32_t *status) {
  Port* port = (Port*) port_pointer;
  Resource::CreateResourceObject(&triggers, tAnalogTrigger::kNumSystems);

  AnalogTrigger* trigger = new AnalogTrigger();
  trigger->port = (AnalogPort*) initializeAnalogInputPort(port, status);
  trigger->index = triggers->Allocate("Analog Trigger");
  *index = trigger->index;
  // TODO: if (index == ~0ul) { CloneError(triggers); return; }

  trigger->trigger = tAnalogTrigger::create(trigger->index, status);
  trigger->trigger->writeSourceSelect_Channel(port->pin, status);

  return trigger;
}

void cleanAnalogTrigger(void* analog_trigger_pointer, int32_t *status) {
  AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
  triggers->Free(trigger->index);
  delete trigger->trigger;
  delete trigger;
}

void setAnalogTriggerLimitsRaw(void* analog_trigger_pointer, int32_t lower, int32_t upper, int32_t *status) {
  AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
  if (lower > upper) {
	*status = ANALOG_TRIGGER_LIMIT_ORDER_ERROR;
  }
  trigger->trigger->writeLowerLimit(lower, status);
  trigger->trigger->writeUpperLimit(upper, status);
}

/**
 * Set the upper and lower limits of the analog trigger.
 * The limits are given as floating point voltage values.
 */
void setAnalogTriggerLimitsVoltage(void* analog_trigger_pointer, double lower, double upper, int32_t *status) {
  AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
  if (lower > upper) {
	*status = ANALOG_TRIGGER_LIMIT_ORDER_ERROR;
  }
  // TODO: This depends on the averaged setting.  Only raw values will work as is.
  trigger->trigger->writeLowerLimit(getAnalogVoltsToValue(trigger->port, lower, status), status);
  trigger->trigger->writeUpperLimit(getAnalogVoltsToValue(trigger->port, upper, status), status);
}

/**
 * Configure the analog trigger to use the averaged vs. raw values.
 * If the value is true, then the averaged value is selected for the analog trigger, otherwise
 * the immediate value is used.
 */
void setAnalogTriggerAveraged(void* analog_trigger_pointer, bool useAveragedValue, int32_t *status) {
  AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
  if (trigger->trigger->readSourceSelect_Filter(status) != 0) {
	*status = INCOMPATIBLE_STATE;
	// TODO: wpi_setWPIErrorWithContext(IncompatibleMode, "Hardware does not support average and filtering at the same time.");
  }
  trigger->trigger->writeSourceSelect_Averaged(useAveragedValue, status);
}

/**
 * Configure the analog trigger to use a filtered value.
 * The analog trigger will operate with a 3 point average rejection filter. This is designed to
 * help with 360 degree pot applications for the period where the pot crosses through zero.
 */
void setAnalogTriggerFiltered(void* analog_trigger_pointer, bool useFilteredValue, int32_t *status) {
  AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
  if (trigger->trigger->readSourceSelect_Averaged(status) != 0) {
	*status = INCOMPATIBLE_STATE;
	// TODO: wpi_setWPIErrorWithContext(IncompatibleMode, "Hardware does not support average and filtering at the same time.");
  }
  trigger->trigger->writeSourceSelect_Filter(useFilteredValue, status);
}

/**
 * Return the InWindow output of the analog trigger.
 * True if the analog input is between the upper and lower limits.
 * @return The InWindow output of the analog trigger.
 */
bool getAnalogTriggerInWindow(void* analog_trigger_pointer, int32_t *status) {
  AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
  return trigger->trigger->readOutput_InHysteresis(trigger->index, status) != 0;
}

/**
 * Return the TriggerState output of the analog trigger.
 * True if above upper limit.
 * False if below lower limit.
 * If in Hysteresis, maintain previous state.
 * @return The TriggerState output of the analog trigger.
 */
bool getAnalogTriggerTriggerState(void* analog_trigger_pointer, int32_t *status) {
  AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
  return trigger->trigger->readOutput_OverLimit(trigger->index, status) != 0;
}

/**
 * Get the state of the analog trigger output.
 * @return The state of the analog trigger output.
 */
bool getAnalogTriggerOutput(void* analog_trigger_pointer, AnalogTriggerType type, int32_t *status) {
  AnalogTrigger* trigger = (AnalogTrigger*) analog_trigger_pointer;
  bool result = false;
  switch(type) {
  case kInWindow:
	result = trigger->trigger->readOutput_InHysteresis(trigger->index, status);
	break; // XXX: Backport
  case kState:
	result = trigger->trigger->readOutput_OverLimit(trigger->index, status);
	break; // XXX: Backport
  case kRisingPulse:
  case kFallingPulse:
	*status = ANALOG_TRIGGER_PULSE_OUTPUT_ERROR;
	return false;
  }
  return result;
}



//// Float JNA Hack
// Float
int getAnalogSampleRateIntHack(int32_t *status) {
  return floatToInt(getAnalogSampleRate(status));
}

int getAnalogSampleRateWithModuleIntHack(uint8_t module, int32_t *status) {
  return floatToInt(getAnalogSampleRateWithModuleIntHack(module, status));
}

int getAnalogVoltageIntHack(void* analog_port_pointer, int32_t *status) {
  return floatToInt(getAnalogVoltage(analog_port_pointer, status));
}

int getAnalogAverageVoltageIntHack(void* analog_port_pointer, int32_t *status) {
  return floatToInt(getAnalogAverageVoltage(analog_port_pointer, status));
}


// Doubles
void setAnalogSampleRateIntHack(int samplesPerSecond, int32_t *status) {
  setAnalogSampleRate(intToFloat(samplesPerSecond), status);
}

void setAnalogSampleRateWithModuleIntHack(uint8_t module, int samplesPerSecond, int32_t *status) {
  setAnalogSampleRateWithModule(module, intToFloat(samplesPerSecond), status);
}

int32_t getAnalogVoltsToValueIntHack(void* analog_port_pointer, int voltage, int32_t *status) {
  return getAnalogVoltsToValue(analog_port_pointer, intToFloat(voltage), status);
}

void setAnalogTriggerLimitsVoltageIntHack(void* analog_trigger_pointer, int lower, int upper, int32_t *status) {
  setAnalogTriggerLimitsVoltage(analog_trigger_pointer, intToFloat(lower), intToFloat(upper), status);
}
