#ifndef WELLSAMPLE_HPP_INCLUDED
#define WELLSAMPLE_HPP_INCLUDED

#include <iostream>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <vector>
#include <NBO/NBO/ImageRef.h>
#include <NBO/NBO/NonNegativeInt_Type.h>
#include <NBO/NBO/UnitsLength_Type.h>
#include <NBO/NBO/WellSampleID_Type.h>
#include <NBO/NBO/ENBOType.h>
#include <NBO/NBOGlobal.h>
#include <NBO/Shared/Base/NBOBaseClass.h>


namespace Inscoper::NBO {

/*!
* \brief Represents the XML element WellSample.
*
* WellSample is an individual Image that has been captured within a Well. 
*/
class NBO_EXPORT WellSample : public NBOBaseClass {

public:
	
	WellSample();
	~WellSample();

	/*!
	* \brief Cast a NBOBaseClass to a WellSample.
	*
	* \param base The base class to cast
	* \return The casted element, or nullptr if the cast fails
	*/
	static std::shared_ptr<WellSample> fromBase(NBOBaseClassPtr base);

	/*!
	* \brief Returns the class type name.
	*
	* \return The class type name
	*/
	std::string getTypeName() const override;

	/*!
	* \brief Returns the XML element name corresponding to this class.
	*
	* \return The XML element name
	*/
	std::string getXMLName() const override;

	/*!
	* \brief Returns the NBO Type corresponding to this class.
	*
	* \return The NBO Type value
	*/
	ENBOType getNBOType() const override;

	/*!
	* \brief Returns a list of all classes derived from this class.
	*
	* \return The list of derived classes
	*/
	std::vector<NBOBaseClassPtr> getChildren() const override;

	/*!
	* \brief Returns a list of all child elements contained in this class.
	*
	* \return The list of child classes
	*/
	std::vector<NBOBaseClassPtr> getSubElements() const override;

	/*!
	* \brief Populates this object from a JSON file.
	*
	* \param path The path to the JSON file
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void fromJsonFile(const std::string& path, bool validate = true);

	/*!
	* \brief Populates this object from a JSON string.
	*
	* \param json_string The JSON content as a string
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void fromJsonString(const std::string& json_string, bool validate = true);

	/*!
	* \brief Populates this object from a XML file.
	*
	* \param path The path to the XML file
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void fromXmlFile(const std::string& path, bool validate = true);

	/*!
	* \brief Populates this object from a XML string.
	*
	* \param xml_string The XML content as a string
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void fromXmlString(const std::string& xml_string, bool validate = true);

	/*!
	* \brief Serializes this object to an XML file.
	*
	* \param filename The path to the output XML file
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void toXmlFile(const std::string& filename, bool validate = true) const;

	/*!
	* \brief Serializes this object to an XML string.
	*
	* \param validate If true, validates constraints on value. Default is true.
	* \return A string containing the XML representation of this object
	*/
	std::string toXmlString(bool validate = true) const;

	/*!
	* \brief Sets the value of the ID attribute.
	*
	* No description available in the XSD standard.
	*
	* \param value The value to set
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void setID(const Inscoper::NBO::WellSampleID_TypePtr& value, bool validate = true);

	/*!
	* \brief Returns the value of the ID attribute.
	*
	* No description available in the XSD standard.
	*
	* \return The value of the attribute
	*/
	WellSampleID_TypePtr getID() const;

	/*!
	* \brief Resets the ID attribute to an unset state.
	*
	* No description available in the XSD standard.
	*/
	void resetID();

	/*!
	* \brief Checks whether the optional ID attribute is present.
	*
	* No description available in the XSD standard.
	*
	* \return true if the optional attribute has been set, false otherwise
	*/
	bool hasID() const;

	/*!
	* \brief Sets the value of the PositionX attribute.
	*
	* The X position of the field (Image) within the well relative to the well origin defined on the Plate. Units are set by PositionXUnit. 
	*
	* \param value The value to set
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void setPositionX(const float& value, bool validate = true);

	/*!
	* \brief Returns the value of the PositionX attribute.
	*
	* The X position of the field (Image) within the well relative to the well origin defined on the Plate. Units are set by PositionXUnit. 
	*
	* \return The value of the attribute
	*/
	float getPositionX() const;

	/*!
	* \brief Resets the PositionX attribute to an unset state.
	*
	* The X position of the field (Image) within the well relative to the well origin defined on the Plate. Units are set by PositionXUnit. 
	*/
	void resetPositionX();

	/*!
	* \brief Checks whether the PositionX attribute is present.
	*
	* The X position of the field (Image) within the well relative to the well origin defined on the Plate. Units are set by PositionXUnit. 
	*
	* \return true if the attribute has been set, false otherwise
	*/
	bool hasPositionX() const;

	/*!
	* \brief Sets the value of the PositionXUnit attribute.
	*
	* No description available in the XSD standard.
	*
	* \param value The value to set
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void setPositionXUnit(const Inscoper::NBO::UnitsLength_TypePtr& value, bool validate = true);

	/*!
	* \brief Returns the value of the PositionXUnit attribute.
	*
	* No description available in the XSD standard.
	*
	* \return The value of the attribute
	*/
	UnitsLength_TypePtr getPositionXUnit() const;

	/*!
	* \brief Resets the PositionXUnit attribute to an unset state.
	*
	* No description available in the XSD standard.
	*/
	void resetPositionXUnit();

	/*!
	* \brief Checks whether the optional PositionXUnit attribute is present.
	*
	* No description available in the XSD standard.
	*
	* \return true if the optional attribute has been set, false otherwise
	*/
	bool hasPositionXUnit() const;

	/*!
	* \brief Sets the value of the PositionY attribute.
	*
	* The Y position of the field (Image) within the well relative to the well origin defined on the Plate. Units are set by PositionYUnit. 
	*
	* \param value The value to set
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void setPositionY(const float& value, bool validate = true);

	/*!
	* \brief Returns the value of the PositionY attribute.
	*
	* The Y position of the field (Image) within the well relative to the well origin defined on the Plate. Units are set by PositionYUnit. 
	*
	* \return The value of the attribute
	*/
	float getPositionY() const;

	/*!
	* \brief Resets the PositionY attribute to an unset state.
	*
	* The Y position of the field (Image) within the well relative to the well origin defined on the Plate. Units are set by PositionYUnit. 
	*/
	void resetPositionY();

	/*!
	* \brief Checks whether the PositionY attribute is present.
	*
	* The Y position of the field (Image) within the well relative to the well origin defined on the Plate. Units are set by PositionYUnit. 
	*
	* \return true if the attribute has been set, false otherwise
	*/
	bool hasPositionY() const;

	/*!
	* \brief Sets the value of the PositionYUnit attribute.
	*
	* No description available in the XSD standard.
	*
	* \param value The value to set
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void setPositionYUnit(const Inscoper::NBO::UnitsLength_TypePtr& value, bool validate = true);

	/*!
	* \brief Returns the value of the PositionYUnit attribute.
	*
	* No description available in the XSD standard.
	*
	* \return The value of the attribute
	*/
	UnitsLength_TypePtr getPositionYUnit() const;

	/*!
	* \brief Resets the PositionYUnit attribute to an unset state.
	*
	* No description available in the XSD standard.
	*/
	void resetPositionYUnit();

	/*!
	* \brief Checks whether the optional PositionYUnit attribute is present.
	*
	* No description available in the XSD standard.
	*
	* \return true if the optional attribute has been set, false otherwise
	*/
	bool hasPositionYUnit() const;

	/*!
	* \brief Sets the value of the Timepoint attribute.
	*
	* The time-point at which the Image started to be collected 
	*
	* \param value The value to set
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void setTimepoint(const std::string& value, bool validate = true);

	/*!
	* \brief Returns the value of the Timepoint attribute.
	*
	* The time-point at which the Image started to be collected 
	*
	* \return The value of the attribute
	*/
	std::string getTimepoint() const;

	/*!
	* \brief Resets the Timepoint attribute to an unset state.
	*
	* The time-point at which the Image started to be collected 
	*/
	void resetTimepoint();

	/*!
	* \brief Checks whether the Timepoint attribute is present.
	*
	* The time-point at which the Image started to be collected 
	*
	* \return true if the attribute has been set, false otherwise
	*/
	bool hasTimepoint() const;

	/*!
	* \brief Returns the allowed values for this attribut.
	*
	* The time-point at which the Image started to be collected 
	*
	* \return The list of allowed values
	*/
	static const std::set<std::string>& getTimepointAllowedValues();

	/*!
	* \brief Sets the value of the Index attribute.
	*
	* This records the order of the well samples. Each index should be unique for a given plate but they do not have to be sequential, there may be gaps if part of the dataset is missing. In the user interface the displayed value of the index will be calculated modulo the number of PlateAcquisitions for the plate. 
	*
	* \param value The value to set
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void setIndex(const Inscoper::NBO::NonNegativeInt_TypePtr& value, bool validate = true);

	/*!
	* \brief Returns the value of the Index attribute.
	*
	* This records the order of the well samples. Each index should be unique for a given plate but they do not have to be sequential, there may be gaps if part of the dataset is missing. In the user interface the displayed value of the index will be calculated modulo the number of PlateAcquisitions for the plate. 
	*
	* \return The value of the attribute
	*/
	NonNegativeInt_TypePtr getIndex() const;

	/*!
	* \brief Resets the Index attribute to an unset state.
	*
	* This records the order of the well samples. Each index should be unique for a given plate but they do not have to be sequential, there may be gaps if part of the dataset is missing. In the user interface the displayed value of the index will be calculated modulo the number of PlateAcquisitions for the plate. 
	*/
	void resetIndex();

	/*!
	* \brief Checks whether the optional Index attribute is present.
	*
	* This records the order of the well samples. Each index should be unique for a given plate but they do not have to be sequential, there may be gaps if part of the dataset is missing. In the user interface the displayed value of the index will be calculated modulo the number of PlateAcquisitions for the plate. 
	*
	* \return true if the optional attribute has been set, false otherwise
	*/
	bool hasIndex() const;

	/*!
	* \brief Sets the value of the ImageRef element.
	*
	* This is the main link to the core Image element 
	*
	* \param value The value to set
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void setImageRef(const Inscoper::NBO::ImageRefPtr& value, bool validate = true);

	/*!
	* \brief Returns the value of the ImageRef element.
	*
	* This is the main link to the core Image element 
	*
	* \return The value of the element
	*/
	Inscoper::NBO::ImageRefPtr getImageRef() const;

	/*!
	* \brief Resets the ImageRef element to an unset state.
	*
	* This is the main link to the core Image element 
	*/
	void resetImageRef();

	/*!
	* \brief Checks whether the optional ImageRef element is present.
	*
	* This is the main link to the core Image element 
	*
	* \return true if the optional attribute has been set, false otherwise
	*/
	bool hasImageRef() const;


private:
	class Impl;
	std::unique_ptr<Impl> pImpl;

	WellSampleID_TypePtr ID = nullptr; // Required attribute
    std::optional<float> PositionX; // Optional attribute
	UnitsLength_TypePtr PositionXUnit = nullptr; // Optional attribute
    std::optional<float> PositionY; // Optional attribute
	UnitsLength_TypePtr PositionYUnit = nullptr; // Optional attribute
    std::optional<std::string> Timepoint; // Optional attribute
    static const std::set<std::string> TimepointAllowedValues;
	NonNegativeInt_TypePtr Index = nullptr; // Required attribute
    Inscoper::NBO::ImageRefPtr ImageRef = nullptr;
};

/*! Shared pointer for WellSample*/
typedef std::shared_ptr<WellSample> WellSamplePtr;

} // namespace Inscoper::NBO

#endif // WELLSAMPLE_HPP_INCLUDED