// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//
// $Id: PTE.h,v 1.30.16.1 2007/03/25 21:23:42 oliver Exp $
//

#ifndef BALL_KERNEL_PTE_H
#define BALL_KERNEL_PTE_H

#ifndef BALL_COMMON_CREATE_H
# include <BALL/COMMON/create.h>
#endif

#ifndef BALL_DATATYPE_STRING_H
# include <BALL/DATATYPE/string.h>
#endif

#ifndef BALL_CONCEPT_PROCESSOR_H
# include <BALL/CONCEPT/processor.h>
#endif

#define BALL_ELEMENT_NAME_DEFAULT                "Unknown"
#define BALL_ELEMENT_SYMBOL_DEFAULT              "?"
#define BALL_ELEMENT_GROUP_DEFAULT               0
#define BALL_ELEMENT_PERIOD_DEFAULT              0
#define BALL_ELEMENT_ATOMICNUMBER_DEFAULT        0
#define BALL_ELEMENT_ATOMICWEIGHT_DEFAULT        0
#define BALL_ELEMENT_ATOMICRADIUS_DEFAULT        0
#define BALL_ELEMENT_COVALENTRADIUS_DEFAULT      0
#define BALL_ELEMENT_VANDERWAALSRADIUS_DEFAULT   0
#define BALL_ELEMENT_ELECTRONEGATIVITY_DEFAULT   0
#define BALL_ELEMENT_IS_METAL_DEFAULT            false


namespace BALL 
{
	/**	\defgroup PTE Periodic Table of Elements
			These classes represent elements and the periodic table of elements.
			The  \link Element Element \endlink  class is used to describe common properties of atoms
			(e.g., mass, nuclear charge, element symbol). Elements can be retrieved
			from the  \link PTE PTE \endlink  object (<b>P</b>eriodic <b>T</b>able of <b>E</b>lements)
			which is a global instance of  \link PTE_ PTE_ \endlink . \par
			
			@see Atom::setElement
			@see Atom::getElement
    	
			\ingroup  KernelMiscellaneous
	*/
	//@{

	/**	Element class.
			\ingroup KernelMiscellaneous	
	*/
	class BALL_EXPORT Element
	{
		public:

			BALL_CREATE(Element)

			/**	Group
			*/
			typedef short Group;

			/**	Period
			*/
			typedef short Period;

			/**	AtomicNumber
			*/
			typedef short AtomicNumber;

			/**	Name
					Element names.
			*/
			enum Name
			{
				ACTINIUM = 0,
				ALUMINUM,
				AMERICIUM,
				ANTIMONY, STIBIUM = ANTIMONY,
				ARGON,
				ARSENIC,
				ASTATINE,
				BARIUM,
				BERKELIUM,
				BERYLLIUM,
				BISMUTH, WISMUT = BISMUTH,
				BOHRIUM,
				BORON,
				BROMINE,
				CADMIUM,
				CAESIUM,
				CALCIUM,
				CALIFORNIUM,
				CARBON, CARBONEUM = CARBON,
				CERIUM,
				CHLORINE,
				CHROMIUM,
				COBALT,
				COPPER, CUPRUM = COPPER,
				CURIUM,
				DUBNIUM,
				DYSPROSIUM,
				EINSTEINIUM,
				ERBIUM,
				EUROPIUM,
				FERMIUM,
				FLUORINE,
				FRANCIUM,
				GADOLINIUM,
				GALLIUM,
				GERMANIUM,
				GOLD, AURUM = GOLD,
				HAFNIUM,
				HAHNIUM,
				HELIUM,
				HOLMIUM,
				HYDROGEN, HYDROGENIUM = HYDROGEN,
				INDIUM,
				IODINE, JOD = IODINE,
				IRIDIUM,
				IRON, FERRUM = IRON,
				JOLIOTIUM,
				KRYPTON,
				LANTHANUM,
				LAWRENCIUM,
				LEAD, PLUMBUM = LEAD,
				LITHIUM,
				LUTETIUM,
				MAGNESIUM,
				MANGANESE, MANGAN = MANGANESE,
				MEITNERIUM,
				MENDELEVIUM,
				MERCURY, HYDRARGYRUM = MERCURY, MERCURIUM = MERCURY,
				MOLYBDENUM,
				NEODYMIUM,
				NEON,
				NEPTUNIUM,
				NICKEL,
				NIOBIUM,
				NITROGEN, NITROGENIUM = NITROGEN,
				NOBELIUM,
				OSMIUM,
				OXYGEN, OXYGENIUM = OXYGEN,
				PALLADIUM,
				PHOSPHORUS,
				PLATINUM,
				PLUTONIUM,
				POLONIUM,
				POTASSIUM, KALIUM = POTASSIUM,
				PRASEODYMIUM,
				PROMETHIUM,
				PROTACTINIUM,
				RADIUM,
				RADON,
				RHENIUM,
				RHODIUM,
				RUBIDIUM,
				RUTHENIUM,
				RUTHERFORDIUM,
				SAMARIUM,
				SCANDIUM,
				SELENIUM,
				SILICON, SILICIUM = SILICON,
				SILVER, ARGENTUM = SILVER,
				SODIUM, NATRIUM = SODIUM,
				STRONTIUM,
				SULPHUR, SULFUR = SULPHUR,
				TANTALUM,
				TECHNETIUM,
				TELLURIUM,
				TERBIUM,
				THALLIUM,
				THORIUM,
				THULIUM,
				TIN, STANNUM = TIN,
				TITANIUM,
				TUNGSTEN, WOLFRAM = TUNGSTEN,
				UNUNBIUM,
				UNUNNILIUM,
				UNUNNINIUM,
				URANIUM,
				VANADIUM,
				XENON,
				YTTERBIUM,
				YTTRIUM,
				ZINC, ZINCUM = ZINC,
				ZIRCONIUM,
				
				NUMBER_OF_ELEMENTS //  111
			};

			/**	@name	Symbol
					Element symbols.
			*/
			enum Symbol
			{
				Ac = 0,
				Al,
				Am,
				Sb,
				Ar,
				As,
				At,
				Ba,
				Bk,
				Be,
				Bi,
				Bh,
				B,
				Br,
				Cd,
				Cs,
				Ca,
				Cf,
				C,
				Ce,
				Cl,
				Cr,
				Co,
				Cu,
				Cm,
				Db,
				Dy,
				Es,
				Er,
				Eu,
				Fm,
				F,
				Fr,
				Gd,
				Ga,
				Ge,
				Au,
				Hf,
				Hn,
				He,
				Ho,
				H,
				In,
				I,
				Ir,
				Fe,
				Jl,
				Kr,
				La,
				Lr,
				Pb,
				Li,
				Lu,
				Mg,
				Mn,
				Mt,
				Md,
				Hg,
				Mo,
				Nd,
				Ne,
				Np,
				Ni,
				Nb,
				N,
				No,
				Os,
				O,
				Pd,
				P,
				Pt,
				Pu,
				Po,
				K,
				Pr,
				Pm,
				Pa,
				Ra,
				Rn,
				Re,
				Rh,
				Rb,
				Ru,
				Rf,
				Sm,
				Sc,
				Se,
				Si,
				Ag,
				Na,
				Sr,
				S,
				Ta,
				Tc,
				Te,
				Tb,
				Tl,
				Th,
				Tm,
				Sn,
				Ti,
				W,
				Uub,
				Uun,
				Uuu,
				U,
				V,
				Xe,
				Yb,
				Y,
				Zn,
				Zr
			};

			/**	@name	Constructors and Destructors.
			*/
			//@{
			
			/**	Default constructor
					The instance is set to the default values
					(=  \link UNKNOWN UNKNOWN \endlink  element).
			*/
			Element();

			/**	Constructor
			*/
			Element(const Element& element);

			/**	Detailed constructor
			*/
			Element
				(const String& name,
				 const String& symbol,
				 Group group,
				 Period period,
				 AtomicNumber atomic_umber,
				 float atomic_weight,
				 float atomic_radius,
				 float covalent_radius,
				 float van_der_waals_radius,
				 bool is_metal,
				 float electronegativity);

			/**	Destructor
			*/
			virtual ~Element();

			/** Clear method.
					The instance is set to the default values
					(=  \link UNKNOWN UNKNOWN \endlink  element).
			*/
			virtual void clear();
			
			//@}
			/**	@name	Assignment 
			*/
			//@{

			/** Assignment operator.
					@param   element the Element to be copied (cloned)
					@return  Element& - this instance
			*/
			Element& operator = (const Element& element);

			//@}
			/** @name	Inspectors and Mutators
			*/
			//@{
		
			/** Set the name.
					@param name the new name
			*/
			void setName(const String& name);

			/**
			 * Get the name.
			 * @return The name of the element. For example if the Element is a hydrogen
			 *         the string "Hydrogen" is returned.
			 */
			const String& getName() const;

			/** Set the atomic symbol.
					@param symbol the new symbol
			*/
			void setSymbol(const String& symbol);

			/** Get the atomic symbol.
					@return String the symbol
			*/
			const String& getSymbol() const;

			/** Set the atomic group.
					@param group (short) the new group
			*/
			void setGroup(Group group);

			/** Get the atomic group.
					@return Group (short) - the group
			*/
			Group getGroup() const;

			/** Set the atomic period.
					@param period (short) the new period
			*/
			void setPeriod(Period period);

			/** Get the atomic period.
					@return Period (short) the period
			*/
			Period getPeriod() const;

			/** Set the atomic number.
					@param atomic_number the new atomic number
			*/
			void setAtomicNumber(AtomicNumber atomic_number);

			/** Get the atomic number.
					@return AtomicNumber (short) - the atomic number
			*/
			AtomicNumber getAtomicNumber() const;

			/** Set the atomic weight.
					@param atomic_weight the new atomic weight
			*/
			void setAtomicWeight(float atomic_weight);

			/** Get the atomic weight.
					@return float - the atomic weight
			*/
			float getAtomicWeight() const;

			/** Set the atomic radius.
					@param atomic_radius the new atomic radius (in Angstrom)
			*/
			void setAtomicRadius(float atomic_radius);

			/** Get the atomic radius.
					@return float the atomic radius (in Angstrom)
			*/
			float getAtomicRadius() const;

			/** Set the covalent radius.
					@param covalent_radius the new covalent radius (in Angstrom)
			*/
			void setCovalentRadius(float covalent_radius);

			/** Get the covalent radius.
					@return float the covalent radius (in Angstrom)
			*/
			float getCovalentRadius() const;

			/** Set the VanDerWaals radius.
					@param van_der_waals_radius the new VanDerWaals radius (in Angstrom)
			*/
			void setVanDerWaalsRadius(float van_der_waals_radius);

			/** Get the VanDerWaals radius.
					@return float the VanDerWaals radius (in Angstrom)
			*/
			float getVanDerWaalsRadius() const;

			/** Set the electronegativity.
					@param electronegativity the new electronegativity (according to the Pauling scale)
			*/
			void setElectronegativity(float electronegativity);

			/** Get the electronegativity.
					@return float the electronegativity (according to the Pauling scale)
			*/
			float getElectronegativity() const;

			//@}
			/** @name	Operators
			*/
			//@{

			/** Equality operator.
					Tests if the atomic number of two elements are equal.
					@param element the Element to compare with
					@return bool
			*/
			bool operator == (const Element& element) const;

			/** Inequality operator.
					Tests if the atomic number of two elements are inequal.
					@param element the Element to compare with
					@return bool
			*/
			bool operator != (const Element& element) const;

			/** Less operator.
					Tests if the atomic number of this instance is less
					compared with <b>  element </b>.
					@param element the Element to compare with
					@return bool
			*/
			bool operator < (const Element& element) const;

			/** Less or equal operator.
					Tests if the atomic number of this instance is less
					or equal compared with <b>  element </b>.
					@param element, the Element to compare with
					@return bool
			*/
			bool operator <= (const Element& element) const;

			/** Greater or equal operator.
					Tests if the atomic number of this instance is greater
					or equal compared with <b>  element </b>.
					@param element the Element to compare with
					@return bool
			*/
			bool operator >= (const Element& element) const;

			/** Greater operator.
					Tests if the atomic number of this instance is greater
					than <b>  element </b>.
					@param element the Element to compare with
					@return bool
			*/
			bool operator >(const Element& element) const;

			/**	Output operator
					Prints <b>  this </b> instance of Element.
			*/
			BALL_EXPORT
			friend std::ostream& operator << (std::ostream& s, const Element& element);

			//@}
			/** @name	Predicates
			*/
			//@{

			/** Test if this instance is unknown.
					Returns true if instance is equal the static Element UNKNOWN
					(The default constructor returns such an element).
					@return bool
			*/
			bool isUnknown() const;

			/**
			 * Test if the Element is a metal.
			 *
			 * @return true if the Element represents a metal.
			 */
			bool isMetal() const;

			//@}

			/** Standard element.
					(The default constructor returns such an element).					
			*/
			static Element UNKNOWN;


		private:

			/** the name of the element 
			*/
			String name_;

			/** the uppercased symbol of the element 
			*/
			String symbol_;

			/** The group number is an identifier used to describe the 
					column of the standard periodic table in which the 
				  element appears. 

				  NOTES:
				  There is considerable confusion surrounding the Group 
				  labels. The scheme used in WebElements is numeric and 
				  is the current IUPAC convention. The other two systems 
				  are less desirable since they are confusing, but still 
				  in common usage. The designations A and B are completely
				  arbitrary. The first of these (A left, B right) is based 
				  upon older IUPAC recommendations and frequently used in 
				  Europe. The last set (main group elements A, transition 
				  elements B) was in common use in America.

				  IUPAC, European, and American Group labelling schemes

					Group   European  American
					1       IA        IA    
					2       IIA       IIA            
					3       IIIA      IIIB    
					4       IVA       IVB   
					5       VA        VB  
					6       VIA       VIB   
					7       VIIA      VIIB    
					8       VIIIA     VIIIB     
					9       VIIIA     VIIIB     
					10      VIIIA     VIIIB      
					11      IB        IB   
					12      IIB       IIB    
					13      IIIB      IIIA     
					14      IVB       IVA    
					15      VB        VA                    
					16      VIB       VIA    
					17      VIIB      VIIA     
					18      VIIIB     VIIIA      
			*/
			Group 	group_;

			/** the period of the element 
			*/
			Period	period_;

			/** The atomic number corresponds to the number of protons 
			    in the nucleus of an atom of that element. 
			 */
			AtomicNumber atomic_number_;

			/** The atomic weight of an element (resp. of its most stabile 
			    isotope) is the ratio of the mass of one mole of the 
			    element in a defined source to 1/12 of the mass of 
			    chemically unbound C-12 in its nuclear and electronic
			    ground state. Adapted from the 1993 report of the IUPAC 
			    Commission on Atomic Weights and Isotopic Abundances 
			*/
			float atomic_weight_;
		
			/** atomic radius (in Angstrom) 
			*/
			float atomic_radius_;

			/** covalent radius (in Angstrom) 
			*/
			float  covalent_radius_;

			/** van der Waals radius (in Angstrom) 
			*/
			float van_der_waals_radius_;

			/** is the element a metal
			 */
			bool is_metal_;

			/** electronegativity (according to the Pauling scale) 
			*/
			float electronegativity_;
	};


	/**	Element output operator.
			Prints the contents of an instance of  \link Element Element \endlink  to an <tt>ostream</tt>.
	*/
	BALL_EXPORT
	std::ostream& operator << (std::ostream& s, const Element& element);


	/**	Periodic Table of Elements Class
	*/
	class BALL_EXPORT PTE_
	{
		public:

			BALL_CREATE(PTE_)

			/**	@name	Constructors and Destructors
			*/
			//@{

			/**	Default constructor
			*/
			PTE_();

			/**	Default constructor
			*/
			PTE_(const PTE_& pse);

			/**	Destructor
			*/
			virtual ~PTE_();

			/** Clear method.
			*/
			virtual void clear();

			//@}
			/**	@name	Assignment 
			*/
			//@{
		
			/** Assignment operator
					Does not assign anything.
					Implemented just for the OCI.
			*/
			PTE_& operator = (const PTE_& /*pte*/);
			
			//@}
			/**	@name	Accessors
			*/
			//@{

			/** Return the element that has the specified atomic number.
			*/
			static Element& getElement(Position atomic_number);
		
			/** Return the element with a given symbol.
					@return Element the static Element with the symbol <b>  symbol </b>
			*/
			static Element& getElement(const String& symbol);

			//@}
			/**	@name	Operators
			*/
			//@{
		
			/** Equality operator.
					Always returns true.
					Implemented just for the OCI.
			*/
			bool operator == (const PTE_& pte) const;

			/** Access operator with a given symbol.
					@return Element the Element with the symbol <b>  symbol </b>
			*/
			Element& operator [] (const String& symbol);

			/** Access operator with a given symbol.
					@return Element the const Element with the symbol <b>  symbol </b>
			*/
			const Element& operator [] (const String& symbol) const;

			/** Access operator with a given name.
					@return Element the Element with the name <b>  name </b>
			*/
			Element& operator [] (Element::Name name);

			/** Access operator with a given name.
					@return Element the const Element with the name <b>  name </b>
			*/
			const Element& operator [] (Element::Name name) const;

			/** Access operator with a given Element::Symbol.
					@return Element the Element with the symbol <b>  symbol </b>
			*/
			Element& operator [] (Element::Symbol symbol);

			/** Access operator with a given Element::Symbol.
					@return Element the const Element with the symbol <b>  symbol </b>
			*/
			const Element& operator [] (Element::Symbol symbol) const;

			/** Access operator with a given position.
					@return Element the Element with the position <b>  position </b>
			*/
			Element& operator [] (Position position);

			/** Access operator with a given position.
					@return Element the const Element with the position <b>  position </b>
			*/
			const Element& operator [] (Position position) const;

			//@}
			/**	@name	Application methods
			*/
			//@{
		
			/** Application of an unary processor on every contained element.
					@param  processor a typed unary processor for Element instances
					@return  bool - <tt>true</tt> if application has been terminated successfully,
													<tt>false</tt> otherwise
			*/
			static bool apply(UnaryProcessor<Element>& applicator);

			//@}

			///
			struct SymbolToElement
			{
				///
				const char* symbol;

				///
				Element* 		element;
			};

		private:

			///
			static Element element_[];

			///
			static SymbolToElement symbol_to_element_[];

			///
			static Element* atomic_number_to_element_[];
	};


	/**	Global static instance of the periodic table.
			\ingroup KernelMiscellaneous
	*/
	BALL_EXPORT extern PTE_ PTE;



#	ifndef BALL_NO_INLINE_FUNCTIONS
#		include <BALL/KERNEL/PTE.iC>
#	endif
} //namespace BALL 


#endif // BALL_KERNEL_PTE_H
