#pragma once

#include <cstddef>
#include <vector>
#include <memory>

namespace xavia::sdk
{
  class SerialNumber;
  class ICamera;
  enum class SensorState;
  enum class SensorError;
  struct FullSensorState;
  constexpr std::size_t DEFAULT_STATE_CHANGE_TIMEOUT_MS = 5000;

  /**
   * \brief Sensor object interface
   *
   * This class defines the interface for a sensor. It provides methods to start, 
   * stop, and get the current state and other information of the sensor.
   *
   * \see SensorFactory for creating instances of this class
   */
  class ISensor
  {
  public:
    virtual ~ISensor() = default;

    /**
     * \brief Starts the sensor
     * \param timeoutMS Timeout in milliseconds in which success response is expected (optional, default is 5 sec)
     * \throws sdk::exception object if no response is received within timeoutMS, or if the new state is not running.
     * 
     * Start means that the sensor is moved from idle to running state.
     * It is only valid to call this function if the current state is idle. An exception is thrown otherwise.
     * An exception is also thrown if the sensor did not respond timely or if the response is unexpected.
     * 
     * \see GetCurrentState()
     */
    virtual void Start(const std::size_t& timeoutMS = DEFAULT_STATE_CHANGE_TIMEOUT_MS) = 0;

    /**
     * \brief Stop the sensor
     * \param timeoutMS Timeout in milliseconds in which success response is expected (optional, default is 5 sec)
     * \throws sdk::exception object if no response is received within timeoutMS, or if the new state is not idle.
     * 
     * Stop means that the sensor is moved from running to idle state.
     * It is only valid to call this function if the current state is running. An exception is thrown otherwise.
     * An exception is also thrown if the sensor did not respond timely or if the response is unexpected.
     * 
     * \see GetCurrentState()
     */
    virtual void Stop(const std::size_t& timeoutMS = DEFAULT_STATE_CHANGE_TIMEOUT_MS)  = 0;

    /**
     * \brief Provides the serial number of the sensor from local memory
     * \return serialNumber object
     * 
     * This function does not interact with the hardware but serves previously 
     * received information.
     * Use GetErrors() to trigger an update if you suspect desynchronisation.
     * 
     * \see SerialNumber, GetErrors()
     */
    virtual SerialNumber GetSerialNumber() const = 0;

    /**
     * \brief Provides the current state of the sensor (running, idle, error)
     * \return sensor state object (@see sensorState.h)
     * 
     * This function does not interact with the hardware but serves previously 
     * received information.
     * Use GetErrors() to trigger an update if you suspect desynchronisation.
     * 
     * \see sensorState, GetErrors()
     */
    virtual SensorState  GetCurrentState() const = 0;

    /**
     * \brief Retrieves the state from the sensor and reports a list of errors
     * \param timeoutMS The maximum amount of time to wait the response (optional, default 5 sec)
     * \throws sdk::exception if no valid response is received in the timeout
     * \return A list of sensorError objects that indicate any problems or an empty vector if there are none.
     * 
     * This function send a TCP message to the sensor to request the sensor state.
     * The return will contain information like the current state (idle, running, error),
     * and more information on the errors if any.
     * 
     * A side effect of this function is that the state of the sensor object in the SDK
     * is updated to match the state of the hardware.
     * 
     * \warning This function throws an exception if it receives a sensor status response message that
     * comes from a different sensor. This can happen if you have multiple sensors in the network, or
     * if you are trying to query multiple sensors at once.
     * 
     * \see SensorError
     */
    virtual std::vector<SensorError> GetErrors(const std::size_t& timeoutMS = DEFAULT_STATE_CHANGE_TIMEOUT_MS ) = 0;

    /**
     * \brief Sends a 'reboot' signal to the sensor.
     * 
     * Reboot means that all the components will be powered down and
     * restarted as if from a clean boot.
     * 
     * When the sensor reboots, the current sensor object is invalidated.
     * Destroy all references to it and build a new sensor object once the system has time to restart.
     * 
     * \note this function does not check if the action is executed, it simply posts the request.
     */
    virtual void Reboot() = 0;

    /**
     * \brief Sends a 'poweroff' signal to the sensor.
     * 
     * Poweroff means that all software will stop, the components will be powered down and
     * the sensor will remain powered off. In order to restart the sensor either toggle the
     * ignition line or unplug and re-plug the power cord.
     * 
     * When the sensor powers off, the current sensor object is invalidated.
     * Destroy all references to it and build a new sensor object if you need it.
     * 
     * \note this function does not check if the action is executed, it simply posts the request.
     */
    virtual void Poweroff() = 0;

    /**
     * \brief Get the camera associated with this sensor
     * \return Shared pointer to ICamera interface, or nullptr if no camera was configured
     * 
     * Returns a pointer to the camera object if a camera was configured during sensor
     * creation via SensorFactory. If no camera was configured, returns nullptr.
     * 
     * The camera can be controlled independently from the sensor using its own
     * Start(), Stop(), and exposure control methods.
     * 
     * \see ICamera for camera control methods
     * \see SensorFactory::SetCameraIPAddress() for configuring camera
     */
    virtual std::shared_ptr<ICamera> GetCamera() const = 0;
  };
}