#pragma once

#include "xmsdk_export.h"
#include <vector>
#include <cstdint>
#include <memory>

namespace xavia::sdk
{
  /**
   * \brief Metadata struct containing general information for each point cloud line
   * 
   * This struct contains information such as the frame and line numbers, the timestamps
   * and the return index.
   */
  struct MetaData
  {
    std::uint64_t frameTimeStampUs{0};     ///< Posix timestamp in us of start of frame acquisition.
    std::uint64_t lineTimeStampUs{0};      ///< Posix timestamp in us of start of line acquisition.
    std::uint64_t transmitTimeStampUs{0};  ///< Posix timestamp in us when data leaves sensor.
    std::uint32_t frameNumber{0};          ///< Monotonically increasing counter. Unique for each frame acquired by the sensor.
    std::uint8_t lineNumber{0};            ///< Line number/counter. Indicates the vertical position within a frame of a dataset.
    std::uint8_t internalFrameNumber{0};    ///< Internal, continuous incrementing frame counter. Overflows at 255.
    std::uint32_t triggerCounter{0};       ///< Monotonically increasing counter indicating a trigger connected to this frame.
    std::uint8_t returnIndex{0};           ///< Indicator for multiple returns mode (currently not supported, will always be 0).
  };

  /**
   * \brief struct containing information from sensors inside the sensors such as
   * temperature and humidity.
   */
  struct OnboardProbes
  {
    float humidityRelative; ///< Relative humidity measurement from inside the sensor.
    float temperatureDegC;  ///< Temperature measurement from inside the sensor in degrees Celsius.
  };

  /**
   * \brief The IMU data registered when the point cloud was recorded
   */
  struct ImuData
  {
    float yawDegS;   ///< yaw change indicated in degrees per second
    float pitchDegS; ///< pitch change indicated in degrees per second
    float rollDegS;  ///< roll change indicated in degrees per second
    float accXMS2;   ///< x acceleration expressed in meter per seconds squared.
    float accYMS2;   ///< y acceleration expressed in meter per seconds squared.
    float accZMS2;   ///< z acceleration expressed in meter per seconds squared.
  };

  /**
   * \brief The interface of the point cloud object.
   * 
   * Returned by the point cloud callback, each point cloud object represents
   * a single line in the FOV of the sensor.
   * 
   * The point cloud object contains: the (x,y,z) reconstruction of each point,
   * the measured distance from the sensor, the reflectivity and ambient light values,
   * a confidence level for each point, and the metadata.
   */
  class IPointCloud
  {
  public:

    virtual ~IPointCloud() = default;
    /**
     * \brief Get the x reconstruction of points of the current line
     * \returns const reference to the vector of x coordinates in mm, with the sensor as origin.
     * This vector holds GetNrPoints() elements.
     */
    virtual const std::vector<float>& GetXMm() const = 0;
    /**
     * \brief Get the y reconstruction of points of the current line
     * \returns const reference to the vector of y coordinates in mm, with the sensor as origin.
     * This vector holds GetNrPoints() elements.
     */
    virtual const std::vector<float>& GetYMm() const = 0;
    /**
     * \brief Get the z reconstruction of points of the current line
     * \returns const reference to the vector of z coordinates in mm, with the sensor as origin.
     * This vector holds GetNrPoints() elements.
     */
    virtual const std::vector<float>& GetZMm() const = 0;

    /**
     * \brief Get the measured distance of the points of the current line
     * \returns const reference to the vector of radial distances from the sensor in mm.
     * This vector holds GetNrPoints() elements.
     */
    virtual const std::vector<float>& GetDistanceMm() const = 0;

    /**
     * \brief Get the reflectivity of the points of the current line
     * \returns const reference to the vector reflectivity values. Expressed in % (can exceed 100%).
     * This vector holds GetNrPoints() elements.
     */
    virtual const std::vector<float>& GetReflectivity() const = 0;

    /**
     * \brief Get the background light of points of the current line
     * \returns const reference to the vector with background light measurements.
     * This vector holds GetNrPoints() elements.
     */
    virtual const std::vector<std::uint16_t>& GetAmbient() const = 0;

    /**
     * \brief Get the confidence of points of the current line
     * \returns const reference to the vector confidences (between 0 and 1, where 1 is 100% confidence).
     * This vector holds GetNrPoints() elements.
     */
    virtual const std::vector<float>& GetConfidence() const = 0;

    /**
     * \brief Get the number of points in the current line
     * \returns number of points as unsigned integer. e.g. 192.
     * This is the number of elements inside the other vector returns of the pointcloud.
     */
    virtual std::size_t GetNrPoints() const = 0;

    /**
     * \brief Get the metadata of the current line
     * \returns const reference to the metadata struct, \see MetaData.
     */
    virtual const MetaData& GetMetaData() const = 0;
    
    /**
     * \brief Get the IMU data of the current line
     * \returns const reference to the imuData struct, \see ImuData.
     * \note ImuData is currently not yet released. Values will always be 0.
     */
    virtual const ImuData& GetImuData() const = 0;
    
    /**
     * \brief Get the temperature and humidity of the current line
     * \returns const reference to the probes struct, \see OnboardProbes.
     */
    virtual const OnboardProbes& GetOnboardProbes() const = 0;

    /**
     * \brief Convert this point cloud object into binary data.
     * 
     * \returns vector of bytes that hold the point cloud information
     * 
     * Use this to efficiently store the point cloud in the XenomatiX proprietary format.
     * When dumping to disk, it accustomary to use the .xpc extension.
     * 
     * \see deserialize() functions to read the information back.
     */
    virtual const std::vector<std::uint8_t>& Serialize() = 0;

    /**
     * \brief Fill this point cloud object from binary data.
     * 
     * \param[in] data vector of bytes that hold the point cloud information.
     * 
     * Use this to efficiently load the point cloud from the XenomatiX proprietary format.
     * The opposite of the Serialize() function.
     *
     * \note although it is acceptable to provide too many bytes, it is best that
     * size matches exactly the required size. This function will keep an internal
     * copy of the provided data.
     */
    virtual void Deserialize(const std::vector<std::uint8_t>& data) = 0;

    /**
     * \brief Fill this point cloud object from binary data.
     * 
     * \param[in] pBuffer pointer to the start of the binary data.
     * \param[in] size number of bytes to read from the buffer.
     * 
     * Use this to efficiently load the point cloud from the XenomatiX proprietary format.
     * The opposite of the Serialize() function.
     * 
     * \note although it is acceptable to provide too many bytes, it is best that
     * size matches exactly the required size. This function will keep an internal
     * copy of the provided data.
     */
    virtual void Deserialize(const std::uint8_t* pBuffer, const std::size_t size) = 0;
  };

  /**
   * \brief Static point cloud factory function.
   * 
   * \returns an empty point cloud object.
   * 
   * Use this function to create your own, empty point cloud objects.
   * Best used in combination with Deserialize to read point clouds from e.g.
   * the disk.
   */
  std::unique_ptr<IPointCloud> XMSDK_EXPORT CreatePointCloud();
} // namespace xavia::sdk