#ifndef __CAN_DEVICE_INTERFACE_H__
#define __CAN_DEVICE_INTERFACE_H__

#define MAX_STRING_LEN 64

#define SUPPORT_UNIQUE_ID	(1) /* depends entirely on old vs new build */
#define USE_NTH_ORDER		(0) /* zero to user deviceId */
#define SUPPORT_MOTOR_CONTROLLER_PROFILE	(1)
namespace CANDeviceInterface1
{

struct PIDSlot
{
	// Proportional gain
	float pGain;
	// Integral gain
	float iGain;
	// Differential gain
	float dGain;
	// Feed-forward gain
	float fGain;
	// Integral zone
	float iZone;
	// Closed-loop ramp rate
	float clRampRate;
};

struct DeviceDescriptor
{
   // The full device ID, including the device number, manufacturer, and device type.
   // The mask of a message the device supports is 0x1FFF003F.
   unsigned int deviceID;
#if SUPPORT_UNIQUE_ID != 0
   // This is the ID that uniquely identifies the device node in the UI.
   // The purpose of this is to be able to track the device across renames or deviceID changes.
   unsigned int uniqueID;
#endif
   // An dynamically assigned ID that will make setting deviceIDs robust,
   //  Never again will you need to isolate a CAN node just to fix it's ID.
   unsigned int dynamicID;
   // User visible name.  This can be customized by the user, but should have a
   // reasonable default.
   char name[MAX_STRING_LEN];
   // This is a user visible model name that should match the can_devices.ini section.
   char model[MAX_STRING_LEN];
   // This is a version number that represents the version of firmware currently
   // installed on the device.
   char currentVersion[MAX_STRING_LEN];
   // Hardware revision.
   char hardwareRev[MAX_STRING_LEN];
   // Bootloader version.  Will not change for the life of the product, but additional
   // field upgrade features could be added in newer hardware.
   char bootloaderRev[MAX_STRING_LEN];
   // Manufacture Date.  Could be a calender date or just the FRC season year.
   // Also helps troubleshooting "old ones" vs "new ones".
   char manufactureDate[MAX_STRING_LEN];
   // General status of the hardware.  For example if the device is in bootloader
   // due to a bad flash UI could emphasize that.
   char softwareStatus[MAX_STRING_LEN];
   // Is the LED currently on?
   bool led;
	// Reserved fields for future use by CTRE.  Not touched by frccansae
   unsigned int dynFlags;
   unsigned int flags; 		/* bitfield */
   unsigned int ptrToString;
   //unsigned int reserved0;
   //unsigned int reserved1;
   //unsigned int reserved2;
#if SUPPORT_MOTOR_CONTROLLER_PROFILE != 0
	// Motor controller properties (ignored if SupportsMotorControllerProperties is false or unset for this model)
	unsigned int brakeMode; // 0=Coast, 1=Brake
	unsigned int limitSwitchFwdMode; // 0=disabled, 1=Normally Closed, 2=Normally Open
	unsigned int limitSwitchRevMode; // 0=disabled, 1=Normally Closed, 2=Normally Open
	// Limit-switch soft limits
	bool bFwdSoftLimitEnable;
	bool bRevSoftLimitEnable;
	float softLimitFwd;
	float softLimitRev;
	// PID constants for slot 0
	struct PIDSlot slot0;
	// PID constants for slot 1
	struct PIDSlot slot1;
#endif
};

#define kLimitSwitchMode_Disabled		(0)
#define kLimitSwitchMode_NormallyClosed	(1)
#define kLimitSwitchMode_NormallyOpen	(2)

// Interface functions that must be implemented by the CAN Firmware Update Library

// Returns the number of devices that will be returned in a call to
// getListOfDevices().  The calling library will use this info to allocate enough
// memory to accept all device info.
int getNumberOfDevices();

// Return info about discovered devices.  The array of structs should be
// populated before returning.  The numDescriptors input describes how many
// elements were allocated to prevent memory corruption.  The number of devices
// populated should be returned from this function as well.
int getListOfDevices(DeviceDescriptor *devices, int numDescriptors);

// When the user requests to update the firmware of a device a thread will be
// spawned and this function is called from that thread.  This function should
// complete the firmware update process before returning.  The image
// contents and size are directly from the file selected by the user.  The
// error message string can be filled with a NULL-terminated message to show the
// user if there was a problem updating firmware.  The error message is only
// displayed if a non-zero value is returned from this function.
int updateFirmware(const DeviceDescriptor *device, const unsigned char *imageContents, unsigned int imageSize, char *errorMessage, int errorMessageMaxSize);

// This function is called periodically from the UI thread while the firmware
// update is in progress.  The percentComplete parameter should the filled in
// with the current progress of the firmware update process to update a progress
// bar in the UI.
void checkUpdateProgress(const DeviceDescriptor *device, int *percentComplete);

// This is called when the user selects a new ID to assign on the bus and
// chooses to save.  The newDeviceID is really just the device number. The
// manufacturer and device type will remain unchanged.  If a problem is detected
// when assigning a new ID, this function should return a non-zero value.
int assignBroadcastDeviceID(unsigned int newDeviceID);
// The device descriptor should be updated with the new device ID.  The name may
// also change in the descriptor and will be updated in the UI immediately.
// Be sure to modify the descriptor first since the refresh from the UI is
// asynchronous.
int assignDeviceID(DeviceDescriptor *device, unsigned int newDeviceID);

// This entry-point will get called when the user chooses to change the value
// of the device's LED.  This will allow the user to identify devices which
// support dynamic addresses or are otherwise unknown.  If this function returns
// a non-zero value, the UI will report an error.
int saveLightLed(const DeviceDescriptor *device, bool newLEDStatus);

// This entry-point will get called when the user chooses to change the alias
// of the device with the device specified.  If this function returns a non-
// zero value, the UI will report an error.  The device descriptor must be
// updated with the new name that was selected. If a different name is saved
// to the descriptor than the user specified, this will require a manual
// refresh by the user.  This is reported as CAR #505139
int saveDeviceName(DeviceDescriptor *device, const char *newName);

// This entry-point will get called when the user changes any of the motor
// controller specific properties. If this function returns a non-zero value,
// the UI will report an error.  The device descriptor may be updated with
// coerced values.
int saveMotorParameters(DeviceDescriptor *device);

// Run some sort of self-test functionality on the device.  This can be anything
// and the results will be displayed to the user.  A non-zero return value
// indicates an error.
int selfTest(const DeviceDescriptor *device, char *detailedResults, int detailedResultsMaxSize);

} /* CANDeviceInterface */

#endif /* __CAN_DEVICE_INTERFACE_H__ */