#pragma once

#include "ctre/phoenix/led/Animation.h"
#include "ctre/phoenix/led/BaseStandardAnimation.h"
#include "ctre/phoenix/led/BaseTwoSizeAnimation.h"
#include "ctre/phoenix/led/CANdleFaults.h"
#include "ctre/phoenix/led/CANdleStatusFrame.h"
#include "ctre/phoenix/led/CANdleControlFrame.h"
#include "ctre/phoenix/led/CANdleConfiguration.h"
#include "ctre/phoenix/led/CANdleConfigUtil.h"
#include "ctre/phoenix/led/CANdleLedStripType.h"
#include "ctre/phoenix/led/VBatOutputMode.h"
#include "ctre/phoenix/cci/CANdle_CCI.h"
#include "ctre/phoenix/paramEnum.h"
#include "ctre/phoenix/ErrorCode.h"

namespace ctre{ namespace phoenix{ namespace led{

/**
 * CTRE CANdle
 *
 * Device for controlling LEDs from the CAN bus.
 *
 * <pre>
 * {@code
 * // Example usage of a CANdle
 * CANdle candle{0}; // creates a new CANdle with ID 0
 *
 * CANdleConfiguration config;
 * config.stripType = LEDStripType::RGB; // set the strip type to RGB
 * config.brightnessScalar = 0.5; // dim the LEDs to half brightness
 * candle.ConfigAllSettings(config);
 *
 * candle.SetLEDs(255, 255, 255); // set the CANdle LEDs to white
 *
 * // create a rainbow animation:
 * // - max brightness
 * // - half speed
 * // - 64 LEDs
 * RainbowAnimation rainbowAnim{1, 0.5, 64};
 * candle.Animate(rainbowAnim);
 *
 * ErrorCode error = candle.GetLastError(); // gets the last error generated by the CANdle
 * CANdleFaults faults;
 * ErrorCode faultsError = candle.GetFaults(faults); // fills faults with the current CANdle faults; returns the last error generated
 * }
 * </pre>
 */
class CANdle {
private:
    void *_handle;

public:

    /**
     * Constructor for a CANdle Device
     * @param deviceId The Device ID of the CANdle
	 * @param canbus Name of the CANbus; can be a SocketCAN interface (on Linux),
	 *               or a CANivore device name or serial number
     */
    CANdle(int deviceId, std::string const &canbus = "");
    ~CANdle();

    /**
     * Gets the Voltage of VBat as measured by CANdle
     * @return Voltage of VBat
     */
    double GetBusVoltage();
    /**
     * Gets the Voltage of the 5V line as measured by CANdle
     * @return Voltage of the 5V line
     */
    double Get5VRailVoltage();
    /**
     * Gets the low-side current as measured by CANdle
     * @return Current in Amps
     */
    double GetCurrent();
    /**
     * Gets the temperature of the CANdle in Celcius
     * @return Temperature in Celcius
     */
    double GetTemperature();
    /**
     * Gets the applied vbat modulation in percent.
     * If the CANdle is configured to always enable VBat, this returns 1
     * If the CANdle is confgigured to always disable VBat, this returns 0
     * Otherwise it returns the last set Modulation as a value [0, 1]
     * @return VBat Output Modulation
     */
    double GetVBatModulation();

    /**
     * Animates the CANdle with the passed-in animation
     * If the animation changes after calling this function,
     *  it must be passed into animate again for the changes to take effect
     * @param animation The animation that CANdle will run
     * @return ErrorCode generated by function. OK indicates no error.
     */
    ctre::phoenix::ErrorCode Animate(Animation& animation);

    ctre::phoenix::ErrorCode Animate(BaseStandardAnimation& animation);
    ctre::phoenix::ErrorCode Animate(BaseTwoSizeAnimation& animation);

    /**
     * Sets a block of LEDs to the specified color
     * @param r The amount of Red to set, range is [0, 255]
     * @param g The amount of Green to set, range is [0, 255]
     * @param b The amount of Blue to set, range is [0, 255]
     * @param w The amount of White to set, range is [0, 255]. This only applies for LED strips with white in them.
     * @param startIdx Where to start setting the LEDs
     * @param count The number of LEDs to apply this to
     * @return ErrorCode generated by function. OK indicates no error.
     */
    ctre::phoenix::ErrorCode SetLEDs(int r, int g, int b, int w = 0, int startIdx = 0, int count = 512);

    /**
     * Modulates the VBat output to the specified duty cycle percentage
     * This function will only do something if the CANdle's VBatOutput is configured to Modulated
     * @param dutyCyclePrcnt The duty cycle of the output modulation [0, 1]
     * @return ErrorCode generated by function. OK indicates no error.
     */
    ErrorCode ModulateVBatOutput(double dutyCyclePrcnt);

    /**
     * Configures what the CANdle should do if it loses communications to the Controller
     * @param disableWhenLOS Set to true to disable the LEDs on Loss of Signal.
     * @param timeoutMs
     *            Timeout value in ms. If nonzero, function will wait for
     *            config success and report an error if it times out.
     *            If zero, no blocking or checking is performed.
     * @return ErrorCode generated by function. OK indicates no error.
     */
    ErrorCode ConfigLOSBehavior(bool disableWhenLOS, int timeoutMs = 0);
    /**
     * Configures the type of LED the CANdle controls
     * @param type The type of the LEDs the CANdle controls
     * @param timeoutMs
     *            Timeout value in ms. If nonzero, function will wait for
     *            config success and report an error if it times out.
     *            If zero, no blocking or checking is performed.
     * @return ErrorCode generated by function. OK indicates no error.
     */
    ErrorCode ConfigLEDType(LEDStripType type, int timeoutMs = 0);
    /**
     * Configures the brightness scalar to be applied to every LED output.
     * This value is bounded to [0, 1].
     *
     * Setting this to 1 will allow the LEDs to function at max brightness.
     * Setting this to 0.5 will scale all values to half their applied value.
     * Setting this to 0 will turn off the LEDs.
     *
     * Forcing the LEDs off this way may be useful in certain testing circumstances
     * but is generally not necessary. Self-test (Tuner) may be used to verify what
     * the effective scalar is in case user forgot to restore the scalar to a
     * non-zero value.
     *
     * @param brightness Value from [0, 1] that will scale the LED output.
     * @param timeoutMs
                    Timeout value in ms. If nonzero, function will wait for
                    config success and report an error if it times out.
                    If zero, no blocking or checking is performed.
     * @return Error Code generated by function. 0 indicates no error.
     */
    ErrorCode ConfigBrightnessScalar(double brightness, int timeoutMs = 0);

    /**
     * Configures how the status led will behave when the CANdle is actively controlling LEDs
     * If the CANdle is LOS or not actively commanded a value, it will always turn on its status LED.
     * @param disableWhenRunning Disables the status LED when the CANdle is running
     * @param timeoutMs
                    Timeout value in ms. If nonzero, function will wait for
                    config success and report an error if it times out.
                    If zero, no blocking or checking is performed.
     * @return Error Code generated by function. 0 indicates no error.
     */
    ErrorCode ConfigStatusLedState(bool disableWhenRunning, int timeoutMs = 0);
    /**
     * Configures how the VBat Output will behave
     * @param mode VBat Output Behavior
     * @param timeoutMs
                    Timeout value in ms. If nonzero, function will wait for
                    config success and report an error if it times out.
                    If zero, no blocking or checking is performed.
     * @return Error Code generated by function. 0 indicates no error.
     */
    ErrorCode ConfigVBatOutput(VBatOutputMode mode, int timeoutMs = 0);

    /**
     * Gets a parameter. Generally this is not used.
     * This can be utilized in
     * - Using new features without updating API installation.
     * - Errata workarounds to circumvent API implementation.
     * - Allows for rapid testing / unit testing of firmware.
     *
     * @param param
     *            Parameter enumeration.
     * @param ordinal
     *            Ordinal of parameter.
     * @param timeoutMs
     *            Timeout value in ms. If nonzero, function will wait for
     *            config success and report an error if it times out.
     *            If zero, no blocking or checking is performed.
     * @return Value of parameter.
     */
    double ConfigGetParameter(ParamEnum param, int ordinal, int timeoutMs = 0);
    /**
     * Sets a parameter. Generally this is not used.
     * This can be utilized in
     * - Using new features without updating API installation.
     * - Errata workarounds to circumvent API implementation.
     * - Allows for rapid testing / unit testing of firmware.
     *
     * @param param
     *            Parameter enumeration.
     * @param value
     *            Value of parameter.
     * @param subValue
     *            Subvalue for parameter. Maximum value of 255.
     * @param ordinal
     *            Ordinal of parameter.
     * @param timeoutMs
     *            Timeout value in ms. If nonzero, function will wait for
     *            config success and report an error if it times out.
     *            If zero, no blocking or checking is performed.
     * @return Error Code generated by function. 0 indicates no error.
     */
    ErrorCode ConfigSetParameter(ParamEnum param, double value, int subValue = 0, int ordinal = 0, int timeoutMs = 0);
    /**
     * Gets the value of a custom parameter. This is for arbitrary use.
     *
     * Sometimes it is necessary to save calibration/duty cycle/output
     * information in the device. Particularly if the
     * device is part of a subsystem that can be replaced.
     *
     * @param paramIndex
     *            Index of custom parameter. [0-1]
     * @param timeoutMs
     *            Timeout value in ms. If nonzero, function will wait for
     *            config success and report an error if it times out.
     *            If zero, no blocking or checking is performed.
     * @return Value of the custom param.
     */
    int ConfigGetCustomParam(int paramIndex, int timeoutMs = 0);
    /**
     * Sets the value of a custom parameter. This is for arbitrary use.
     *
     * Sometimes it is necessary to save calibration/duty cycle/output
     * information in the device. Particularly if the
     * device is part of a subsystem that can be replaced.
     *
     * @param newValue
     *            Value for custom parameter.
     * @param paramIndex
     *            Index of custom parameter. [0-1]
     * @param timeoutMs
     *            Timeout value in ms. If nonzero, function will wait for
     *            config success and report an error if it times out.
     *            If zero, no blocking or checking is performed.
     * @return Error Code generated by function. 0 indicates no error.
     */
    ErrorCode ConfigSetCustomParam(int paramIndex, int value, int timeoutMs = 0);
    /**
     * Configures all persistent settings to defaults.
     *
     * @param timeoutMs
     *              Timeout value in ms. If nonzero, function will wait for
     *              config success and report an error if it times out.
     *              If zero, no blocking or checking is performed.
     *
     * @return Error Code generated by function. 0 indicates no error.
     */
    ErrorCode ConfigFactoryDefault(int timeoutMs = 50);
    /**
     * Gets the CANdle fault status
     *
     * @param toFill Container for fault statuses.
     * @return Error Code generated by function. OK indicates no error.
     */
    ErrorCode GetFaults(CANdleFaults& toFill);
    /**
     * Gets the CANdle sticky fault status
     *
     * @param toFill Container for sticky fault statuses.
     * @return Error Code generated by function. OK indicates no error.
     */
    ErrorCode GetStickyFaults(CANdleStickyFaults& toFill);
    /**
     * Clears the sticky faults.
     *
     * @param timeoutMs
     *            Timeout value in ms. If nonzero, function will wait for
     *            config success and report an error if it times out.
     *            If zero, no blocking or checking is performed.
     * @return Error Code generated by function. 0 indicates no error.
     */
    ErrorCode ClearStickyFaults(int timeoutMs = 0);
    /**
     * Returns true if the device has reset since last call.
     *
     * @return Has a Device Reset Occurred?
     */
    bool HasResetOccurred();
    /**
     * Sets the period of the given status frame.
     *
     * @param frame
     *            Frame whose period is to be changed.
     * @param periodMs
     *            Period in ms for the given frame.
     * @param timeoutMs
     *            Timeout value in ms. If nonzero, function will wait for
     *            config success and report an error if it times out.
     *            If zero, no blocking or checking is performed.
     * @return Error Code generated by function. 0 indicates no error.
     */
    ErrorCode SetStatusFramePeriod(CANdleStatusFrame frame, int periodMs, int timeoutMs = 0);
    /**
     * Gets the period of the given status frame.
     *
     * @param frame
     *            Frame to get the period of.
     * @param timeoutMs
     *            Timeout value in ms. If nonzero, function will wait for
     *            config success and report an error if it times out.
     *            If zero, no blocking or checking is performed.
     * @return Period of the given status frame.
     */
    int GetStatusFramePeriod(CANdleStatusFrame frame, int timeoutMs = 0);
	/**
	 * Sets the period of the given control frame.
	 *
	 * @param frame
	 *            Frame whose period is to be changed.
	 * @param periodMs
	 *            Period in ms for the given frame.
	 * @return Error Code generated by function. 0 indicates no error.
	 */
    ErrorCode SetControlFramePeriod(CANdleControlFrame frame, int periodMs);

    /**
     * Configures all persistent settings.
     *
     * @param allConfigs        Object with all of the persistant settings
     * @param timeoutMs
     *              Timeout value in ms. If nonzero, function will wait for
     *              config success and report an error if it times out.
     *              If zero, no blocking or checking is performed.
     * @return Error Code generated by function. 0 indicates no error.
     */
    ErrorCode ConfigAllSettings(CANdleConfiguration allConfigs, int timeoutMs = 50);
    /**
     * Gets all persistant settings.
     *
     * @param allConfigs        Object with all of the persistant settings
     * @param timeoutMs
     *              Timeout value in ms. If nonzero, function will wait for
     *              config success and report an error if it times out.
     *              If zero, no blocking or checking is performed.
     */
    void GetAllConfigs(CANdleConfiguration allConfigs, int timeoutMs = 50);

    /**
     * Call GetLastError() generated by this object.
     * Not all functions return an error code but can
     * potentially report errors.
     *
     * This function can be used to retrieve those error codes.
     *
     * @return The last ErrorCode generated.
     */
    ErrorCode GetLastError();
};

} // namespace led
} // namespace phoenix
} // namespace ctre

