/*
 *
 *  Copyright (C) 2015-2025, 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:  dcmseg
 *
 *  Author:  Michael Onken
 *
 *  Purpose: Class representing a Segment from the Segment Ident. Sequence
 *
 */

#ifndef SEGMENT_H
#define SEGMENT_H

#include "dcmtk/config/osconfig.h" // include OS configuration first

#include "dcmtk/dcmdata/dcvrus.h"
#include "dcmtk/dcmiod/iodmacro.h"
#include "dcmtk/dcmseg/segtypes.h"
#include "dcmtk/dcmdata/dcvrui.h"
#include "dcmtk/dcmdata/dcvrlo.h"
#include "dcmtk/dcmdata/dcvrut.h"

class DcmSegmentation;

/** Class that represents a Segment inside a Segmentation object.
 *  It mostly represents data as found in an item of the Segment Identification
 *  Sequence (Segmentation Image Module).
 */
class DCMTK_DCMSEG_EXPORT DcmSegment
{

public:
    // -------------- constructors/destructors --------------------

    // constructor is protected

    /** Destructor, frees memory
     */
    virtual ~DcmSegment();

    /** Clears all data
     */
    virtual void clearData();

    // -------------- creation --------------------

    /** Factory method to create a Segment that expects the minimal parameters
     *  required.
     *  @param  segment Pointer to the resulting segment if creation was
     *          successful; memory is allocated by the function
     *  @param  segmentLabel Free text label for the segment
     *  @param  segmentedPropertyCategory The segmented property category.
     *          Baseline CID 7150 "Segmentation Property Categories" should be
     *          used.
     *  @param  segmentedPropertyType The segmented property type. Baseline CID
     *          7151 "Segmentation Property Types"
     *  @param  algoType The algorithm type used for segmenting this segment
     *  @param  algoName Algorithm name (required if algoType is not MANUAL)
     *  @return EC_Normal if creation was successful, error otherwise
     */
    static OFCondition create(DcmSegment*& segment,
                              const OFString& segmentLabel,
                              const CodeSequenceMacro& segmentedPropertyCategory,
                              const CodeSequenceMacro& segmentedPropertyType,
                              const DcmSegTypes::E_SegmentAlgoType algoType,
                              const OFString& algoName = "");

    /** Make a clone of this segment.
     *  Note that the reference to DcmSegmentation is copied, if not provided in the parameter.
     *  @param seg Pointer to the DcmSegmentation object to associate with the cloned segment,
     *         copied from the original segment if not provided
     *  @return Pointer to the cloned segment if successful, OFnullptr otherwise
     */
    DcmSegment* clone(DcmSegmentation* seg = NULL);

    /** Assignment operator, performs deep copy
     *  @param  rhs The right-hand side segment to assign from
     *  @return Reference to this segment
     */
    DcmSegment& operator=(const DcmSegment& rhs);

    /** Copy constructor
     *  @param  rhs The right-hand side segment to copy from
     */
    DcmSegment(const DcmSegment& rhs);

    // ---------------- writing --------------------

    /** Write segment to given item which is usually contained within
     *  within the Segment Sequence
     *  @param  item The item to write to
     *  @return EC_Normal if successful, error otherwise
     */
    OFCondition write(DcmItem& item);

    // ---------------- reading --------------------

    /** Read segment from given item
     *  @param  item The item to read from (must be item as found in the
     *          Segment Identification Sequence)
     *  @param  clearOldData If true, old data is deleted first
     *  @return  EC_Normal if reading was successful, error otherwise
     */
    OFCondition read(DcmItem& item, const OFBool clearOldData = OFTrue);

    // ---------------- access --------------------

    /** Get Segment Number
     *  @return The Segment number
     */
    virtual Uint16 getSegmentNumber();

    /** Get Segment Label
     *  @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 getSegmentLabel(OFString& value, const signed long pos = 0);

    /** Get Segment Description
     *  @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 getSegmentDescription(OFString& value, const signed long pos = 0);

    /** Get the Segment Algorithm Type.
     *  @return The Algorithm Type
     */
    virtual DcmSegTypes::E_SegmentAlgoType getSegmentAlgorithmType();

    /** Get the Segment Algorithm Name
     *  @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 getSegmentAlgorithmName(OFString& value, const signed long pos = 0);

    /** Get General Anatomy Code
     *  @return Reference to the General Anatomy Macro, may be unset
     */
    virtual GeneralAnatomyMacro& getGeneralAnatomyCode();

    /** Get Segmented Property Category Code
     *  @return Reference to the Segmented Property Category Code, may be unset
     */
    virtual CodeSequenceMacro& getSegmentedPropertyCategoryCode();

    /** Get Segmented Property Type  Code
     *  @return Reference to the Segmented Property Type Code, may be unset
     */
    virtual CodeSequenceMacro& getSegmentedPropertyTypeCode();

    /** Get Segmented Property Type Modifier Code
     *  @return Reference to the Segmented Property Type Modifier Code, may
     *  be unset
     */
    virtual OFVector<CodeSequenceMacro*>& getSegmentedPropertyTypeModifierCode();

    /** Get Segmentation Algorithm Identification
     *  @warning This method has earlier been named getSegmentSurfaceGenerationAlgorithmIdentification()
     *           which has changed due to DICOM CP-1597. The code value returned is now
     *           taken from the "Segmentation Algorithm Identification Sequence" instead of the
     *           "Segment Surface Generation Algorithm Identification Sequence".
     *  @return Reference to the Segmentation Algorithm Identification
     *  Identification, may be unset
     */
    virtual AlgorithmIdentificationMacro& getSegmentationAlgorithmIdentification();

    /** Get Recommended Display Grayscale Value
     *  @param  value Reference to variable in which the value should be stored
     *  @param  pos Index of the value to get (0..vm-1)
     *  @return EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition getRecommendedDisplayGrayscaleValue(Uint16& value, const unsigned long pos = 0);

    /** Returns Recommended Display CIELab Value
     *  @param  L The L component
     *  @param  a The a* component
     *  @param  b The b* component
     *  @return EC_Normal if values could be returned
     */
    virtual OFCondition getRecommendedDisplayCIELabValue(Uint16& L, Uint16& a, Uint16& b);

    /** Get Tracking ID
     *  @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 getTrackingID(OFString& value, const signed long pos = 0);

    /** Get Tracking 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 getTrackingUID(OFString& value, const signed long pos = 0);

    // -------------- setters --------------------

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

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

    /** Set Segment Algorithm
     *  @param  algoType Algorithm type used to find segment
     *  @param  algoName Name of the algorithm used (required if algorithm type
     *          is not MANUAL
     *  @param  checkValue If OFTrue, input is checked for validity
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition setSegmentAlgorithm(const DcmSegTypes::E_SegmentAlgoType algoType,
                                            const OFString& algoName,
                                            const OFBool checkValue = OFTrue);

    /** Set Segmentation Algorithm Identification
     *  @warning This method has earlier been named setSegmentSurfaceGenerationAlgorithmIdentification()
     *           which has changed due to DICOM CP-1597. The resulting code value is now
     *           written to the "Segmentation Algorithm Identification Sequence" instead of the
     *           "Segment Surface Generation Algorithm Identification Sequence".
     *  @param  value The algorithm identification
     *  @param  checkValue If OFTrue, value undergoes some validity checks
     *  @return EC_Normal if setting was successful, error otherwise
     */
    virtual OFCondition setSegmentationAlgorithmIdentification(const AlgorithmIdentificationMacro& value,
                                                               const OFBool checkValue = OFTrue);

    /** Set Recommended Display Grayscale Value
     *  @param  value Value to be set (single value only) or "" for no value
     *  @param  checkValue Check 'value' for validity if enabled
     *  @return EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition setRecommendedDisplayGrayscaleValue(const Uint16 value, const OFBool checkValue = OFTrue);

    /** Set Recommended Display CIELab Value
     *  @param  L L component
     *  @param  a a component
     *  @param  b b component
     *  @param  checkValue Check 'value' for validity if enabled
     *  @return EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition
    setRecommendedDisplayCIELabValue(const Uint16 L, const Uint16 a, const Uint16 b, const OFBool checkValue = OFTrue);

    /** Set Tracking ID
     *  @param  value Value to be set (single value only) or "" for no value
     *  @param  checkValue Check 'value'. Not evaluated (here for consistency
     *          with other setter functions).
     *  @return EC_Normal if successful, an error code otherwise
     */
    virtual OFCondition setTrackingID(const OFString& value, const OFBool checkValue = OFTrue);

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

    /** THIS IS ONLY FOR INTERNAL USE. DO NOT USE this as a regular API user.
     *  Get Segment Number as read from the Segment Sequence for this segment.
     *  It may be different from the Segment Number as returned by getSegmentNumber(),
     *  i.e. do not rely on this method for anything.
     *  @return EC_Normal if successful, an error code otherwise
     */
    virtual Uint16 getSegmentNumberRead();

    OFshared_ptr<IODRules> getIODRules();

    /// The utility class must access the protected default constructor
    friend class DcmIODUtil;

protected:
    /** Protected default constructor
     */
    DcmSegment();

    /** Initialize IOD rules for this component
     */
    virtual void initIODRules();

    /** Set reference to the Segmentation object that this segment belongs to.
     *  This is used to find the Segment Number this Segment has within the
     *  Segmentation.
     *  @param  doc Pointer to the Segmentation object
     */
    void referenceSegmentationDoc(DcmSegmentation* doc);

private:

    /// The segmentation document where this segment is located in
    DcmSegmentation* m_SegmentationDoc;

    /// The segment number as read from the Segment Number attribute.
    /// This attribute only holds the number read from the file/item,
    /// and will be updated only during a write process. It is not meant
    /// to be used by the API user at all but only for internal purposes.
    DcmUnsignedShort m_SegmentNumber;

    /// Segment Description Macro
    SegmentDescriptionMacro m_SegmentDescription;

    /// Segment Algorithm Name: (LO, 1, 1C)
    DcmLongString m_SegmentAlgorithmName;

    /// Segmentation Algorithm Identification (SQ, 1, 3).
    /// This attribute has earlier been named m_SegmentationSurfaceGenerationAlgorithmIdentification
    /// representing the related sequence. This has been changed in favor of the
    /// Segmentation Algorithm Identification Sequence due to DICOM CP-1597.
    AlgorithmIdentificationMacro m_SegmentationAlgorithmIdentification;

    /// Recommended Display Grayscale Value (US, 1, 3)
    DcmUnsignedShort m_RecommendedDisplayGrayscaleValue;

    /// Recommended Display CIELab Value (US, 3, 3)
    DcmUnsignedShort m_RecommendedDisplayCIELabValue;

    /// Tracking ID (UT, 1, 1C)
    DcmUnlimitedText m_TrackingID;

    /// Tracking UID (UI, 1, 1C)
    DcmUniqueIdentifier m_TrackingUID;

    /// Rules for data elements within this IOD
    OFshared_ptr<IODRules> m_Rules;
};

#endif // SEGMENT_H
