/*
 *
 *  Copyright (C) 2015-2019, Open Connections GmbH
 *  All rights reserved.  See COPYRIGHT file for details.
 *
 *  This software and supporting documentation are maintained by
 *
 *    OFFIS e.V.
 *    R&D Division Health
 *    Escherweg 2
 *    D-26121 Oldenburg, Germany
 *
 *
 *  Module: dcmiod
 *
 *  Author: Michael Onken
 *
 *  Purpose: Collection of classes representing DICOM IOD macros
 *
 */

#ifndef IODMACRO_H
#define IODMACRO_H

#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmdata/dctk.h" // For VR classes, i.e. DcmCodeString etc.
#include "dcmtk/dcmiod/iodrules.h"
#include "dcmtk/dcmiod/modbase.h"
#include "dcmtk/ofstd/ofstring.h"
#include "dcmtk/ofstd/ofvector.h"

/** Class representing a Code Sequence Macro
 */
class DCMTK_DCMIOD_EXPORT CodeSequenceMacro : public IODComponent
{

public:
    /** Constructor
     *  @param  item The item to be used for data storage. If NULL, the
     *          class creates an empty data container.
     *  @param  rules The rule set for this class. If NULL, the class creates
     *          one from scratch and adds its values.
     *  @param  parent The parent of the IOD component (NULL if none or unknown)
     */
    CodeSequenceMacro(OFshared_ptr<DcmItem> item, OFshared_ptr<IODRules> rules, IODComponent* parent = NULL);

    /** Constructor
     *  @param  parent The parent of the IOD component (NULL if none or unknown)
     */
    CodeSequenceMacro(IODComponent* parent = NULL);

    /** Copy Constructor, performs deep copy
     *  @param  rhs The parent of the IOD component (NULL if none or unknown)
     */
    CodeSequenceMacro(const CodeSequenceMacro& rhs);

    /** Convenience constructor to set initial values
     *  @param  item The item to be used for data storage. If NULL, the
     *          class creates an empty data container.
     *  @param  rules The rule set for this class. If NULL, the class creates
     *          one from scratch and adds its values.
     *  @param  parent The parent of the IOD component (NULL if none or unknown)
     *  @param  codeValue The code value
     *  @param  codingSchemeDesignator The coding scheme designator
     *  @param  codeMeaning The code meaning
     *  @param  codingSchemeVersion The coding scheme version (might be empty if
     *          coding scheme is unique)
     */
    CodeSequenceMacro(OFshared_ptr<DcmItem> item,
                      OFshared_ptr<IODRules> rules,
                      IODComponent* parent,
                      const OFString& codeValue,
                      const OFString& codingSchemeDesignator,
                      const OFString& codeMeaning,
                      const OFString& codingSchemeVersion = "");

    /** Convenience constructor to set initial values
     *  @param  codeValue The code value
     *  @param  codingSchemeDesignator The coding scheme designator
     *  @param  codeMeaning The code meaning
     *  @param  codingSchemeVersion The coding scheme version (might be empty if
     *          coding scheme is unique)
     *  @param  parent The parent of the IOD component (NULL if none or unknown)
     */
    CodeSequenceMacro(const OFString& codeValue,
                      const OFString& codingSchemeDesignator,
                      const OFString& codeMeaning,
                      const OFString& codingSchemeVersion = "",
                      IODComponent* parent                = NULL);

    /** Resets rules to their original values
     */
    virtual void resetRules();

    /** Get name of macro
     *  @return Name of the component ("CodeSequenceMacro")
     */
    virtual OFString getName() const;

    /** Virtual Destructor
     */
    virtual ~CodeSequenceMacro();

    /** Check whether this component's data satisfies the underlying
     *  rules. In this case, it is only checked that Designator, Value and
     *  Meaning of the code are set.
     *  @param  quiet If OFTrue, not error / warning messages will be produced. Only
     *                the returned error code will indicate error or OK. Per default,
     *                logging output is produced (OFFalse).
     *  @result EC_Normal if rules are satisfied, error otherwise
     */
    virtual OFCondition check(const OFBool quiet = OFFalse);

    /** Get Code Value, URL Code Value or Long Code Value
     *  @param  value Reference to variable in which the value should be stored
     *  @param  pos Index of the value to get (0..vm-1), -1 for all components
     *  @param  autoTag If OFTrue, this method will consider tags
     *                  Code Value (0008,0100)
     *                  URN Code Code Value (0008,0120) and
     *                  Long Code Value (0008,0119) and
     *                  in this order and will return once it finds that tag,
     *                  even if the related value is empty.
     *                  If OFFalse, getCodeValue() only returns the value
     *                  found in Code Value (0008,0100).
     *  @return EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition getCodeValue(OFString& value, const signed long pos = 0, const OFBool autoTag = OFTrue);

    /** Get URN Code Value
     *  @param  value Reference to variable in which the value should be stored
     *  @param  pos Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition getURNCodeValue(OFString& value, const signed long pos = 0);

    /** Get Long Code Value
     *  @param  value Reference to variable in which the value should be stored
     *  @param  pos Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition getLongCodeValue(OFString& value, const signed long pos = 0);

    /** Get Coding Scheme Designator
     *  @param  value Reference to variable in which the value should be stored
     *  @param  pos   Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition getCodingSchemeDesignator(OFString& value, const signed long pos = 0);

    /** Get Coding Scheme Version
     *  @param  value Reference to variable in which the value should be stored
     *  @param  pos Index of the value to get (0..vm-1), -1 for all components
     *  @return  EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition getCodingSchemeVersion(OFString& value, const signed long pos = 0);

    /** Get Code Meaning
     *  @param  value  Reference to variable in which the value should be stored
     *  @param  pos    Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition getCodeMeaning(OFString& value, const signed long pos = 0);

    /** Returns whether code is empty, i.e. no component of the Code Sequence Macro
     *  is set. This can be used in order to find out whether someone actually
     *  wanted to fill in a valid code as opposed of leaving it unset.
     *  @return OFTrue if no component of this class is set, OFFalse otherwise.
     */
    virtual OFBool empty();

    /** Set Code Value
     *  @param  value The value to set
     *  @param  checkValue If OFTrue, VM and VR of value are checked
     *  @param  autoTag If OFTrue, it is determined automatically if
     *          tag Code Value, URN Code Value or Long Code Value is used.
     *          If OFFalse, the classic Code Value is used.
     *          Default is OFTrue.
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition
    setCodeValue(const OFString& value, const OFBool checkValue = OFTrue, const OFBool autoTag = OFTrue);

    /** Set URN Code Value
     *  @param  value The value to set
     *  @param  checkValue If OFTrue, VM and VR of value are checked
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition setURNCodeValue(const OFString& value, const OFBool checkValue = OFTrue);

    /** Set Long Code Value
     *  @param  value The value to set
     *  @param  checkValue If OFTrue, VM and VR of value are checked
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition setLongCodeValue(const OFString& value, const OFBool checkValue = OFTrue);

    /** Set Coding Scheme Designator
     *  @param  value The value to set
     *  @param  checkValue If OFTrue, VM and VR of value are checked
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition setCodingSchemeDesignator(const OFString& value, const OFBool checkValue = OFTrue);

    /** Set Coding Scheme Version
     *  @param  value The value to set
     *  @param  checkValue If OFTrue, VM and VR of value are checked
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition setCodingSchemeVersion(const OFString& value, const OFBool checkValue = OFTrue);

    /** Set Code Meaning
     *  @param  value The value to set
     *  @param  checkValue If OFTrue, VM and VR of value are checked
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition setCodeMeaning(const OFString& value, const OFBool checkValue = OFTrue);

    /** Set all values in this class conveniently
     *  @param  value Code Value to set
     *  @param  scheme Coding Scheme Designator to set
     *  @param  meaning Code Meaning to set
     *  @param  schemeVersion The Coding Scheme Designator version to set
     *  (optional)
     *  @param  checkValue If OFTrue, VM and VR of values is checked
     *  @param  autoTag If OFTrue, it is determined automatically if
     *          tag Code Value, URN Code Value or Long Code Value is used.
     *          If OFFalse, the classic Code Value is used.
     *          Default is OFTrue.
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition set(const OFString& value,
                            const OFString& scheme,
                            const OFString& meaning,
                            const OFString& schemeVersion = "",
                            const OFBool checkValue       = OFTrue,
                            const OFBool autoTag          = OFTrue);

    /** Returns string representation reflecting the coded value.
     *  Mostly useful for debugging purposes.
     *  @return String representing the coded value
     */
    virtual OFString toString();

protected:
    /** Deletes all out of the the following list of tags:
     *  Code Value, Long Code Value and URN Code Value
     *  @param  keepTag The tag to be keep
     */
    void deleteUnusedCodeValues(const DcmTagKey& keepTag);
};

/** Code with Modifier(s). Represents the combination of a Code Sequence Macro
 *  that is amended by a Modifier Code Sequence with one or more items. The
 *  VM and requirement type of the Modifier Code Sequence can be configured, as
 *  well as the tag of the sequence holding the modifiers (default: Modifier
 *  Code Sequence). Overall, the class reads and write the following structure
 *  from an item:
 *
 *  Coding Scheme Designator, 1, 1C
 *  Coding Scheme Version, 1, 1C
 *  Code Value, 1, 1C
 *  Code Meaning, 1, 1C
 *  Modifier Code Sequence, VM, requirement type
 *  > Code Sequence Macro
 *
 *  Note that the Code Sequence Macro does not support Long Code Value and
 *  URN Code Value, so that Coding Scheme Designator and Code Value are
 *  handled as type 1 (and not 1C as denoted now in the standard and above).
 */
class DCMTK_DCMIOD_EXPORT CodeWithModifiers : public CodeSequenceMacro
{

public:
    /** Constructor
     *  @param  modifierType Denotes type of Modifier Code Sequence (i.e. 1, 1C,
     *          2, 2C or 3)
     *  @param  modifierVM Denotes how many items are allowed in the Modifier Code
     *          Sequence
     *  @param  modifierSeq Tag of the sequence that holds the modifier codes.
     *          The default is the Modifier Code Sequence.
     */
    CodeWithModifiers(const OFString& modifierType,
                      const OFString& modifierVM   = "1-n",
                      const DcmTagKey& modifierSeq = DCM_ModifierCodeSequence);

    /** Copy constructor, performs deep copy.
     *  @param  rhs The component to be copied from
     */
    CodeWithModifiers(const CodeWithModifiers& rhs);

    /** Assignment operator, performs deep copy.
     *  @param  rhs The component to be assigned from
     *  @return Reference to this object
     */
    CodeWithModifiers& operator=(const CodeWithModifiers& rhs);

    /** Virtual Destructor, frees memory
     */
    virtual ~CodeWithModifiers();

    /** Clear all attributes from the data that are handled by this component.
     *  An attribute is considered belonging to the module if there are rules
     *  marked as belonging to this module via the rule's module name.
     */
    void clearData();

    /** Resets rules to their original values
     */
    virtual void resetRules();

    /** Get rules handled by this module
     *  @return The rules
     */
    OFshared_ptr<IODRules> getRules()
    {
        return m_Rules;
    }

    /** Get name of component
     *  @return Name of the component
     */
    virtual OFString getName() const;

    /** Get modifier code denoted by index
     *  @param  index Index of modifier code to get (first modifier = 0)
     *  @return Code if modifier with index exists, NULL otherwise
     */
    virtual CodeSequenceMacro* getModifier(const size_t index = 0);

    /** Adds modifier code
     *  @param  modifier The code to be added
     *  @return EC_Normal if adding was successful, error otherwise
     */
    virtual OFCondition addModifier(const CodeSequenceMacro& modifier);

    /** Read attributes from given item into this class
     *  @param source  The source to read from
     *  @param clearOldData If OFTrue, old data is cleared before reading. Otherwise
     *         old data is overwritten (or amended)
     *  @result EC_Normal if reading was successful, error otherwise
     */
    virtual OFCondition read(DcmItem& source, const OFBool clearOldData = OFTrue);

    /** Write attributes from this class into given item
     *  @param  destination The item to write to
     *  @result EC_Normal if writing was successful, error otherwise
     */
    virtual OFCondition write(DcmItem& destination);

    /** Check whether this component's data satisfies the underlying
     *  rules
     *  @param  quiet If OFTrue, not error / warning messages will be produced. Only
     *                the returned error code will indicate error or OK. Per default,
     *                logging output is produced (OFFalse).
     *  @result EC_Normal if rules are satisfied, error otherwise
     */
    virtual OFCondition check(const OFBool quiet = OFFalse);

    /** Comparison operator for IOD Components
     *  @param  rhs The right hand side of the comparison
     *  @return 0, if the given object is equal to this object, other value otherwise
     */
    virtual int compare(const IODComponent& rhs) const;

private:
    /// Private undefined default constructor
    CodeWithModifiers();

    /// Items of Modifier Code Sequence
    OFVector<CodeSequenceMacro*> m_Modifiers;

    /// Type 1,2,3,1C or 2C
    OFString m_ModifierType;

    /// 1, 1-n, 2-2n, ...
    OFString m_ModifierVM;

    /// The sequence tag key that contains the modifier codes
    DcmTagKey m_CodeModifierSeq;
};

/** Class implementing the SOP Instance Reference Macro
 */
class DCMTK_DCMIOD_EXPORT SOPInstanceReferenceMacro : public IODComponent
{

public:
    /** Constructor
     *  @param  item The item to be used for data storage. If NULL, the
     *          class creates an empty data container.
     *  @param  rules The rule set for this class. If NULL, the class creates
     *          one from scratch and adds its values.
     *  @param  parent The parent of the IOD component (NULL if none or unknown)
     */
    SOPInstanceReferenceMacro(OFshared_ptr<DcmItem> item, OFshared_ptr<IODRules> rules, IODComponent* parent = NULL);

    /** Constructor
     *  @param  parent The parent of the IOD component (NULL if none or unknown)
     */
    SOPInstanceReferenceMacro(IODComponent* parent = NULL);

    /** Virtual Destructor
     */
    virtual ~SOPInstanceReferenceMacro();

    /** Resets rules to their original values
     */
    virtual void resetRules();

    /** Get name of macro
     *  @return Name of the module ("SOPInstanceReferenceMacro")
     */
    virtual OFString getName() const;

    /** Get Referenced SOP Class UID
     *  @param  value Reference to variable in which the value should be stored
     *  @param  pos   Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition getReferencedSOPClassUID(OFString& value, const signed long pos = 0);
    /** Get Referenced SOP Instance UID
     *  @param  value Reference to variable in which the value should be stored
     *  @param  pos   Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition getReferencedSOPInstanceUID(OFString& value, const signed long pos = 0);
    /** Set Referenced SOP Class UID
     *  @param  value The value to set
     *  @param  checkValue If OFTrue, the value is checked regarding VM and VR
     *  @return EC_Normal, if successful, error otherwise
     */
    virtual OFCondition setReferencedSOPClassUID(const OFString& value, const OFBool checkValue = OFTrue);
    /** Set Referenced SOP Instance UID
     *  @param  value The value to set
     *  @param  checkValue If OFTrue, the value is checked regarding VM and VR
     *  @return EC_Normal, if successful, error otherwise
     */
    virtual OFCondition setReferencedSOPInstanceUID(const OFString& value, const OFBool checkValue = OFTrue);
};

/** Class representing the Series and Instance Reference Macro
 */
class DCMTK_DCMIOD_EXPORT IODSeriesAndInstanceReferenceMacro : public IODComponent
{

public:
    // Forward declaration
    class ReferencedSeriesItem;

    /** Constructor
     *  @param  data The item to be used for data storage. If NULL, the
     *          class creates an empty data container.
     *  @param  rules The rule set for this class. If NULL, the class creates
     *          one from scratch and adds its values.
     *  @param  parent The parent of the IOD component (NULL if none or unknown)
     */
    IODSeriesAndInstanceReferenceMacro(OFshared_ptr<DcmItem> data,
                                       OFshared_ptr<IODRules> rules,
                                       IODComponent* parent = NULL);

    /** Constructor
     *  @param  parent The parent component of this class (if applicable, might
     *          be NULL)
     */
    IODSeriesAndInstanceReferenceMacro(IODComponent* parent = NULL);

    /** Virtual Destructor
     */
    virtual ~IODSeriesAndInstanceReferenceMacro();

    /** Read Series and Instance Reference Macro from given item
     *  @param  source The item to read from
     *  @param  clearOldData If OFTrue, old data is deleted before
     *          reading (default)
     *  @return EC_Normal if successful, error otherwise
     */
    virtual OFCondition read(DcmItem& source, const OFBool clearOldData = OFTrue);

    /** Write Series and Instance Reference Macro to given item
     *  @param  destination The item to write to
     *  @return EC_Normal if successful, error otherwise
     */
    virtual OFCondition write(DcmItem& destination);

    /** Get name of module ("SeriesAndInstanceReferenceMacro")
     *  @return Name of the module ("SeriesAndInstanceReferenceMacro")
     */
    virtual OFString getName() const;

    /** Resets rules to their original values
     */
    virtual void resetRules();

    /** Clear (removes) all attributes handled by the modules of this component.
     *  Rules are not reset.
     */
    virtual void clearData();

    /** Return reference to list of Referenced Series items
     *  @return Reference to list of Reference Series Items
     */
    OFVector<ReferencedSeriesItem*>& getReferencedSeriesItems();

private:
    /// Vector with all items of the Referenced Series Sequence
    OFVector<ReferencedSeriesItem*> m_ReferencedSeriesItems;

    /// Name of this component ("SeriesAndInstanceReferenceMacro")
    static const OFString m_ComponentName;
};

/** Class representing Items from the Referenced Series Sequence:
 *
 * [Referenced Series Sequence: (SQ, VM 1-n, Type 1C)]
 * > Series Instance UID: (UI, 1, 1)
 * > Referenced Instance Sequence: (SQ, 1-n, 1)
 * >> SOP Instance Reference Macro
 *
 */
class DCMTK_DCMIOD_EXPORT IODSeriesAndInstanceReferenceMacro::ReferencedSeriesItem : public IODComponent
{

public:
    /** Constructor
     *  @param  item The item to be used for data storage. If NULL, the
     *          class creates an empty data container.
     *  @param  rules The rule set for this class. If NULL, the class creates
     *          one from scratch and adds its values.
     *  @param  parent The parent of the IOD component (NULL if none or unknown)
     */
    ReferencedSeriesItem(OFshared_ptr<DcmItem> item, OFshared_ptr<IODRules> rules, IODComponent* parent = NULL);

    /** Constructor
     *  @param  parent The parent component of this class (if applicable, might
     *          be NULL)
     */
    ReferencedSeriesItem(IODComponent* parent = NULL);

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

    /** Clear (removes) all attributes handled by the modules of this component.
     *  Rules are not reset.
     */
    virtual void clearData();

    /** Read Referenced Series Sequence item data from given item
     *  @param  source The item to read from
     *  @param  clearOldData If OFTrue, old data is cleared before reading
     *  @return EC_Normal if data could be read successfully, error otherwise
     */
    virtual OFCondition read(DcmItem& source, const OFBool clearOldData = OFTrue);

    /** Write this Referenced Series Sequence item data to given item
     *  @param  destination The item to write to
     *  @return EC_Normal if successful, error otherwise
     */
    virtual OFCondition write(DcmItem& destination);

    /** Resets rules to their original values.
     */
    virtual void resetRules();

    /** Get name of module ("SeriesAndInstanceReferenceMacro")
     *  @return Name of the module ("SeriesAndInstanceReferenceMacro")
     */
    virtual OFString getName() const;

    /** Get Series Instance UID
     *  @param  value  Reference to variable in which the value should be stored
     *  @param  pos    Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition getSeriesInstanceUID(OFString& value, const signed long pos = 0) const;

    /** Set Series Instance UID
     *  @param  value Value to be set (single value only) or "" for no value
     *  @param  checkValue Check 'value' for conformance with VR and VM if enabled
     *  @return status EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition setSeriesInstanceUID(const OFString& value, const OFBool checkValue = OFTrue);

    virtual OFCondition addReference(const OFString& sopClassUID, const OFString& sopInstanceUID);

    /** Get content of the Referenced Instance Sequence
     *  @return Reference to the Referenced Instance Sequence content
     */
    virtual OFVector<SOPInstanceReferenceMacro*>& getReferencedInstanceItems();

private:
    /// The name of this component ("SeriesAndInstanceReferenceMacro")
    static const OFString m_ComponentName;

    /// Vector containing the data of all items of the Referenced Instance Sequence
    OFVector<SOPInstanceReferenceMacro*> m_ReferencedInstanceSequence;
};

/** Class representing the Image SOP Instance Reference Macro
 */
class DCMTK_DCMIOD_EXPORT ImageSOPInstanceReferenceMacro : public SOPInstanceReferenceMacro
{

public:
    /** Constructor
     */
    ImageSOPInstanceReferenceMacro();

    /** Virtual Destructor
     */
    virtual ~ImageSOPInstanceReferenceMacro();

    /** Creates an ImageSOPInstanceReferenceMacro object without frame/segment
     *  reference from required information.
     *  @param  sopClassUID The SOP Class UID of the reference
     *  @param  sopInstanceUID The SOP Instance UID of the reference
     *  @param  result Returns the resulting object if successful, NULL otherwise
     *  @return EC_Normal if creation was successful, error code otherwise
     */
    static OFCondition
    create(const OFString& sopClassUID, const OFString& sopInstanceUID, ImageSOPInstanceReferenceMacro*& result);

    /** Creates an ImageSOPInstanceReferenceMacro object with frame or segment
     *  references from required information.
     *  @param  sopClassUID The SOP Class UID of the reference
     *  @param  sopInstanceUID The SOP Instance UID of the reference
     *  @param  refFramesOrSegments Reference to specific frames of an image or
     *          segments of a Segmentation object. The decision (image or
     *          segmentation) is based on the SOP Class; in case it is the
     *          Segmentation Storage SOP Class, the parameter is interpreted
     *          as segment references, otherwise as frame references. If this
     *          parameter is provided empty, then no frame/segment reference is
     *          set at all.
     *  @param  result Returns the resulting object if successful, NULL otherwise
     *  @return EC_Normal if creation was successful, error code otherwise
     */
    static OFCondition create(const OFString& sopClassUID,
                              const OFString& sopInstanceUID,
                              const OFVector<Uint16>& refFramesOrSegments,
                              ImageSOPInstanceReferenceMacro*& result);

    /** Comparison operator that compares the normalized value of this object
     *  with a given object of the same type
     *  @param  rhs The right hand side of the comparison
     *  @return 0 If the object values are equal.
     *          -1 if either the value of the first component that does not match
     *          is lower in the rhs object, or all compared components match
     *          but the rhs component is shorter. Also returned if rhs cannot be
     *          casted to DcmAttributeTag.
     *          1 if either the value of the first component that does not match
     *          is greater in the rhs object, or all compared components match
     *          but the rhs component is longer.
     */
    virtual int compare(const IODComponent& rhs) const;

    /** Clear data
     */
    virtual void clear();

    /** Read Image SOP Instance Reference Macro from given item
     *  @param  source The item to read from
     *  @param  clearOldData If OFTrue (default), old data is cleared before reading
     *  @return EC_Normal if successful, error otherwise
     */
    virtual OFCondition read(DcmItem& source, const OFBool clearOldData = OFTrue);

    /** Write Image SOP Instance Reference Macrom to given item
     *  @param  item The item to write to
     *  @return EC_Normal if successful, error otherwise
     */
    virtual OFCondition write(DcmItem& item);

    /** Get Referenced Frame Number
     *  @param  values Reference to variable in which the value should be stored
     *  @return EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition getReferencedFrameNumber(OFVector<Uint16>& values);

    /** Get Referenced Segment Number
     *  @param  values Reference to variable in which the value should be stored
     *  @return EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition getReferencedSegmentNumber(OFVector<Uint16>& values);

    /** Set Referenced Frame Number
     *  @param  values The frame numbers that shoule be referenced
     *  @param  checkValue  If OFTrue (default) the given values will be checked
     *  @return EC_Normal if successful, error otherwise
     */
    virtual OFCondition setReferencedFrameNumber(const OFVector<Uint16>& values, const OFBool checkValue = OFTrue);
    /** Add a Referenced Frame Number
     *  @param  value The frame number to add
     *  @param  checkValue If OFTrue, consistency checks are performed (as possible)
     *  @return EC_Normal if successful, error otherwise
     */
    virtual OFCondition addReferencedFrameNumber(const Uint16& value, const OFBool checkValue = OFTrue);
    /** Set the Referenced Segment Numbers
     *  @param  values The segment numbers to add
     *  @param  checkValue  If OFTrue, consistency checks are performed (as possible)
     *  @return EC_Normal if successful, error otherwise
     */
    virtual OFCondition setReferencedSegmentNumber(const OFVector<Uint16>& values, const OFBool checkValue = OFTrue);

    /** Add a Referenced Segment Number
     *  @param  value The segment number to add
     *  @param  checkValue If OFTrue, consistency checks are performed (as possible)
     *  @return EC_Normal if successful, error otherwise
     */
    virtual OFCondition addReferencedSegmentNumber(const Uint16& value, const OFBool checkValue = OFTrue);

private:
    // DICOM attributes.
    // The comments for each attribute describe "Name: (VR, VM, Type)".
    // See DICOM standard for further reference.

    /// Referenced Frame Number: (IS, 1-n, 1C)
    DcmIntegerString ReferencedFrameNumber;

    /// Referenced Segment Number: (US, 1-n, 1C)
    DcmUnsignedShort ReferencedSegmentNumber;
};

/** Class representing the Primary Anatomic Structure Macro
 */
typedef CodeWithModifiers PrimaryAnatomicStructureMacro;

/** Class representing the General Anatomy Mandatory or Optional Macro
 */
class DCMTK_DCMIOD_EXPORT GeneralAnatomyMacro
{

public:
    /** Constructor
     *  @param type Type of Anatomic Region Sequence. Permitted values: If 1,
     * the class represents the "General Anatomy Mandatory Macro", if
     * type 2 then it behaves like the "General Anatomy Required Macro",
     * and type 3 like the "General Anatomy Optional Macro". Other values
     * are interpreted as type 3 (optional).
     */
    GeneralAnatomyMacro(const OFString& type);

    /** Copy constructor, creates deep copy.
     *  @param  rhs The macro to copy from
     */
    GeneralAnatomyMacro(const GeneralAnatomyMacro& rhs);

    /** Virtual destructor
     */
    virtual ~GeneralAnatomyMacro();

    /** Clear (removes) all attributes handled by the modules of this component.
     */
    virtual void clearData();

    /** Check whether this component's data satisfies the underlying
     *  rules
     *  @param  quiet If OFTrue, not error / warning messages will be produced.
     *          Only the returned error code will indicate error or OK. Per
     *          default, logging output is produced (OFFalse).
     *  @result EC_Normal if rules are satisfied, error otherwise
     */
    virtual OFCondition check(const OFBool quiet = OFFalse);

    /** Return Anatomic Region
     *  @return Reference to Anatomic Region Code
     */
    virtual CodeSequenceMacro& getAnatomicRegion();

    /** Return Anatomic Region Modifier Codes
     *  @return Reference to Anatomic Region Modifier codes
     */
    virtual OFVector<CodeSequenceMacro*>& getAnatomicRegionModifier();

    /** Return anatomic structure
     *  @return Reference to anatomic structure macro
     */
    virtual PrimaryAnatomicStructureMacro& getPrimaryAnatomicStructure();

    /** Reads Anatomic Region Sequence and Primary Anatomic Structure Macro from
     *  given item
     *  @param  source The item to read from
     *  @param  clearOldData If OFTrue, old data is cleared before reading,
     *          otherwise it is kept/overwritten.
     *  @return EC_Normal if no error, error otherwise
     */
    virtual OFCondition read(DcmItem& source, const OFBool clearOldData = OFTrue);

    /** Write Anatomic Region Sequence and Primary Anatomic Structure Macro to
     *  given item
     *  @param  item The item to write to
     *  @return EC_Normal if writing was successful, error otherwise
     */
    virtual OFCondition write(DcmItem& item);

    /** Assignment operator (performs deep copy)
     *  @param  rhs The macro to copy from
     *  @return Reference to "this" object
     */
    GeneralAnatomyMacro& operator=(const GeneralAnatomyMacro& rhs);

    /** Comparison operator that compares the normalized value of this object
     *  with a given object of the same type, i.e.\ the elements within both
     *  objects (this and rhs parameter) are compared by value.
     *  @param  rhs the right hand side of the comparison
     *  @return 0 if the object values are equal.
     *          -1 if either the value of the first component that does not match
     *          is lower in the rhs object, or all compared components match
     *          but the rhs component is shorter.
     *          1 if either the value of the first component that does not match
     *          is greater in the rhs object, or all compared components match
     *          but the rhs component is longer.
     */
    virtual int compare(const GeneralAnatomyMacro& rhs) const;

private:
    /// Type (1,2,3) of Anatomic Region Sequence.
    OFString m_Type;

    /// Anatomic Region Sequence (SQ, 1, 1) (Code Sequence Macro within item of
    /// Anatomic Region Sequence))
    CodeSequenceMacro m_AnatomicRegion;

    /// Anatomic Region Modifier Macro (within item of Anatomic Region Sequence)
    OFVector<CodeSequenceMacro*> m_AnatomicRegionModifier;

    /// Primary Anatomic Structure Macro (on the same level as Anatomic
    /// Region Sequence)
    PrimaryAnatomicStructureMacro m_PrimaryAnatomicStructure;
};

/** Class representing the Algorithm Identification Macro
 */
class DCMTK_DCMIOD_EXPORT AlgorithmIdentificationMacro
{

public:
    /** Constructor
     */
    AlgorithmIdentificationMacro();

    /** Virtual destructor
     */
    virtual ~AlgorithmIdentificationMacro();

    /** Clear (removes) all attributes handled by the modules of this component.
     */
    virtual void clearData();

    /** Perform consistency checks
     *  @param  quiet If OFTrue, not error / warning messages will be produced.
     *          Only the returned error code will indicate error or OK. Per
     *          default, logging output is produced (OFFalse).
     *  @result EC_Normal if rules are satisfied, error otherwise
     */
    virtual OFCondition check(const OFBool quiet = OFFalse);

    /** Return Algorithm Family Code
     *  @return Reference to algorithm family code
     */
    virtual CodeSequenceMacro& getAlgorithmFamilyCode();

    /** Get Algorithm Name Code
     *  @return Algorithm Name Code
     */
    virtual CodeSequenceMacro& getAlgorithmNameCode();

    /** Get Algorithm Name
     *  @param  value Reference variable to store the value to
     *  @param  pos Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if value could be get, error otherwise
     */
    virtual OFCondition getAlgorithmName(OFString& value, const signed long pos = 0);

    /** Get Algorithm Version
     *  @param  value Reference variable to store the value to
     *  @param  pos Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if value could be get, error otherwise
     */
    virtual OFCondition getAlgorithmVersion(OFString& value, const signed long pos = 0);

    /** Get Algorithm Parameters
     *  @param  value Reference variable to store the value to
     *  @param  pos Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if value could be get, error otherwise
     */
    virtual OFCondition getAlgorithmParameters(OFString& value, const signed long pos = 0);
    /** Get Algorithm Source
     *  @param  value Reference variable to store the value to
     *  @param  pos Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if value could be get, error otherwise
     */
    virtual OFCondition getAlgorithmSource(OFString& value, const signed long pos = 0);

    /** Set Algorithm Name
     *  @param  value The value to be set
     *  @param  checkValue If OFTrue, the given value is checked
     *  @return EC_Normal, if value could be set, error otherwise
     */
    virtual OFCondition setAlgorithmName(const OFString& value, const OFBool checkValue = OFTrue);

    /** Set Algorithm Version
     *  @param  value The value to be set
     *  @param  checkValue If OFTrue, the given value is checked
     *  @return EC_Normal, if value could be set, error otherwise
     */
    virtual OFCondition setAlgorithmVersion(const OFString& value, const OFBool checkValue = OFTrue);

    /** Set Algorithm Parameters
     *  @param  value The value to be set
     *  @param  checkValue Check 'value'. Not evaluated (here for consistency
     *          with other setter functions).
     *  @return EC_Normal, if value could be set, error otherwise
     */
    virtual OFCondition setAlgorithmParameters(const OFString& value, const OFBool checkValue = OFTrue);

    /** Set Algorithm Source
     *  @param  value The value to be set
     *  @param  checkValue If OFTrue, the given value is checked
     *  @return EC_Normal, if value could be set, error otherwise
     */
    virtual OFCondition setAlgorithmSource(const OFString& value, const OFBool checkValue = OFTrue);

    /** Reads this macro from given item
     *  @param  source The item to read from
     *  @param  clearOldData If OFTrue, old data is cleared before reading,
     *          otherwise it is kept/overwritten
     *  @return EC_Normal if reading was successful, error otherwise
     */
    virtual OFCondition read(DcmItem& source, const OFBool clearOldData = OFTrue);

    /** Write this macro to given item
     *  @param  item The item to write to
     *  @return EC_Normal if writing was successful, error otherwise
     */
    virtual OFCondition write(DcmItem& item);

private:
    /// Code Sequence Macro from Algorithm Family Code Sequence (single item),
    /// (SQ, VM 1, Type 1)
    CodeSequenceMacro m_AlgorithmFamilyCode;

    /// Code Sequence Macro from Algorithm Name Code Sequence (single item)
    /// (SQ, VM 1, Type 3)
    CodeSequenceMacro m_AlgorithmNameCode;

    /// Algorithm Name: (LO, VM 1, Typ 1)
    DcmLongString m_AlgorithmName;

    /// Algorithm Version: (LO, VM 1, Typ 1)
    DcmLongString m_AlgorithmVersion;

    /// Algorithm Parameters: (LT, VM 1, Typ 3)
    DcmLongText m_AlgorithmParameters;

    /// Algorithm Source: (LO, VM 1, Typ 3)
    DcmLongString m_AlgorithmSource;
};

/** Content Identification Macro
 */
class DCMTK_DCMIOD_EXPORT ContentIdentificationMacro
{

public:
    /** Class representing an Alternate Content Description item
     */
    class DCMTK_DCMIOD_EXPORT AlternateContentDescriptionItem
    {
    public:
        /** Constructor
         */
        AlternateContentDescriptionItem();

        /** Virtual destructor
         */
        virtual ~AlternateContentDescriptionItem();

        /** Clear (removes) all attributes handled by the modules of this component.
         */
        virtual void clearData();

        /** Get Content Description
         *  @param  value Reference variable to store the value to
         *  @param  pos Index of the value to get (0..vm-1), -1 for all components
         *  @return EC_Normal if successful, error otherwise
         */
        virtual OFCondition getContentDescription(OFString& value, const signed long pos = 0);
        /** Get Language Code
         *  @return Reference to the language code
         */
        virtual CodeSequenceMacro& getLanguageCode();

        /** Set Content Description
         *  @param  value The value to set
         *  @param  checkValue If OFTrue, consistency check is performed
         *  @result EC_Normal if setting was successful, error otherwise
         */
        virtual OFCondition setContentDescription(const OFString& value, const OFBool checkValue = OFTrue);

        /** Read Alternate Content Description Sequence item from given item
         *  @param  source The item to read from
         *  @param  clearOldData If OFTrue, old data is cleared before reading,
         *          otherwise it is kept/overwritten
         *  @result EC_Normal, if reading was successful, error otherwise
         */
        virtual OFCondition read(DcmItem& source, const OFBool clearOldData = OFTrue);

        /** Write alternate content description item to given item
         *  @param  item The item to write to
         *  @result EC_Normal, if writing was successful, error otherwise
         */
        virtual OFCondition write(DcmItem& item);

    private:
        /// Content Description: (LO, VM 1, Type 1)
        DcmLongString m_ContentDescription;

        /// Item of Language Code Sequence: (SQ, VM 1, Type 1)
        CodeSequenceMacro m_LanguageCode;
    };

    /** Constructor
     */
    ContentIdentificationMacro();

    /** Constructor initializing basic data
     *  @param  instanceNumber Instance Number
     *  @param  contentLabel Content Label
     *  @param  contentDescription Content Description, may be empty
     *  @param  contentCreatorName Content Creator's Name, may be empty
     */
    ContentIdentificationMacro(const OFString& instanceNumber,
                               const OFString& contentLabel,
                               const OFString& contentDescription,
                               const OFString& contentCreatorName);

    /** Copy constructor
     *  @param  rhs The macro to copy from (deep copy)
     */
    ContentIdentificationMacro(const ContentIdentificationMacro& rhs);

    /** Create Content Identification Macro with minimally required data.
     *  @param  instanceNumber Instance Number
     *  @param  contentLabel Content Label
     *  @param  contentDescription Content Description, may be empty
     *  @param  contentCreatorName Content Creator's Name, may be empty
     *  @param  result Returns created macro if successful, NULL otherwise
     *  @return EC_Normal if creation was successful, error otherwise
     */
    static OFCondition create(const OFString& instanceNumber,
                              const OFString& contentLabel,
                              const OFString& contentDescription,
                              const OFString& contentCreatorName,
                              ContentIdentificationMacro*& result);

    /** Assignment operator (deep copy)
     *  @param  rhs The macro to copy from
     *  @return Reference to "this" object
     */
    ContentIdentificationMacro& operator=(const ContentIdentificationMacro& rhs);

    /** Virtual destructor
     */
    virtual ~ContentIdentificationMacro();

    /** Get rules for this data structure
     *  @return Reference to the rules
     */
    virtual IODRules& getIODRules();

    /** Clear (removes) all attributes handled by the modules of this component.
     */
    virtual void clearData();

    /** Resets rules to their original values
     */
    virtual void resetRules();

    /** Get name of macro
     *  @return Name of the module ("ContentIdentificationMacro")
     */
    virtual OFString getName() const;

    /** Check whether this component's data satisfies the underlying
     *  rules
     *  @param  quiet If OFTrue, not error / warning messages will be produced.
     *                Only the returned error code will indicate error or OK. Per
     *                default, logging output is produced (OFFalse).
     *  @result EC_Normal if rules are satisfied, error otherwise
     */
    virtual OFCondition check(const OFBool quiet = OFFalse);

    /** Get Instance Number
     *  @param  value Variable to store the result to
     *  @param  pos Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if value could be get, error otherwise
     */
    virtual OFCondition getInstanceNumber(OFString& value, const signed long pos = 0) const;

    /** Get Content Label
     *  @param  value Variable to store the result to
     *  @param  pos Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if value could be get, error otherwise
     */
    virtual OFCondition getContentLabel(OFString& value, const signed long pos = 0) const;

    /** Get Content Description
     *  @param  value Variable to store the result to
     *  @param  pos Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if value could be get, error otherwise
     */
    virtual OFCondition getContentDescription(OFString& value, const signed long pos = 0) const;

    /** Get Alternate Content Description items
     *  @return Reference to the items
     */
    virtual OFVector<AlternateContentDescriptionItem*>& getAlternateContentDescription();

    /** Get Content Creator Name
     *  @param  value Variable to store the result to
     *  @param  pos Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if value could be get, error otherwise
     */
    virtual OFCondition getContentCreatorName(OFString& value, const signed long pos = 0) const;

    /** Get Content Creator Identification Code
     *  @return Reference to code
     */
    virtual CodeSequenceMacro& getContentCreatorIdentificationCode();

    /** Set Instance Number
     *  @param  value Value to be set
     *  @param  checkValue If OFTrue, value is checked for validity
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition setInstanceNumber(const OFString& value, const OFBool checkValue = OFTrue);

    /** Set Content Label
     *  @param  value Value to be set
     *  @param  checkValue If OFTrue, value is checked for validity
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition setContentLabel(const OFString& value, const OFBool checkValue = OFTrue);

    /** Set Content Description
     *  @param  value Value to be set
     *  @param  checkValue If OFTrue, value is checked for validity
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition setContentDescription(const OFString& value, const OFBool checkValue = OFTrue);

    /** Set Content Creator Name
     *  @param  value Value to be set
     *  @param  checkValue If OFTrue, value is checked for validity
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition setContentCreatorName(const OFString& value, const OFBool checkValue = OFTrue);

    /** Reads Content Identification Macro from given item
     *  @param  source The item to read from
     *  @param  clearOldData If OFTrue, old data is cleared before reading, otherwise
     *          it is kept/overwritten
     *  @return EC_Normal if reading was successful, an error code otherwise
     */
    virtual OFCondition read(DcmItem& source, const OFBool clearOldData = OFTrue);

    /** Write Content Identification Macro to given item
     *  @param  item The item to write to
     *  @return EC_Normal if successful, error otherwise
     */
    virtual OFCondition write(DcmItem& item);

private:
    // Instance Number: (IS, VM 1, Type 1)
    DcmIntegerString m_InstanceNumber;

    /// Content Label: (CS, VM 1, Type 1)
    DcmCodeString m_ContentLabel;

    /// Content Description: (LO, VM 1, Type 1)
    DcmLongString m_ContentDescription;

    // Alternate Content Description Sequence (VM 1-n, Type 3)
    OFVector<AlternateContentDescriptionItem*> m_AlternateContentDescription;

    /// Content Creator's Name: (LO, VM 1, Type 2)
    DcmPersonName m_ContentCreatorName;

    /// Content Creator's Identification Code Sequence
    CodeSequenceMacro m_ContentCreatorIdentificationCode;

    /// IOD rules for this data structure
    IODRules m_IODRules;
};

/** Class representing the HL7 V2 Hierarchic Designator Macro
 */
class DCMTK_DCMIOD_EXPORT HL7HierarchicDesignatorMacro : public IODComponent
{

public:
    /** Constructor
     *  @param  item The item to be used for data storage. If NULL, the
     *          class creates an empty data container.
     *  @param  rules The rule set for this class. If NULL, the class creates
     *          one from scratch and adds its values.
     *  @param  parent The parent of the IOD component (NULL if none or unknown)
     */
    HL7HierarchicDesignatorMacro(OFshared_ptr<DcmItem> item, OFshared_ptr<IODRules> rules, IODComponent* parent = NULL);

    /** Constructor
     *  @param  parent The parent of the IOD component (NULL if none or unknown)
     */
    HL7HierarchicDesignatorMacro(IODComponent* parent = NULL);

    /** Resets rules to their original values
     */
    virtual void resetRules();

    /** Get name of macro
     *  @return Name of the module ("HL7HierarchicDesignatorMacro")
     */
    virtual OFString getName() const;

    /** Get Local Namespace Entity ID
     *  @param  value Variable to store the result to
     *  @param  pos Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if value could be get, error otherwise
     */
    virtual OFCondition getLocalNamespaceEntityID(OFString& value, const signed long pos = 0) const;

    /** Get Universal Entity ID
     *  @param  value Variable to store the result to
     *  @param  pos Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if value could be get, error otherwise
     */
    virtual OFCondition getUniversalEntityID(OFString& value, const signed long pos = 0) const;

    /** Get Universal Entity ID Type
     *  @param  value Variable to store the result to
     *  @param  pos Index of the value to get (0..vm-1), -1 for all components
     *  @return EC_Normal if value could be get, error otherwise
     */
    virtual OFCondition getUniversalEntityIDType(OFString& value, const signed long pos = 0) const;

    /** Set Local Namespace Entity ID
     *  @param  value Value to be set
     *  @param  checkValue Check 'value'. Not evaluated (here for consistency
     *          with other setter functions).
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition setLocalNamespaceEntityID(const OFString& value, const OFBool checkValue = OFTrue);

    /** Set Universal Entity ID
     *  @param  value Value to be set
     *  @param  checkValue Check 'value'. Not evaluated (here for consistency
     *          with other setter functions).
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition setUniversalEntityID(const OFString& value, const OFBool checkValue = OFTrue);

    /** Set Universal Entity ID Type
     *  @param  value Value to be set
     *  @param  checkValue If OFTrue, value is checked for validity
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition setUniversalEntityIDType(const OFString& value, const OFBool checkValue = OFTrue);
};

/** Class representing the Mandatory View and Slice Progression Direction Macro
 */

class DCMTK_DCMIOD_EXPORT MandatoryViewAndSliceProgressionDirectionMacro : public IODComponent
{

public:
    /** Constructor
     *  @param  item The item to be used for data storage. If NULL, the
     *  class creates an empty data container.
     *  @param  rules The rule set where this classes rules are added to. If NULL,
     *          the class creates an empty rule set.
     *  @param  parent The parent of the IOD component (NULL if none or unknown)
     */
    MandatoryViewAndSliceProgressionDirectionMacro(OFshared_ptr<DcmItem> item,
                                                   OFshared_ptr<IODRules> rules,
                                                   IODComponent* parent = NULL);

    /** Constructor
     *  @param  parent The parent of the IOD component (NULL if none or unknown)
     */
    MandatoryViewAndSliceProgressionDirectionMacro(IODComponent* parent = NULL);

    /** Clear (removes) all attributes handled by the modules of this component.
     */
    virtual void clearData();

    /** Resets rules to their original values
     */
    virtual void resetRules();

    /** Get name of macro
     *  @return Name of the module ("MandatoryViewAndSliceProgressionDirectionMacro")
     */
    virtual OFString getName() const;

    /** Read Mandatory View and Slice Progression Direction Macro from given item
     *  @param  source The item to read from
     *  @param  clearOldData If OFTrue (default), old data is cleared
     *  before reading
     *  @return EC_Normal if successful, error otherwise
     */
    virtual OFCondition read(DcmItem& source, const OFBool clearOldData = OFTrue);

    /** Write Mandatory View and Slice Progression Direction Macro to given item
     *  @param  item The item to write to
     *  @return EC_Normal if successful, error otherwise
     */
    virtual OFCondition write(DcmItem& item);

    /** Get View Code
     *  @return Reference to code
     */
    virtual CodeSequenceMacro& getViewCode();

    /** Get View Modifier Code
     *  @return Reference to code
     */
    virtual OFVector<CodeSequenceMacro*>& getViewModifierCode();

protected:
    /// View Code Sequence (SQ, VM 1, type 1)
    CodeSequenceMacro m_ViewCodeSequence;

    /// View Modifier Code Sequence (SQ, VM 1-n, type 2C)
    OFVector<CodeSequenceMacro*> m_ViewModifierCode;
};

#endif // IODMACRO_H
