#pragma once

#include "dll/LogColumns.h"

namespace inertia
{

enum ServiceHardwareTypeID : unsigned;

/** Options to handle an existing logfile. */
enum ExistBehaviour : unsigned {
	EXIST_OVERWRITE = 0, ///< Overwrite the existing file.
	EXIST_APPEND,        ///< Append data to the existing file.
	EXIST_AUTONUMBER,    ///< Add a unique number to the new file.
	EXIST_DATETIME,      ///< Add a local timestamp to the new file.
	EXIST_DATETIME_UTC   ///< Add a UTC timestamp to the new file.
};


/** Supported logfile formats. */
enum LogFileType : unsigned {
	LOG_FILETYPE_AUTODETECT = 0, ///< Determine file-type based on extension, defaults to ::LOG_FILETYPE_ITLOG.
	LOG_FILETYPE_ITLOG,          ///< Inertia Logfile, binary format.
	LOG_FILETYPE_CSV,            ///< Comma Separated Value format.
	LOG_FILETYPE_BVH,            ///< BioVision Hierarchy format.
	LOG_FILETYPE_MAT,            ///< MATLAB format.
	LOG_FILETYPE_V3D,            ///< Visual3D ASCII format.
	LOG_FILETYPE_LAST
};


/** @brief LogWriter File Settings
 *
 *  Struct containing general settings for creating a logfile.
 */
struct INERTIA_API LogWriterFileSettings
{
	bool enableLogging;            ///< Enable creating a logfile.
	std::wstring logfile;          ///< The (base) name of the logfile.
	LogFileType logFileType;       ///< The file-type of the logfile.
	ExistBehaviour existBehaviour; ///< The way to handle existing file with the specified logfile name.
	std::wstring logfilePostfix;   ///< Post-fix to append to the logfile name.
	long maxFileSize;              ///< The maximum file size of a logfile before starting a new file (only :::LOG_FILETYPE_ITLOG).
	long maxDuration;              ///< The maximum duration of a logfile before starting a new file (only :::LOG_FILETYPE_ITLOG).
	bool useFlashCreateDate;       ///< Set the creation timestamp of the logfile to the date of a downloaded flash file (when using ::DownloadFlashStart()).

	/** Default constructor.
	 */
	LogWriterFileSettings();

	/** Constructor with individual arguments.
	 *  @param enableLogging enable logging
	 *  @param logfile the filename
	 *  @param existBehaviour the way to handle an existing file
	 *  @param logfilePostfix a post-fix to add to the filename
	 *  @param logFileType explicitly set the file-type to use
	 */
	LogWriterFileSettings(const bool enableLogging, std::wstring const& logfile,
		const ExistBehaviour existBehaviour = EXIST_AUTONUMBER, std::wstring const& logfilePostfix = std::wstring(),
		const LogFileType logFileType = LOG_FILETYPE_AUTODETECT);

	/** Constructor with string argument.
	 *  Creates LogWriterFileSettings from a string.
	 *  @param argString string with arguments
	 */
	LogWriterFileSettings(std::wstring const& argString);

	/** Get the actual file-type that will be used.
	 *  @return the file-type
	 */
	LogFileType GetLogFileTypeUsed() const;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	/** Get the possible command-line arguments for LogWriterFileSettings.
	 *  Only available when \a WITH_BOOST_PROGRAM_OPTIONS is defined.
	 *  @return the option description
	 */
	static const po::options_description GetOptionDescription();
#endif

	/** Get a string representation of this LogWriterFileSettings.
	 *  The string is compatible with GetOptionDescription() and LogWriterFileSettings(std::wstring const&).
	 *  @return the string
	 */
	const std::wstring ToString() const;
};


/** @brief LogWriter Source Settings
 *
 *  Struct containing information about the file.
 */
struct INERTIA_API LogWriterSourceInfo
{
	time_t createDate;             ///< UTC timestamp when the file is created.
	std::wstring description;      ///< Description that can be added to the file.
	std::wstring versionInfo;      ///< Version of the Inertia Library that creates the file, automatically filled in but can be overwritten.
	std::wstring originalFilename; ///< The original name of the file, for example the name in the flash.

	/** Default constructor.
	 */
	LogWriterSourceInfo();

	/** Constructor with string argument.
	 *  Creates LogWriterSourceInfo from a string.
	 *  @param argString string with arguments
	 */
	LogWriterSourceInfo(std::wstring const& argString);

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	/** Get the possible command-line arguments for LogWriterSourceInfo.
	 *  Only available when \a WITH_BOOST_PROGRAM_OPTIONS is defined.
	 *  @return the option description
	 */
	static const po::options_description GetOptionDescription();
#endif

	/** Get a string representation of this LogWriterSourceInfo.
	 *  The string is compatible with GetOptionDescription() and LogWriterSourceInfo(std::wstring const&).
	 *  @return the string
	 */
	const std::wstring ToString() const;
};


/** @brief LogWriter CSV Settings
 *
 *  Struct containing specific information for creating a Comma Separated Values (CSV) file.
 *  Information lines are prepended with a '#'.
 */
struct INERTIA_API LogWriterCSVSettings
{
	bool perNode;                  ///< Enable to create a separate CSV file for each node.
	unsigned includeInfo;          ///< Add information (description, configurations) to the beginning of the file: 0 = nothing, 1 = with simple config, 2 = with advanced config
	LogColumns columns;            ///< The data to include in the CSV file. Note that GPS will be logged in a separate file with '-gps' post-fix.
	bool usePrevious;              ///< When data for a sensor is missing, repeat the previous value.
	bool interpolateMissingSample; ///< When there is 1 lost sample, try to fill it by interpolating it.
	bool onlyValidTimestamps;      ///< Only include samples with valid timestamps.
	bool onlyWithSamples;          ///< Only include samples with at least 1 sensor value.
	bool includeAllConfigurations; ///< Add configuration information within the logfile to the file (not only at the beginning). includeInfo should be enabled as well.

	/** Default constructor.
	 */
	LogWriterCSVSettings();

	/** Constructor with individual arguments.
	 *  @param perNode enable one file per node
	 *  @param includeInfo include information
	 *  @param columns data to include
	 *  @param usePrevious repeat previous value
	 *  @param interpolateMissingSample interpolate a missing sample
	 *  @param onlyValidTimestamps only include samples with valid timestamps
	 *  @param onlyWithSamples only include samples with sensor data
	 */
	LogWriterCSVSettings(const bool perNode, const unsigned includeInfo, const LogColumns& columns,
		const bool usePrevious, const bool interpolateMissingSample, const bool onlyValidTimestamps, const bool onlyWithSamples);

	/** Constructor with string argument.
	 *  Creates LogWriterCSVSettings from a string.
	 *  @param argString string with arguments
	 */
	LogWriterCSVSettings(std::wstring const& argString);

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	/** Get the possible command-line arguments for LogWriterCSVSettings.
	 *  Only available when \a WITH_BOOST_PROGRAM_OPTIONS is defined.
	 *  @return the option description
	 */
	static const po::options_description GetOptionDescription();
#endif

	/** Get a string representation of this LogWriterCSVSettings.
	 *  The string is compatible with GetOptionDescription() and LogWriterCSVSettings(std::wstring const&).
	 *  @return the string
	 */
	const std::wstring ToString() const;
};


/** @brief BVH Joint Settings
 *
 *  Struct containing info for a BVHJoint, as used in LogWriterBVHSettings.
 */
struct INERTIA_API BVHJoint
{
	std::wstring name;   ///< The name of the joint.
	unsigned nodeID;     ///< The unique nodeID of the joint, 0 for end sites.
	unsigned parentID;   ///< The nodeID of the parent joint, 0 when root.
	unsigned childIndex; ///< The index within the parent joint.
	double offsetX;      ///< The X-offset of the joint from the parent (length).
	double offsetY;      ///< The Y-offset of the joint from the parent (width).
	double offsetZ;      ///< The Z-offset of the joint from the parent (height).
	double posX;         ///< The X-position of the joint (only for root).
	double posY;         ///< The Y-position of the joint (only for root).
	double posZ;         ///< The Z-position of the joint (only for root).

	/** Default constructor.
	 */
	BVHJoint();

	/** Constructor with individual arguments.
	 *  @param name the name of the joint
	 *  @param nodeID the nodeID of the joint
	 *  @param parentID the nodeID of the parent joint
	 *  @param childIndex the index within the parent joint
	 */
	BVHJoint(std::wstring const& name, const unsigned nodeID, const unsigned parentID, const unsigned childIndex);

	/** Constructor with string argument.
	 *  Creates BVHJoint from a string.
	 *  @param argString string with arguments
	 */
	BVHJoint(std::wstring const& argString);

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	/** Get the possible command-line arguments for BVHJoint.
	 *  Only available when \a WITH_BOOST_PROGRAM_OPTIONS is defined.
	 *  @return the option description
	 */
	static const po::options_description GetOptionDescription();
#endif

	/** Get a string representation of this BVHJoint.
	 *  The string is compatible with GetOptionDescription() and BVHJoint(std::wstring const&).
	 *  @return the string
	 */
	std::wstring const ToString() const;

	/** Get if the BVHJoint is a root.
	 *  @return true when the joint is a root
	 */
	bool IsRoot() const;

	/** Get if the BVHJoint is an end site.
	 *  @return true when the joint an end site
	 */
	bool IsEndSite() const;

	/** Get if the BVHJoint is an valid joint (not a root or end site).
	 *  @return true when it is a valid joint
	 */
	bool IsJoint() const;

	/** Set the offset of the BVHJoint.
	 *  @param x the X offset
	 *  @param y the Y offset
	 *  @param z the Z offset
	 */
	void SetOffset(const double x, const double y, const double z);

	/** Set the position of the BVHJoint.
	 *  @param x the X position
	 *  @param y the Y position
	 *  @param z the Z position
	 */
	void SetPosition(const double x, const double y, const double z);
};


/** @brief BVH Log Settings
 *
 *  Settings for creating a BioVision Hierarchy (BVH) file.
 */
struct INERTIA_API LogWriterBVHSettings
{
	/** Type of BVH separator between angles. */
	enum BVHAngleSeparator : unsigned {
		BVH_SEPARATOR_SPACE_ALIGNED = 0,  ///< Separate with multiple spaces to align the columns.
		BVH_SEPARATOR_SPACE_SINGLE        ///< Separate with a single space.
	};

	std::vector<BVHJoint> joints; ///< Joints that represent the BVH model.
	double rotationAngle;         ///< Yaw rotation to apply to make the model line up with the data.
	double initialPositionTime;   ///< Number of seconds after which to stop calibrating.
	double initialPositionDuration; ///< Number of seconds to use for calibration data.
	BVHAngleSeparator separator;  ///< Separation characters between frame angles.

	/** Default constructor.
	 */
	LogWriterBVHSettings();

	/** Constructor with string argument.
	 *  Creates LogWriterBVHSettings from a string.
	 *  @param argString string with arguments
	 */
	LogWriterBVHSettings(std::wstring const& argString);

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	/** Get the possible command-line arguments for LogWriterBVHSettings.
	 *  Only available when \a WITH_BOOST_PROGRAM_OPTIONS is defined.
	 *  @return the option description
	 */
	static const po::options_description GetOptionDescription();
#endif

	/** Get a string representation of this LogWriterBVHSettings.
	 *  The string is compatible with GetOptionDescription() and LogWriterBVHSettings(std::wstring const&).
	 *  @return the string
	 */
	const std::wstring ToString() const;
};


/** @brief MAT Log Settings
 *
 *  Settings for creating a MATLAB (MAT) file.
 */
struct INERTIA_API LogWriterMATSettings
{
	/** MAT File version options. */
	enum class FileVersion {
		MAT_6,     ///< MATLAB -v6, uncompressed
		MAT_7,     ///< MATLAB -v7, compressed
		MAT_7_3,   ///< MATLAB -v7.3, compressed HDF5 format
	};

	LogColumns columns;         ///< The data to include in the MAT file.
	size_t sampleCount;         ///< The number of samples to add to the MAT file, 0 to first count all samples.
	std::wstring structName;    ///< The global name of the structure in the MAT file containing the data.
	FileVersion matFileVersion; ///< The MAT file version, default is FileVersion::MAT_7

	/** Default constructor.
	 */
	LogWriterMATSettings();

	/** Constructor with string argument.
	 *  Creates LogWriterMATSettings from a string.
	 *  @param argString string with arguments
	 */
	LogWriterMATSettings(std::wstring const& argString);

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	/** Get the possible command-line arguments for LogWriterMATSettings.
	 *  Only available when \a WITH_BOOST_PROGRAM_OPTIONS is defined.
	 *  @return the option description
	 */
	static const po::options_description GetOptionDescription();
#endif

	/** Get a string representation of this LogWriterMATSettings.
	 *  The string is compatible with GetOptionDescription() and LogWriterMATSettings(std::wstring const&).
	 *  @return the string
	 */
	const std::wstring ToString() const;
};


/** @brief V3D Log Settings
 *
 *  Settings for creating a Visual3D ACII (V3D) file.
 */
struct INERTIA_API LogWriterV3DSettings
{
	LogColumns columns;         ///< The data to include in the V3D file.
	bool usePrevious;           ///< When data for a sensor is missing, repeat the previous value.
	std::wstring owner;         ///< The source of the data, default is 'GLOBAL'.
	std::wstring emptyValue;    ///< Use this value when there is no known sensor value.
	bool enablePointRate;       ///< Add a point rate time signal.
	double pointRateStartTime;  ///< The start time of the point rate time signal.
	bool enableAnalogRate;      ///< Add an analog rate time signal.
	double analogRateStartTime; ///< The start time of the analog rate time signal.

	/** Default constructor.
	 */
	LogWriterV3DSettings();

	/** Constructor with string argument.
	 *  Creates LogWriterV3DSettings from a string.
	 *  @param argString string with arguments
	 */
	LogWriterV3DSettings(std::wstring const& argString);

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	/** Get the possible command-line arguments for LogWriterV3DSettings.
	 *  Only available when \a WITH_BOOST_PROGRAM_OPTIONS is defined.
	 *  @return the option description
	 */
	static const po::options_description GetOptionDescription();
#endif

	/** Get a string representation of this LogWriterV3DSettings.
	 *  The string is compatible with GetOptionDescription() and LogWriterV3DSettings(std::wstring const&).
	 *  @return the string
	 */
	const std::wstring ToString() const;
};


/** @brief Log Writer Settings
 *
 *  Settings for writing a logfile.
 */
struct INERTIA_API LogWriterSettings
{
	LogWriterFileSettings fileSettings; ///< General settings for creating a logfile.
	LogWriterCSVSettings csvSettings;   ///< Specific settings for creating a CSV file.
	LogWriterBVHSettings bvhSettings;   ///< Specific settings for creating a BVH file.
	LogWriterMATSettings matSettings;   ///< Specific settings for creating a MAT file.
	LogWriterV3DSettings v3dSettings;   ///< Specific settings for creating a V3D file.
	LogWriterSourceInfo sourceInfo;     ///< Information about the file.

	/** Default constructor.
	 */
	LogWriterSettings();

	/** Constructor with string argument.
	 *  Creates LogWriterSettings from a string.
	 *  @param argString string with arguments
	 */
	LogWriterSettings(std::wstring const& argString);

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	/** Get the possible command-line arguments for LogWriterSettings.
	 *  Only available when \a WITH_BOOST_PROGRAM_OPTIONS is defined.
	 *  @return the option description
	 */
	static const po::options_description GetOptionDescription();
#endif

	/** Get a string representation of this LogWriterSettings.
	 *  The string is compatible with GetOptionDescription() and LogWriterSettings(std::wstring const&).
	 *  @return the string
	 */
	const std::wstring ToString() const;
};


/** @brief Validation Settings
 *
 *  Class containing settings for validating an ITLOG file.
 *  And device info when this is not encoded in the ITLOG file.
 */
struct INERTIA_API ValidateFileSettings
{
	bool validateHeader;                ///< Check if the file to convert has a valid ITLOG header.
	unsigned nodeID;                    ///< NodeID to use when it is not encoded in the file (e.g. for SD-card .LOG files).
	ServiceHardwareTypeID hardwareType; ///< Hardware type to use when it is not encoded in the file (e.g. for SD-card .LOG files).

	/** Default constructor. */
	ValidateFileSettings();

	/** Constructor with string argument.
	 *  Creates ValidateFileSettings from a string.
	 *  @param argString string with arguments
	 */
	ValidateFileSettings(std::wstring const& argString);

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	/** Get the possible command-line arguments for ValidateFileSettings.
	 *  Only available when \a WITH_BOOST_PROGRAM_OPTIONS is defined.
	 *  @return the option description
	 */
	static const po::options_description GetOptionDescription();
#endif

	/** Get a string representation of this ValidateFileSettings.
	 *  The string is compatible with GetOptionDescription() and ValidateFileSettings(std::wstring const&).
	 *  @return the string
	 */
	std::wstring const ToString() const;
};


/** @brief Log Reader Settings
 *
 *  Settings for reading a logfile.
 */
struct INERTIA_API LogReaderSettings
{
	std::wstring logfile;    ///< The logfile to read.
	unsigned readInterval;   ///< Interval between subsequent line reads in microseconds.
	bool autoRead;           ///< Read the file asynchronously (true) and use StreamListener callbacks, or manually (false) via ReplayGetSamples().
	unsigned maxDataBuffer;  ///< Maximum number of samples to buffer.
	bool writeReplay;        ///< Rewrite the logfile with recalculated orientation data, adds post-fix '-replay' to the new file. Requires autoRead enabled. Only applicable when reading a CSV file.
	LogFileType logFileType; ///< The file-type of the logfile to read.

	ValidateFileSettings validateSettings; ///< Settings for validating the file to convert.

	/** Default constructor.
	 */
	LogReaderSettings();

	/** Constructor with individual arguments.
	 *  @param logfile the logfile to read
	 *  @param readInterval the read interval in microseconds
	 *  @param autoRead read asynchronous or synchronous.
	 *  @param logFileType file-type of the logfile
	 */
	LogReaderSettings(std::wstring const& logfile, const unsigned readInterval, const bool autoRead, const LogFileType logFileType = LOG_FILETYPE_AUTODETECT);

	/** Constructor with string argument.
	 *  Creates LogReaderSettings from a string.
	 *  @param argString string with arguments
	 */
	LogReaderSettings(std::wstring const& argString);

	/** Get the actual file-type that will be used.
	 *  @return the file-type
	 */
	LogFileType GetLogFileTypeUsed() const;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	/** Get the possible command-line arguments for LogReaderSettings.
	 *  Only available when \a WITH_BOOST_PROGRAM_OPTIONS is defined.
	 *  @return the option description
	 */
	static const po::options_description GetOptionDescription();
#endif

	/** Get a string representation of this LogReaderSettings.
	 *  The string is compatible with GetOptionDescription() and LogReaderSettings(std::wstring const&).
	 *  @return the string
	 */
	std::wstring const ToString() const;
};

}
