#ifndef WELL_HPP_INCLUDED
#define WELL_HPP_INCLUDED

#include <iostream>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <vector>
#include <NBO/NBO/AnnotationRef.h>
#include <NBO/NBO/Color_Type.h>
#include <NBO/NBO/NonNegativeInt_Type.h>
#include <NBO/NBO/ReagentRef.h>
#include <NBO/NBO/WellID_Type.h>
#include <NBO/NBO/WellSample.h>
#include <NBO/NBO/ENBOType.h>
#include <NBO/NBOGlobal.h>
#include <NBO/Shared/Base/NBOBaseClass.h>


namespace Inscoper::NBO {

/*!
* \brief Represents the XML element Well.
*
* A Well is a component of the Well/Plate/Screen construct to describe screening applications. A Well has a number of WellSample elements that link to the Images collected in this well. The ReagentRef links any Reagents that were used in this Well. A well is part of only one Plate. The origin for the row and column identifiers is the top left corner of the plate starting at zero. i.e The top left well of a plate is index (0,0) 
*/
class NBO_EXPORT Well : public NBOBaseClass {

public:
	
	Well();
	~Well();

	/*!
	* \brief Cast a NBOBaseClass to a Well.
	*
	* \param base The base class to cast
	* \return The casted element, or nullptr if the cast fails
	*/
	static std::shared_ptr<Well> 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::WellID_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
	*/
	WellID_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 Column attribute.
	*
	* This is the column index of the well, the origin is the top left corner of the plate with the first column of cells being column zero. i.e top left is (0,0) The combination of Row, Column has to be unique for each well in a plate. 
	*
	* \param value The value to set
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void setColumn(const Inscoper::NBO::NonNegativeInt_TypePtr& value, bool validate = true);

	/*!
	* \brief Returns the value of the Column attribute.
	*
	* This is the column index of the well, the origin is the top left corner of the plate with the first column of cells being column zero. i.e top left is (0,0) The combination of Row, Column has to be unique for each well in a plate. 
	*
	* \return The value of the attribute
	*/
	NonNegativeInt_TypePtr getColumn() const;

	/*!
	* \brief Resets the Column attribute to an unset state.
	*
	* This is the column index of the well, the origin is the top left corner of the plate with the first column of cells being column zero. i.e top left is (0,0) The combination of Row, Column has to be unique for each well in a plate. 
	*/
	void resetColumn();

	/*!
	* \brief Checks whether the optional Column attribute is present.
	*
	* This is the column index of the well, the origin is the top left corner of the plate with the first column of cells being column zero. i.e top left is (0,0) The combination of Row, Column has to be unique for each well in a plate. 
	*
	* \return true if the optional attribute has been set, false otherwise
	*/
	bool hasColumn() const;

	/*!
	* \brief Sets the value of the Row attribute.
	*
	* This is the row index of the well, the origin is the top left corner of the plate with the first row of wells being row zero. i.e top left is (0,0) The combination of Row, Column has to be unique for each well in a plate. 
	*
	* \param value The value to set
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void setRow(const Inscoper::NBO::NonNegativeInt_TypePtr& value, bool validate = true);

	/*!
	* \brief Returns the value of the Row attribute.
	*
	* This is the row index of the well, the origin is the top left corner of the plate with the first row of wells being row zero. i.e top left is (0,0) The combination of Row, Column has to be unique for each well in a plate. 
	*
	* \return The value of the attribute
	*/
	NonNegativeInt_TypePtr getRow() const;

	/*!
	* \brief Resets the Row attribute to an unset state.
	*
	* This is the row index of the well, the origin is the top left corner of the plate with the first row of wells being row zero. i.e top left is (0,0) The combination of Row, Column has to be unique for each well in a plate. 
	*/
	void resetRow();

	/*!
	* \brief Checks whether the optional Row attribute is present.
	*
	* This is the row index of the well, the origin is the top left corner of the plate with the first row of wells being row zero. i.e top left is (0,0) The combination of Row, Column has to be unique for each well in a plate. 
	*
	* \return true if the optional attribute has been set, false otherwise
	*/
	bool hasRow() const;

	/*!
	* \brief Sets the value of the ExternalDescription attribute.
	*
	* A description of the externally defined identifier for this plate. 
	*
	* \param value The value to set
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void setExternalDescription(const std::string& value, bool validate = true);

	/*!
	* \brief Returns the value of the ExternalDescription attribute.
	*
	* A description of the externally defined identifier for this plate. 
	*
	* \return The value of the attribute
	*/
	std::string getExternalDescription() const;

	/*!
	* \brief Resets the ExternalDescription attribute to an unset state.
	*
	* A description of the externally defined identifier for this plate. 
	*/
	void resetExternalDescription();

	/*!
	* \brief Checks whether the ExternalDescription attribute is present.
	*
	* A description of the externally defined identifier for this plate. 
	*
	* \return true if the attribute has been set, false otherwise
	*/
	bool hasExternalDescription() const;

	/*!
	* \brief Returns the allowed values for this attribut.
	*
	* A description of the externally defined identifier for this plate. 
	*
	* \return The list of allowed values
	*/
	static const std::set<std::string>& getExternalDescriptionAllowedValues();

	/*!
	* \brief Sets the value of the ExternalIdentifier attribute.
	*
	* The ExternalIdentifier attribute may contain a reference to an external database. 
	*
	* \param value The value to set
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void setExternalIdentifier(const std::string& value, bool validate = true);

	/*!
	* \brief Returns the value of the ExternalIdentifier attribute.
	*
	* The ExternalIdentifier attribute may contain a reference to an external database. 
	*
	* \return The value of the attribute
	*/
	std::string getExternalIdentifier() const;

	/*!
	* \brief Resets the ExternalIdentifier attribute to an unset state.
	*
	* The ExternalIdentifier attribute may contain a reference to an external database. 
	*/
	void resetExternalIdentifier();

	/*!
	* \brief Checks whether the ExternalIdentifier attribute is present.
	*
	* The ExternalIdentifier attribute may contain a reference to an external database. 
	*
	* \return true if the attribute has been set, false otherwise
	*/
	bool hasExternalIdentifier() const;

	/*!
	* \brief Returns the allowed values for this attribut.
	*
	* The ExternalIdentifier attribute may contain a reference to an external database. 
	*
	* \return The list of allowed values
	*/
	static const std::set<std::string>& getExternalIdentifierAllowedValues();

	/*!
	* \brief Sets the value of the Type 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 setType(const std::string& value, bool validate = true);

	/*!
	* \brief Returns the value of the Type attribute.
	*
	* No description available in the XSD standard.
	*
	* \return The value of the attribute
	*/
	std::string getType() const;

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

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

	/*!
	* \brief Returns the allowed values for this attribut.
	*
	* No description available in the XSD standard.
	*
	* \return The list of allowed values
	*/
	static const std::set<std::string>& getTypeAllowedValues();

	/*!
	* \brief Sets the value of the Color 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 setColor(const Inscoper::NBO::Color_TypePtr& value, bool validate = true);

	/*!
	* \brief Returns the value of the Color attribute.
	*
	* No description available in the XSD standard.
	*
	* \return The value of the attribute
	*/
	Color_TypePtr getColor() const;

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

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

	/*!
	* \brief Sets the list of WellSample elements.
	*
	* No description available in the XSD standard.
	*
	* \param value The new list of WellSample elements
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void setWellSample_List(const std::vector<Inscoper::NBO::WellSamplePtr>& value, bool validate = true);

	/*!
	* \brief Returns the list of WellSample elements.
	*
	* No description available in the XSD standard.
	*
	* \return The list of WellSample elements
	*/
	std::vector<Inscoper::NBO::WellSamplePtr> getWellSample_List() const;

	/*!
	* \brief Adds a new WellSample element to this object.
	*
	* No description available in the XSD standard.
	*
	* \param value The element to add
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void addToWellSample_List(const Inscoper::NBO::WellSamplePtr& value, bool validate = true);

	/*!
	* \brief Clear the list of WellSample.
	*
	* No description available in the XSD standard.
	*/
	void clearWellSample_List();

	/*!
	* \brief Sets the value of the ReagentRef element.
	*
	* 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 setReagentRef(const Inscoper::NBO::ReagentRefPtr& value, bool validate = true);

	/*!
	* \brief Returns the value of the ReagentRef element.
	*
	* No description available in the XSD standard.
	*
	* \return The value of the element
	*/
	Inscoper::NBO::ReagentRefPtr getReagentRef() const;

	/*!
	* \brief Resets the ReagentRef element to an unset state.
	*
	* No description available in the XSD standard.
	*/
	void resetReagentRef();

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

	/*!
	* \brief Sets the list of AnnotationRef elements.
	*
	* No description available in the XSD standard.
	*
	* \param value The new list of AnnotationRef elements
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void setAnnotationRef_List(const std::vector<Inscoper::NBO::AnnotationRefPtr>& value, bool validate = true);

	/*!
	* \brief Returns the list of AnnotationRef elements.
	*
	* No description available in the XSD standard.
	*
	* \return The list of AnnotationRef elements
	*/
	std::vector<Inscoper::NBO::AnnotationRefPtr> getAnnotationRef_List() const;

	/*!
	* \brief Adds a new AnnotationRef element to this object.
	*
	* No description available in the XSD standard.
	*
	* \param value The element to add
	* \param validate If true, validates constraints on value. Default is true.
	*/
	void addToAnnotationRef_List(const Inscoper::NBO::AnnotationRefPtr& value, bool validate = true);

	/*!
	* \brief Clear the list of AnnotationRef.
	*
	* No description available in the XSD standard.
	*/
	void clearAnnotationRef_List();


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

	WellID_TypePtr ID = nullptr; // Required attribute
	NonNegativeInt_TypePtr Column = nullptr; // Required attribute
	NonNegativeInt_TypePtr Row = nullptr; // Required attribute
    std::optional<std::string> ExternalDescription; // Optional attribute
    static const std::set<std::string> ExternalDescriptionAllowedValues;
    std::optional<std::string> ExternalIdentifier; // Optional attribute
    static const std::set<std::string> ExternalIdentifierAllowedValues;
    std::optional<std::string> Type; // Optional attribute
    static const std::set<std::string> TypeAllowedValues;
	Color_TypePtr Color = nullptr; // Optional attribute
    std::vector<Inscoper::NBO::WellSamplePtr> WellSample_List;
    Inscoper::NBO::ReagentRefPtr ReagentRef = nullptr;
    std::vector<Inscoper::NBO::AnnotationRefPtr> AnnotationRef_List;
};

/*! Shared pointer for Well*/
typedef std::shared_ptr<Well> WellPtr;

} // namespace Inscoper::NBO

#endif // WELL_HPP_INCLUDED