/*
 *
 *  Copyright (C) 2015-2017, J. Riesmeier, Oldenburg, Germany
 *  All rights reserved.  See COPYRIGHT file for details.
 *
 *  Source file for class TID1600_ImageLibrary
 *
 *  Author: Joerg Riesmeier
 *
 */


#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */

#include "dcmtk/dcmsr/cmr/tid1600.h"
#include "dcmtk/dcmsr/cmr/cid29e.h"
#include "dcmtk/dcmsr/cmr/cid244e.h"
#include "dcmtk/dcmsr/cmr/cid4031e.h"
#include "dcmtk/dcmsr/cmr/cid10013e.h"
#include "dcmtk/dcmsr/cmr/cid10033e.h"
#include "dcmtk/dcmsr/cmr/logger.h"
#include "dcmtk/dcmsr/codes/dcm.h"
#include "dcmtk/dcmsr/codes/srt.h"
#include "dcmtk/dcmsr/codes/ucum.h"
#include "dcmtk/dcmsr/dsrdattn.h"

#include "dcmtk/dcmdata/dcdeftag.h"
#include "dcmtk/dcmdata/dcuid.h"


// helper macros for checking the return value of API calls
#define CHECK_RESULT(call) if (result.good()) result = call
#define STORE_RESULT(call) result = call
#define GOOD_RESULT(call) if (result.good()) call
#define BAD_RESULT(call) if (result.bad()) call

// index positions in node list (makes source code more readable)
#define IMAGE_LIBRARY            0
#define LAST_IMAGE_LIBRARY_GROUP 1
#define LAST_IMAGE_LIBRARY_ENTRY 2
#define NUMBER_OF_LIST_ENTRIES   3

// general information on TID 1600 (Image Library)
#define TEMPLATE_NUMBER      "1600"
#define MAPPING_RESOURCE     "DCMR"
#define MAPPING_RESOURCE_UID UID_DICOMContentMappingResource
#define TEMPLATE_TYPE        OFTrue   /* extensible */
#define TEMPLATE_ORDER       OFFalse  /* non-significant */


// conditions constants
makeOFConditionConst(CMR_EC_NoImageLibrary,                                OFM_dcmsr, 1600, OF_error, "No Image Library");
makeOFConditionConst(CMR_EC_NoImageLibraryGroup,                           OFM_dcmsr, 1601, OF_error, "No Image Library Group");
makeOFConditionConst(CMR_EC_NoImageLibraryEntry,                           OFM_dcmsr, 1602, OF_error, "No Image Library Entry");
makeOFConditionConst(CMR_EC_CannotAddMultipleImageLibraryGroupDescriptors, OFM_dcmsr, 1603, OF_error, "Cannot add multiple Image Library Group Descriptors");
makeOFConditionConst(CMR_EC_MissingImageLibraryEntryDescriptorModality,    OFM_dcmsr, 1604, OF_error, "Missing Image Library Entry Descriptor 'Modality'");
makeOFConditionConst(CMR_EC_WrongImageLibraryEntryDescriptorModality,      OFM_dcmsr, 1605, OF_error, "Wrong Image Library Entry Descriptor 'Modality'");
makeOFConditionConst(CMR_EC_NoImageLibraryEntryDescriptorsToBeAdded,       OFM_dcmsr, 1606, OF_ok,    "No Image Library Entry Descriptors to be added");
makeOFConditionConst(CMR_EC_NoImageLibraryEntryDescriptorsToBeMoved,       OFM_dcmsr, 1607, OF_ok,    "No Image Library Entry Descriptors to be moved");


TID1600_ImageLibrary::TID1600_ImageLibrary(const OFBool createLibrary)
  : DSRSubTemplate(TEMPLATE_NUMBER, MAPPING_RESOURCE, MAPPING_RESOURCE_UID)
{
    setExtensible(TEMPLATE_TYPE);
    setOrderSignificant(TEMPLATE_ORDER);
    /* need to store position of various content items */
    reserveEntriesInNodeList(NUMBER_OF_LIST_ENTRIES);
    /* TID 1600 (Image Library) Row 1 */
    if (createLibrary)
        createImageLibrary();
}


OFBool TID1600_ImageLibrary::isValid() const
{
    /* check whether base class is valid and image library exists */
    return DSRSubTemplate::isValid() && hasImageLibrary();
}


OFBool TID1600_ImageLibrary::hasImageLibrary() const
{
    /* check for image library (TID 1600 - Row 1) */
    return (getEntryFromNodeList(IMAGE_LIBRARY) > 0);
}


OFBool TID1600_ImageLibrary::hasImageLibraryGroup() const
{
    /* check for image library group (TID 1600 - Row 2) */
    return (getEntryFromNodeList(LAST_IMAGE_LIBRARY_GROUP) > 0);
}


OFCondition TID1600_ImageLibrary::createNewImageLibrary()
{
    clear();
    /* TID 1600 (Image Library) Row 1 */
    return createImageLibrary();
}


OFCondition TID1600_ImageLibrary::addImageGroup()
{
    OFCondition result = CMR_EC_NoImageLibrary;
    /* check whether image library exists */
    if (hasImageLibrary())
    {
        /* go to last image library group (if any) */
        if (gotoEntryFromNodeList(this, LAST_IMAGE_LIBRARY_GROUP) > 0)
        {
            /* TID 1600 (Image Library) Row 2, append to last group */
            STORE_RESULT(addContentItem(RT_contains, VT_Container, CODE_DCM_ImageLibraryGroup));
        }
        /* go to image library (root node) */
        else if (gotoEntryFromNodeList(this, IMAGE_LIBRARY) > 0)
        {
            /* TID 1600 (Image Library) Row 2, create first group (child) */
            STORE_RESULT(addChildContentItem(RT_contains, VT_Container, CODE_DCM_ImageLibraryGroup));
        } else {
            /* should never happen but ... */
            result = SR_EC_InvalidTemplateStructure;
        }
        CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1600 - Row 2"));
        /* store ID of recently added node for later use */
        if (result.good())
        {
            storeEntryInNodeList(LAST_IMAGE_LIBRARY_GROUP, getNodeID());
            storeEntryInNodeList(LAST_IMAGE_LIBRARY_ENTRY, 0 /* forget last entry */);
        }
    }
    return result;
}


OFCondition TID1600_ImageLibrary::addImageEntry(DcmItem &dataset,
                                                const AddImageMode mode,
                                                const ConceptNameList &descriptors,
                                                const OFBool check)
{
    OFCondition result = CMR_EC_NoImageLibraryGroup;
    /* check whether image library group exists */
    if (hasImageLibraryGroup())
    {
        /* create a new subtree in order to "rollback" in case of error */
        DSRDocumentSubTree *tid1601 = new DSRDocumentSubTree;
        if (tid1601 != NULL)
        {
            DSRImageReferenceValue imageRef;
            /* TID 1601 (Image Library Entry) Row 1 */
            STORE_RESULT(tid1601->addContentItem(RT_contains, VT_Image, DSRCodedEntryValue(), check));
            CHECK_RESULT(imageRef.setReference(dataset, check));
            CHECK_RESULT(tid1601->getCurrentContentItem().setImageReference(imageRef, check));
            CHECK_RESULT(tid1601->getCurrentContentItem().setAnnotationText("TID 1601 - Row 1"));
            const size_t lastNode = tid1601->getNodeID();
            /* TID 1601 (Image Library Entry) Row 2 */
            if (anyDescriptorsToBeAdded(mode, descriptors))
            {
                /* create a new subtree for TID 1602 (Image Library Entry Descriptors) */
                DSRDocumentSubTree *tid1602 = new DSRDocumentSubTree;
                if (tid1602 != NULL)
                {
                    /* call the function doing the real work */
                    STORE_RESULT(addImageEntryDescriptors(*tid1602, dataset, mode, descriptors, check));
                    /* if everything was OK, insert new subtree into the template */
                    if (result.good() && !tid1602->isEmpty())
                    {
                        /* insert subtree below current node */
                        STORE_RESULT(tid1601->insertSubTree(tid1602));
                        /* in case of error, make sure that memory is freed */
                        BAD_RESULT(delete tid1602);
                    } else {
                        CHECK_RESULT(CMR_EC_NoImageLibraryEntryDescriptorsToBeAdded);
                        /* delete the new subtree since it has not been inserted */
                        delete tid1602;
                    }
                } else
                    result = EC_MemoryExhausted;
            }
            /* if everything was OK, insert new subtree into the template */
            if (result.good())
            {
                E_AddMode addMode = AM_afterCurrent;
                /* go to last image library entry (if any) */
                if (gotoEntryFromNodeList(this, LAST_IMAGE_LIBRARY_ENTRY) > 0)
                    addMode = AM_afterCurrent;
                /* go to last image library group */
                else if (gotoEntryFromNodeList(this, LAST_IMAGE_LIBRARY_GROUP) > 0)
                    addMode = AM_belowCurrent;
                else {
                    /* should never happen but ... */
                    result = CMR_EC_NoImageLibraryGroup;
                }
                /* there is at least an image library group */
                if (result.good())
                {
                    /* insert subtree at current position */
                    STORE_RESULT(insertSubTree(tid1601, addMode));
                    /* store ID of recently added node for later use */
                    GOOD_RESULT(storeEntryInNodeList(LAST_IMAGE_LIBRARY_ENTRY, lastNode));
                }
            }
            /* in case of error, make sure that memory is freed */
            BAD_RESULT(delete tid1601);
        } else
            result = EC_MemoryExhausted;
    }
    return result;
}


OFCondition TID1600_ImageLibrary::addImageGroupDescriptors(DcmItem &dataset,
                                                           const AddImageMode mode,
                                                           const ConceptNameList &descriptors,
                                                           const OFBool check)
{
    OFCondition result = CMR_EC_NoImageLibraryGroup;
    /* check whether image library group exists */
    if (hasImageLibraryGroup())
    {
        /* check whether there are any descriptors to be added (at all) */
        if (anyDescriptorsToBeAdded(mode, descriptors))
        {
            /* create a new subtree in order to "rollback" in case of error */
            DSRDocumentSubTree *subTree = new DSRDocumentSubTree;
            if (subTree != NULL)
            {
                /* call the function doing the real work */
                STORE_RESULT(addImageEntryDescriptors(*subTree, dataset, mode, descriptors, check));
                /* if everything was OK, insert new subtree into the template */
                if (result.good() && !subTree->isEmpty())
                {
                    /* go to last image library group (if any) */
                    if (gotoEntryFromNodeList(this, LAST_IMAGE_LIBRARY_GROUP) > 0)
                    {
                        /* check whether TID 1600 (Image Library) Row 3 is already there */
                        const DSRDocumentTreeNode *childNode = getChildNode();
                        if ((childNode != NULL) && (childNode->getRelationshipType() == RT_hasAcqContext))
                        {
                            /* only a single invocation of the included template allowed */
                            result = CMR_EC_CannotAddMultipleImageLibraryGroupDescriptors;
                        } else {
                            /* insert subtree at current position */
                            STORE_RESULT(insertSubTree(subTree, AM_belowCurrentBeforeFirstChild));
                        }
                    } else {
                        /* should never happen but ... */
                        result = CMR_EC_NoImageLibraryGroup;
                    }
                    /* in case of error, make sure that memory is freed */
                    BAD_RESULT(delete subTree);
                } else {
                    CHECK_RESULT(CMR_EC_NoImageLibraryEntryDescriptorsToBeAdded);
                    /* delete the new subtree since it has not been inserted */
                    delete subTree;
                }
            } else
                result = EC_MemoryExhausted;
        } else {
            /* the combination of 'mode' and 'descriptors' parameter makes no sense */
            result = EC_IllegalParameter;
        }
    }
    return result;
}


OFCondition TID1600_ImageLibrary::getImageEntryModality(DSRCodedEntryValue &modalityCode)
{
    OFCondition result = CMR_EC_NoImageLibraryEntry;
    /* go to last image library entry (if any) */
    if (gotoEntryFromNodeList(this, LAST_IMAGE_LIBRARY_ENTRY) > 0)
    {
        /* get value of TID 1602 (Image Library Entry Descriptors) Row 1 */
        if ((gotoNamedChildNode(CODE_DCM_Modality) > 0))
        {
            modalityCode = getCurrentContentItem().getCodeValue();
            result = EC_Normal;
        } else
            result = CMR_EC_MissingImageLibraryEntryDescriptorModality;
    }
    /* in case of error, clear the result variable */
    BAD_RESULT(modalityCode.clear());
    return result;
}


OFCondition TID1600_ImageLibrary::moveCommonImageDescriptorsToImageGroups()
{
    OFCondition result = CMR_EC_NoImageLibrary;
    /* go to image library (root node) */
    DSRDocumentTreeNodeCursor groupCursor(getRoot());
    if (groupCursor.isValid())
    {
        DCMSR_CMR_DEBUG("Moving common image descriptors to image library groups (TID 1600)");
        /* counter for moved image descriptors and image groups */
        size_t movedDescriptors = 0;
        size_t groupCounter = 0;
        const DSRDocumentTreeNodeConceptNameFilter groupFilter(CODE_DCM_ImageLibraryGroup);
        /* go to the first child (if any) and check for image library group */
        if (groupCursor.gotoChild() && groupCursor.gotoMatchingNode(groupFilter, OFFalse /*searchIntoSub*/))
        {
            result = EC_Normal;
            /* iterate over all image library groups */
            do {
                ++groupCounter;
                DSRDocumentTreeNodeCursor imageCursor(groupCursor);
                const DSRDocumentTreeNodeValueTypeFilter imageFilter(VT_Image);
                /* go to the first child (if any) and check for image library entry */
                if (imageCursor.gotoChild() && imageCursor.gotoMatchingNode(imageFilter, OFFalse /*searchIntoSub*/))
                {
                    DSRDocumentSubTree *subTree = NULL;
                    /* iterate over all image library entries */
                    do {
                        DSRDocumentTreeNodeCursor childCursor(imageCursor);
                        if (childCursor.gotoChild())
                        {
                            if (subTree != NULL)
                            {
                                size_t nodeID = subTree->gotoRoot();
                                /* check whether there are any common descriptors */
                                if (nodeID > 0)
                                {
                                    while (nodeID > 0)
                                    {
                                        DSRDocumentTreeNodeCursor cursor(childCursor);
                                        /* if not found, remove it from the list */
                                        if (!cursor.gotoNode(*subTree->getCurrentNode()))
                                            nodeID = subTree->removeCurrentContentItem();
                                        else
                                            nodeID = subTree->gotoNext();
                                    }
                                } else {
                                    /* nothing to compare, so exit the loop */
                                    break;
                                }
                            } else {
                                /* create a copy of the subtree (starting from the first child) */
                                subTree = DSRDocumentSubTree::cloneSubTree(childCursor, imageCursor.getNodeID());
                            }
                        } else {
                            /* no image descriptors for this entry, so clear the list and exit the loop */
                            if (subTree != NULL)
                                subTree->clear();
                            break;
                        }
                    } while (imageCursor.gotoNextMatchingNode(imageFilter, OFFalse /*searchIntoSub*/));
                    /* if there are any common descriptors ... */
                    if (subTree != NULL)
                    {
                        DCMSR_CMR_DEBUG("  Found " << subTree->countNodes() << " common image descriptors in image library group #" << groupCounter);
                        if (!subTree->isEmpty())
                        {
                            const DSRDocumentTreeNodeCursor oldCursor(getCursor());
                            /* remove them from the respective image library entries */
                            imageCursor = groupCursor;
                            if (imageCursor.gotoChild() && imageCursor.gotoMatchingNode(imageFilter, OFFalse /*searchIntoSub*/))
                            {
                                /* iterate over all image library entries */
                                do {
                                    if (subTree->gotoRoot())
                                    {
                                        do {
                                            /* go to the matching node (if any) */
                                            setCursor(imageCursor);
                                            if (gotoChild())
                                            {
                                                if (gotoNode(*subTree->getCurrentNode(), OFFalse /*startFromRoot*/))
                                                {
                                                    /* and remove it from the tree */
                                                    removeCurrentContentItem();
                                                    ++movedDescriptors;
                                                }
                                            } else {
                                                /* nothing to compare, so exit the loop */
                                                break;
                                            }
                                        } while (subTree->gotoNext());
                                    }
                                } while (imageCursor.gotoNextMatchingNode(imageFilter, OFFalse /*searchIntoSub*/));
                            }
                            /* and, finally, insert common descriptors to image library group */
                            setCursor(groupCursor);
                            STORE_RESULT(insertSubTree(subTree, AM_belowCurrentBeforeFirstChild, RT_unknown /*defaultRelType*/, OFTrue /*deleteIfFail*/));
                            /* reset cursor (to old position) */
                            setCursor(oldCursor);
                        } else {
                            /* free memory */
                            delete subTree;
                        }
                    } else
                        DCMSR_CMR_DEBUG("  No descriptors on image level, skipping image library group #" << groupCounter);
                }
            } while (result.good() && groupCursor.gotoNextMatchingNode(groupFilter, OFFalse /*searchIntoSub*/));
            /* check whether any descriptors have been moved */
            if (movedDescriptors > 0)
                DCMSR_CMR_DEBUG("Moved a total of " << movedDescriptors << " image descriptors to image group level");
            else
                result = CMR_EC_NoImageLibraryEntryDescriptorsToBeMoved;
        } else
            result = CMR_EC_NoImageLibraryGroup;
    }
    return result;
}


// set methods

OFCondition TID1600_ImageLibrary::setPETImageRadionuclide(const CID4020_PETRadionuclide &radionuclide,
                                                          const OFBool check)
{
    /* remember current node */
    const DSRDocumentTreeNodeCursor cursor(getCursor());
    /* go to image library entry and check for expected modality */
    OFCondition result = goAndCheckImageLibraryEntry(CODE_DCM_PositronEmissionTomography);
    /* set content item value (and add a new content item if needed) */
    CHECK_RESULT(setCodeContentItemFromValue(CODE_SRT_Radionuclide, radionuclide, "TID 1607 - Row 1", check));
    /* in case of error, reset cursor to stored node */
    BAD_RESULT(setCursor(cursor));
    return result;
}


OFCondition TID1600_ImageLibrary::setPETImageRadiopharmaceuticalAgent(const CID4021_PETRadiopharmaceutical &agent,
                                                                      const OFBool check)
{
    /* remember current node */
    const DSRDocumentTreeNodeCursor cursor(getCursor());
    /* go to image library entry and check for expected modality */
    OFCondition result = goAndCheckImageLibraryEntry(CODE_DCM_PositronEmissionTomography);
    /* set content item value (and add a new content item if needed) */
    CHECK_RESULT(setCodeContentItemFromValue(CODE_SRT_RadiopharmaceuticalAgent, agent, "TID 1607 - Row 2", check));
    /* in case of error, reset cursor to stored node */
    BAD_RESULT(setCursor(cursor));
    return result;
}


OFCondition TID1600_ImageLibrary::setPETImageRadiopharmaceuticalStartDateTime(const OFString &dateTime,
                                                                              const OFBool check)
{
    /* remember current node */
    const DSRDocumentTreeNodeCursor cursor(getCursor());
    /* go to image library entry and check for expected modality */
    OFCondition result = goAndCheckImageLibraryEntry(CODE_DCM_PositronEmissionTomography);
    /* set content item value (and add a new content item if needed) */
    CHECK_RESULT(setStringContentItemFromValue(VT_DateTime, CODE_DCM_RadiopharmaceuticalStartDateTime, dateTime, "TID 1607 - Row 4", check));
    /* in case of error, reset cursor to stored node */
    BAD_RESULT(setCursor(cursor));
    return result;
}


OFCondition TID1600_ImageLibrary::setPETImageRadiopharmaceuticalStopDateTime(const OFString &dateTime,
                                                                             const OFBool check)
{
    /* remember current node */
    const DSRDocumentTreeNodeCursor cursor(getCursor());
    /* go to image library entry and check for expected modality */
    OFCondition result = goAndCheckImageLibraryEntry(CODE_DCM_PositronEmissionTomography);
    /* set content item value (and add a new content item if needed) */
    CHECK_RESULT(setStringContentItemFromValue(VT_DateTime, CODE_DCM_RadiopharmaceuticalStopDateTime, dateTime, "TID 1607 - Row 5", check));
    /* in case of error, reset cursor to stored node */
    BAD_RESULT(setCursor(cursor));
    return result;
}


OFCondition TID1600_ImageLibrary::setPETImageRadiopharmaceuticalVolume(const CMR_SRNumericMeasurementValue &volume,
                                                                       const OFBool check)
{
    /* remember current node */
    const DSRDocumentTreeNodeCursor cursor(getCursor());
    /* go to image library entry and check for expected modality */
    OFCondition result = goAndCheckImageLibraryEntry(CODE_DCM_PositronEmissionTomography);
    /* set content item value (and add a new content item if needed) */
    CHECK_RESULT(setNumericContentItemFromValue(CODE_DCM_RadiopharmaceuticalVolume, volume, CODE_UCUM_cm3 /*might change with a CP*/, "TID 1607 - Row 6", check));
    /* in case of error, reset cursor to stored node */
    BAD_RESULT(setCursor(cursor));
    return result;
}


OFCondition TID1600_ImageLibrary::setPETImageRadionuclideTotalDose(const CMR_SRNumericMeasurementValue &totalDose,
                                                                   const OFBool check)
{
    /* remember current node */
    const DSRDocumentTreeNodeCursor cursor(getCursor());
    /* go to image library entry and check for expected modality */
    OFCondition result = goAndCheckImageLibraryEntry(CODE_DCM_PositronEmissionTomography);
    /* set content item value (and add a new content item if needed) */
    CHECK_RESULT(setNumericContentItemFromValue(CODE_DCM_RadionuclideTotalDose, totalDose, CODE_UCUM_Bq, "TID 1607 - Row 7", check));
    /* in case of error, reset cursor to stored node */
    BAD_RESULT(setCursor(cursor));
    return result;
}


// protected methods

OFCondition TID1600_ImageLibrary::createImageLibrary()
{
    OFCondition result = SR_EC_InvalidTemplateStructure;
    if (isEmpty())
    {
        /* TID 1600 (Image Library) Row 1 */
        STORE_RESULT(addContentItem(RT_unknown, VT_Container, CODE_DCM_ImageLibrary));
        CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1600 - Row 1"));
        /* store ID of root node for later use */
        GOOD_RESULT(storeEntryInNodeList(IMAGE_LIBRARY, getNodeID()));
    }
    return result;
}


OFCondition TID1600_ImageLibrary::addImageEntryDescriptors(DSRDocumentSubTree &tree,
                                                           DcmItem &dataset,
                                                           const AddImageMode mode,
                                                           const ConceptNameList &descriptors,
                                                           const OFBool check)
{
    OFCondition result = EC_Normal;
    /* TID 1602 (Image Library Entry Descriptors) Row 1 */
    OFString modality;
    if (getStringValueFromDataset(dataset, DCM_Modality, modality).good() && !modality.empty())
    {
        /* check whether descriptor should be added */
        if (checkDescriptorToBeAdded(CODE_DCM_Modality, mode, descriptors))
        {
            /* determine modality code from CID 29 */
            DSRCodedEntryValue modalityCode;
            const CID29e_AcquisitionModality contextGroup;
            if (contextGroup.mapModality(modality, modalityCode).good())
            {
                CHECK_RESULT(tree.addContentItem(RT_hasAcqContext, VT_Code, CODE_DCM_Modality, check));
                CHECK_RESULT(tree.getCurrentContentItem().setCodeValue(modalityCode, check));
                CHECK_RESULT(tree.getCurrentContentItem().setAnnotationText("TID 1602 - Row 1"));
            } else {
                /* do not treat this as an error, just report a warning */
                DCMSR_CMR_WARN("Cannot map Modality '" << modality << "' to a coded entry (not in CID 29)");
            }
        }
    }
    /* TID 1602 (Image Library Entry Descriptors) Row 2 */
    if (checkDescriptorToBeAdded(CODE_DCM_TargetRegion, mode, descriptors))
    {
        DSRCodedEntryValue regionCode;
        /* try to get coded entry from code sequence */
        if (regionCode.readSequence(dataset, DCM_AnatomicRegionSequence, "3" /*type*/).bad())
        {
            OFString bodyPartExamined;
            if (getStringValueFromDataset(dataset, DCM_BodyPartExamined, bodyPartExamined).good() && !bodyPartExamined.empty())
            {
                /* alternatively, determine target region code from CID 4031 (using PS 3.16 Annex L) */
                if (CID4031e_CommonAnatomicRegions::mapBodyPartExamined(bodyPartExamined, regionCode).bad())
                {
                    /* report this as a debug message (avoid too many warnings) */
                    DCMSR_CMR_DEBUG("Cannot map Body Part Examined '" << bodyPartExamined << "' to a coded entry (no mapping to CID 4031 defined)");
                }
            }
        }
        if (regionCode.isComplete())
        {
            CHECK_RESULT(tree.addContentItem(RT_hasAcqContext, VT_Code, CODE_DCM_TargetRegion, check));
            CHECK_RESULT(tree.getCurrentContentItem().setCodeValue(regionCode, check));
            CHECK_RESULT(tree.getCurrentContentItem().setAnnotationText("TID 1602 - Row 2"));
        }
    }
    /* TID 1602 (Image Library Entry Descriptors) Row 3 */
    if (checkDescriptorToBeAdded(CODE_DCM_ImageLaterality, mode, descriptors))
    {
        OFString imageLaterality;
        if (getStringValueFromDataset(dataset, DCM_ImageLaterality, imageLaterality).good() && !imageLaterality.empty())
        {
            /* determine image laterality code from CID 244 */
            DSRCodedEntryValue lateralityCode;
            if (CID244e_Laterality::mapImageLaterality(imageLaterality, lateralityCode).good())
            {
                CHECK_RESULT(tree.addContentItem(RT_hasAcqContext, VT_Code, CODE_DCM_ImageLaterality, check));
                CHECK_RESULT(tree.getCurrentContentItem().setCodeValue(lateralityCode, check));
                CHECK_RESULT(tree.getCurrentContentItem().setAnnotationText("TID 1602 - Row 3"));
            } else {
                /* do not treat this as an error, just report a warning */
                DCMSR_CMR_WARN("Cannot map Image Laterality '" << imageLaterality << "' to a coded entry (not in CID 244)");
            }
        }
    }
    /* TID 1602 (Image Library Entry Descriptors) Row 4 */
    if (checkDescriptorToBeAdded(CODE_DCM_StudyDate, mode, descriptors))
        CHECK_RESULT(addStringContentItemFromDataset(tree, dataset, DCM_StudyDate, 0 /*pos*/, VT_Date, CODE_DCM_StudyDate, "TID 1602 - Row 4", check));
    /* TID 1602 (Image Library Entry Descriptors) Row 5 */
    if (checkDescriptorToBeAdded(CODE_DCM_StudyTime, mode, descriptors))
        CHECK_RESULT(addStringContentItemFromDataset(tree, dataset, DCM_StudyTime, 0 /*pos*/, VT_Time, CODE_DCM_StudyTime, "TID 1602 - Row 5", check));
    /* TID 1602 (Image Library Entry Descriptors) Row 6 */
    if (checkDescriptorToBeAdded(CODE_DCM_ContentDate, mode, descriptors))
        CHECK_RESULT(addStringContentItemFromDataset(tree, dataset, DCM_ContentDate, 0 /*pos*/, VT_Date, CODE_DCM_ContentDate, "TID 1602 - Row 6", check));
    /* TID 1602 (Image Library Entry Descriptors) Row 7 */
    if (checkDescriptorToBeAdded(CODE_DCM_ContentTime, mode, descriptors))
        CHECK_RESULT(addStringContentItemFromDataset(tree, dataset, DCM_ContentTime, 0 /*pos*/, VT_Time, CODE_DCM_ContentTime, "TID 1602 - Row 7", check));
    /* TID 1602 (Image Library Entry Descriptors) Row 8 */
    if (checkDescriptorToBeAdded(CODE_DCM_AcquisitionDate, mode, descriptors))
        CHECK_RESULT(addStringContentItemFromDataset(tree, dataset, DCM_AcquisitionDate, 0 /*pos*/, VT_Date, CODE_DCM_AcquisitionDate, "TID 1602 - Row 8", check));
    /* TID 1602 (Image Library Entry Descriptors) Row 9 */
    if (checkDescriptorToBeAdded(CODE_DCM_AcquisitionTime, mode, descriptors))
        CHECK_RESULT(addStringContentItemFromDataset(tree, dataset, DCM_AcquisitionTime, 0 /*pos*/, VT_Time, CODE_DCM_AcquisitionTime, "TID 1602 - Row 9", check));
    /* TID 1602 (Image Library Entry Descriptors) Row 10 */
    if (checkDescriptorToBeAdded(CODE_DCM_FrameOfReferenceUID, mode, descriptors))
        CHECK_RESULT(addStringContentItemFromDataset(tree, dataset, DCM_FrameOfReferenceUID, 0 /*pos*/, VT_UIDRef, CODE_DCM_FrameOfReferenceUID, "TID 1602 - Row 10", check));
    /* TID 1602 (Image Library Entry Descriptors) Row 11 */
    /* - tbc: what about DCM_TotalPixelMatrixRows (e.g. used for WSI images)? */
    if (checkDescriptorToBeAdded(CODE_DCM_PixelDataRows, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_Rows, 0 /*pos*/, CODE_DCM_PixelDataRows, CODE_UCUM_Pixels, "TID 1602 - Row 11", check));
    /* TID 1602 (Image Library Entry Descriptors) Row 12 */
    /* - tbc: what about DCM_TotalPixelMatrixColumns (e.g. used for WSI images)? */
    if (checkDescriptorToBeAdded(CODE_DCM_PixelDataColumns, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_Columns, 0 /*pos*/, CODE_DCM_PixelDataColumns, CODE_UCUM_Pixels, "TID 1602 - Row 12", check));
    /* continue with modality-specific descriptors (TID 1603 to 1607) */
    CHECK_RESULT(addModalitySpecificDescriptors(tree, dataset, modality, mode, descriptors, check));
    return result;
}


OFCondition TID1600_ImageLibrary::addModalitySpecificDescriptors(DSRDocumentSubTree &tree,
                                                                 DcmItem &dataset,
                                                                 const OFString &modality,
                                                                 const AddImageMode mode,
                                                                 const ConceptNameList &descriptors,
                                                                 const OFBool check)
{
    OFCondition result = EC_Normal;
    /* TID 1603 (Image Library Entry Descriptors for Projection Radiography) */
    if ((modality == "CR") || (modality == "RG") || (modality == "DX") || (modality == "MG") || (modality == "XA") || (modality == "RF") || (modality == "PX") || (modality == "IO"))
        CHECK_RESULT(addProjectionRadiographyDescriptors(tree, dataset, mode, descriptors, check));
    /* TID 1604 (Image Library Entry Descriptors for Cross-Sectional Modalities) */
    if ((modality == "CT") || (modality == "MR") || (modality == "US") /* correct? */ || (modality == "PT") || (modality == "OCT") || (modality == "OPT") || (modality == "IVOCT"))
        CHECK_RESULT(addCrossSectionalModalitiesDescriptors(tree, dataset, mode, descriptors, check));
    /* TID 1605 (Image Library Entry Descriptors for CT) */
    if (modality == "CT")
        CHECK_RESULT(addComputedTomographyDescriptors(tree, dataset, mode, descriptors, check));
    /* TID 1606 (Image Library Entry Descriptors for MR) */
    if (modality == "MR")
        CHECK_RESULT(addMagneticResonanceDescriptors(tree, dataset, mode, descriptors, check));
    /* TID 1607 (Image Library Entry Descriptors for PET) */
    if (modality == "PT")
        CHECK_RESULT(addPositronEmissionTomographyDescriptors(tree, dataset, mode, descriptors, check));
    return result;
}


OFCondition TID1600_ImageLibrary::addProjectionRadiographyDescriptors(DSRDocumentSubTree &tree,
                                                                      DcmItem &dataset,
                                                                      const AddImageMode mode,
                                                                      const ConceptNameList &descriptors,
                                                                      const OFBool check)
{
    OFCondition result = EC_Normal;
    /* TID 1603 (Image Library Entry Descriptors for Projection Radiography) Row 1 */
    if (checkDescriptorToBeAdded(CODE_DCM_ImageView, mode, descriptors))
        CHECK_RESULT(addCodeContentItemFromDataset(tree, dataset, DCM_ViewCodeSequence, CODE_DCM_ImageView, "TID 1603 - Row 1", check));
    /* TID 1603 (Image Library Entry Descriptors for Projection Radiography) Row 2 */
    if (checkDescriptorToBeAdded(CODE_DCM_ImageViewModifier, mode, descriptors))
    {
        if (result.good() && (tree.getCurrentContentItem().getConceptName() == CODE_DCM_ImageView))
        {
            DcmItem *item = NULL;
            /* get view modifiers (if any) */
            if (dataset.findAndGetSequenceItem(DCM_ViewCodeSequence, item, 0 /*itemNum*/).good())
            {
                DcmSequenceOfItems *sequence = NULL;
                if (item->findAndGetSequence(DCM_ViewModifierCodeSequence, sequence).good())
                {
                    /* iterate over all sequence items */
                    DcmObject *object = NULL;
                    while (((object = sequence->nextInContainer(object)) != NULL) && result.good())
                    {
                        DSRCodedEntryValue modifierCode;
                        if (modifierCode.readSequenceItem(*OFstatic_cast(DcmItem *, object), DCM_ViewModifierCodeSequence).good())
                        {
                            CHECK_RESULT(tree.addChildContentItem(RT_hasAcqContext, VT_Code, CODE_DCM_ImageViewModifier, check));
                            CHECK_RESULT(tree.getCurrentContentItem().setCodeValue(modifierCode, check));
                            CHECK_RESULT(tree.getCurrentContentItem().setAnnotationText("TID 1603 - Row 2"));
                            tree.goUp();
                        }
                    }
                }
            }
        }
    }
    /* TID 1603 (Image Library Entry Descriptors for Projection Radiography) Row 3 */
    if (checkDescriptorToBeAdded(CODE_DCM_PatientOrientationRow, mode, descriptors))
        CHECK_RESULT(addStringContentItemFromDataset(tree, dataset, DCM_PatientOrientation, 0 /*pos*/, VT_Text, CODE_DCM_PatientOrientationRow, "TID 1603 - Row 3", check));
    /* TID 1603 (Image Library Entry Descriptors for Projection Radiography) Row 4 */
    if (checkDescriptorToBeAdded(CODE_DCM_PatientOrientationColumn, mode, descriptors))
        CHECK_RESULT(addStringContentItemFromDataset(tree, dataset, DCM_PatientOrientation, 1 /*pos*/, VT_Text, CODE_DCM_PatientOrientationColumn, "TID 1603 - Row 4", check));
    /* TID 1603 (Image Library Entry Descriptors for Projection Radiography) Row 5 */
    if (checkDescriptorToBeAdded(CODE_DCM_HorizontalPixelSpacing, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_ImagerPixelSpacing, 1 /*pos*/, CODE_DCM_HorizontalPixelSpacing, CODE_UCUM_Millimeter, "TID 1603 - Row 5", check));
    /* TID 1603 (Image Library Entry Descriptors for Projection Radiography) Row 6 */
    if (checkDescriptorToBeAdded(CODE_DCM_VerticalPixelSpacing, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_ImagerPixelSpacing, 0 /*pos*/, CODE_DCM_VerticalPixelSpacing, CODE_UCUM_Millimeter, "TID 1603 - Row 6", check));
    /* TID 1603 (Image Library Entry Descriptors for Projection Radiography) Row 7 */
    if (checkDescriptorToBeAdded(CODE_DCM_PositionerPrimaryAngle, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_PositionerPrimaryAngle, 0 /*pos*/, CODE_DCM_PositionerPrimaryAngle, CODE_UCUM_Degrees, "TID 1603 - Row 7", check));
    /* TID 1603 (Image Library Entry Descriptors for Projection Radiography) Row 8 */
    if (checkDescriptorToBeAdded(CODE_DCM_PositionerSecondaryAngle, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_PositionerSecondaryAngle, 0 /*pos*/, CODE_DCM_PositionerSecondaryAngle, CODE_UCUM_Degrees, "TID 1603 - Row 8", check));
    return result;
}


OFCondition TID1600_ImageLibrary::addCrossSectionalModalitiesDescriptors(DSRDocumentSubTree &tree,
                                                                         DcmItem &dataset,
                                                                         const AddImageMode mode,
                                                                         const ConceptNameList &descriptors,
                                                                         const OFBool check)
{
    OFCondition result = EC_Normal;
    /* TID 1604 (Image Library Entry Descriptors for Cross-Sectional Modalities) Row 1 */
    if (checkDescriptorToBeAdded(CODE_DCM_HorizontalPixelSpacing, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_PixelSpacing, 1 /*pos*/, CODE_DCM_HorizontalPixelSpacing, CODE_UCUM_Millimeter, "TID 1604 - Row 1", check));
    /* TID 1604 (Image Library Entry Descriptors for Cross-Sectional Modalities) Row 2 */
    if (checkDescriptorToBeAdded(CODE_DCM_VerticalPixelSpacing, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_PixelSpacing, 0 /*pos*/, CODE_DCM_VerticalPixelSpacing, CODE_UCUM_Millimeter, "TID 1604 - Row 2", check));
    /* TID 1604 (Image Library Entry Descriptors for Cross-Sectional Modalities) Row 3 */
    if (checkDescriptorToBeAdded(CODE_DCM_SpacingBetweenSlices, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_SpacingBetweenSlices, 0 /*pos*/, CODE_DCM_SpacingBetweenSlices, CODE_UCUM_Millimeter, "TID 1604 - Row 3", check));
    /* TID 1604 (Image Library Entry Descriptors for Cross-Sectional Modalities) Row 4 */
    if (checkDescriptorToBeAdded(CODE_DCM_SliceThickness, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_SliceThickness, 0 /*pos*/, CODE_DCM_SliceThickness, CODE_UCUM_Millimeter, "TID 1604 - Row 4", check));
    /* TID 1604 (Image Library Entry Descriptors for Cross-Sectional Modalities) Row 5 */
    if (checkDescriptorToBeAdded(CODE_DCM_ImagePosition_Patient_X, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_ImagePositionPatient, 0 /*pos*/, CODE_DCM_ImagePosition_Patient_X, CODE_UCUM_Millimeter, "TID 1604 - Row 5", check));
    /* TID 1604 (Image Library Entry Descriptors for Cross-Sectional Modalities) Row 6 */
    if (checkDescriptorToBeAdded(CODE_DCM_ImagePosition_Patient_Y, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_ImagePositionPatient, 1 /*pos*/, CODE_DCM_ImagePosition_Patient_Y, CODE_UCUM_Millimeter, "TID 1604 - Row 6", check));
    /* TID 1604 (Image Library Entry Descriptors for Cross-Sectional Modalities) Row 7 */
    if (checkDescriptorToBeAdded(CODE_DCM_ImagePosition_Patient_Z, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_ImagePositionPatient, 2 /*pos*/, CODE_DCM_ImagePosition_Patient_Z, CODE_UCUM_Millimeter, "TID 1604 - Row 7", check));
    /* TID 1604 (Image Library Entry Descriptors for Cross-Sectional Modalities) Row 8 */
    if (checkDescriptorToBeAdded(CODE_DCM_ImageOrientation_Patient_RowX, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_ImageOrientationPatient, 0 /*pos*/, CODE_DCM_ImageOrientation_Patient_RowX, CODE_UCUM_Minus1To1, "TID 1604 - Row 8", check));
    /* TID 1604 (Image Library Entry Descriptors for Cross-Sectional Modalities) Row 9 */
    if (checkDescriptorToBeAdded(CODE_DCM_ImageOrientation_Patient_RowY, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_ImageOrientationPatient, 1 /*pos*/, CODE_DCM_ImageOrientation_Patient_RowY, CODE_UCUM_Minus1To1, "TID 1604 - Row 9", check));
    /* TID 1604 (Image Library Entry Descriptors for Cross-Sectional Modalities) Row 10 */
    if (checkDescriptorToBeAdded(CODE_DCM_ImageOrientation_Patient_RowZ, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_ImageOrientationPatient, 2 /*pos*/, CODE_DCM_ImageOrientation_Patient_RowZ, CODE_UCUM_Minus1To1, "TID 1604 - Row 10", check));
    /* TID 1604 (Image Library Entry Descriptors for Cross-Sectional Modalities) Row 11 */
    if (checkDescriptorToBeAdded(CODE_DCM_ImageOrientation_Patient_ColumnX, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_ImageOrientationPatient, 3 /*pos*/, CODE_DCM_ImageOrientation_Patient_ColumnX, CODE_UCUM_Minus1To1, "TID 1604 - Row 11", check));
    /* TID 1604 (Image Library Entry Descriptors for Cross-Sectional Modalities) Row 12 */
    if (checkDescriptorToBeAdded(CODE_DCM_ImageOrientation_Patient_ColumnY, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_ImageOrientationPatient, 4 /*pos*/, CODE_DCM_ImageOrientation_Patient_ColumnY, CODE_UCUM_Minus1To1, "TID 1604 - Row 12", check));
    /* TID 1604 (Image Library Entry Descriptors for Cross-Sectional Modalities) Row 13 */
    if (checkDescriptorToBeAdded(CODE_DCM_ImageOrientation_Patient_ColumnZ, mode, descriptors))
        CHECK_RESULT(addNumericContentItemFromDataset(tree, dataset, DCM_ImageOrientationPatient, 5 /*pos*/, CODE_DCM_ImageOrientation_Patient_ColumnZ, CODE_UCUM_Minus1To1, "TID 1604 - Row 13", check));
    return result;
}


OFCondition TID1600_ImageLibrary::addComputedTomographyDescriptors(DSRDocumentSubTree &tree,
                                                                   DcmItem &dataset,
                                                                   const AddImageMode mode,
                                                                   const ConceptNameList &descriptors,
                                                                   const OFBool check)
{
    OFCondition result = EC_Normal;
    /* TID 1605 (Image Library Entry Descriptors for CT) Row 1 */
    if (checkDescriptorToBeAdded(CODE_DCM_CTAcquisitionType, mode, descriptors))
    {
        DcmSequenceOfItems *ctAcquisitionTypeSequence = NULL;
        /* - tbd: only check in functional groups sequences? might use "dcmfg" for this purpose */
        if (dataset.findAndGetSequence(DCM_CTAcquisitionTypeSequence, ctAcquisitionTypeSequence, OFTrue /*searchIntoSub*/).good())
        {
            DcmItem *item = ctAcquisitionTypeSequence->getItem(0);
            if (item != NULL)
            {
                OFString acquisitionType;
                if (getStringValueFromDataset(*item, DCM_AcquisitionType, acquisitionType).good() && !acquisitionType.empty())
                {
                    /* determine CT acquisition type code from CID 10013 */
                    DSRCodedEntryValue acquisitionTypeCode;
                    if (CID10013e_CTAcquisitionType::mapAcquisitionType(acquisitionType, acquisitionTypeCode).good())
                    {
                        CHECK_RESULT(tree.addContentItem(RT_hasAcqContext, VT_Code, CODE_DCM_CTAcquisitionType, check));
                        CHECK_RESULT(tree.getCurrentContentItem().setCodeValue(acquisitionTypeCode, check));
                        CHECK_RESULT(tree.getCurrentContentItem().setAnnotationText("TID 1605 - Row 1"));
                    } else {
                        /* do not treat this as an error, just report a warning */
                        DCMSR_CMR_WARN("Cannot map Acquisition Type '" << acquisitionType << "' to a coded entry (not in CID 10013)");
                    }
                }
            }
        }
    }
    /* TID 1605 (Image Library Entry Descriptors for CT) Row 2 */
    if (checkDescriptorToBeAdded(CODE_DCM_ReconstructionAlgorithm, mode, descriptors))
    {
        DcmSequenceOfItems *ctReconstructionSequence = NULL;
        /* - tbd: only check in functional groups sequences? might use "dcmfg" for this purpose */
        if (dataset.findAndGetSequence(DCM_CTReconstructionSequence, ctReconstructionSequence, OFTrue /*searchIntoSub*/).good())
        {
            DcmItem *item = ctReconstructionSequence->getItem(0);
            if (item != NULL)
            {
                OFString reconstructionAlgorithm;
                if (getStringValueFromDataset(*item, DCM_ReconstructionAlgorithm, reconstructionAlgorithm).good() && !reconstructionAlgorithm.empty())
                {
                    /* determine CT reconstruction algorithm code from CID 10033 */
                    DSRCodedEntryValue reconstructionAlgorithmCode;
                    if (CID10033e_CTReconstructionAlgorithm::mapReconstructionAlgorithm(reconstructionAlgorithm, reconstructionAlgorithmCode).good())
                    {
                        CHECK_RESULT(tree.addContentItem(RT_hasAcqContext, VT_Code, CODE_DCM_ReconstructionAlgorithm, check));
                        CHECK_RESULT(tree.getCurrentContentItem().setCodeValue(reconstructionAlgorithmCode, check));
                        CHECK_RESULT(tree.getCurrentContentItem().setAnnotationText("TID 1605 - Row 2"));
                    } else {
                        /* do not treat this as an error, just report a warning */
                        DCMSR_CMR_WARN("Cannot map Reconstruction Algorithm '" << reconstructionAlgorithm << "' to a coded entry (not in CID 10033)");
                    }
                }
            }
        }
    }
    return result;
}


OFCondition TID1600_ImageLibrary::addMagneticResonanceDescriptors(DSRDocumentSubTree &tree,
                                                                  DcmItem &dataset,
                                                                  const AddImageMode mode,
                                                                  const ConceptNameList &descriptors,
                                                                  const OFBool check)
{
    OFCondition result = EC_Normal;
    /* TID 1606 (Image Library Entry Descriptors for MR) Row 1 */
    if (checkDescriptorToBeAdded(CODE_DCM_PulseSequenceName, mode, descriptors))
    {
        OFString sequenceName;
        /* get one of two alternative elements values */
        if ((getStringValueFromDataset(dataset, DCM_PulseSequenceName, sequenceName).good() && !sequenceName.empty()) ||
            (getStringValueFromDataset(dataset, DCM_SequenceName, sequenceName).good() && !sequenceName.empty()))
        {
            CHECK_RESULT(tree.addContentItem(RT_hasAcqContext, VT_Text, CODE_DCM_PulseSequenceName, check));
            CHECK_RESULT(tree.getCurrentContentItem().setStringValue(sequenceName, check));
            CHECK_RESULT(tree.getCurrentContentItem().setAnnotationText("TID 1606 - Row 1"));
        }
    }
    return result;
}


OFCondition TID1600_ImageLibrary::addPositronEmissionTomographyDescriptors(DSRDocumentSubTree &tree,
                                                                           DcmItem &dataset,
                                                                           const AddImageMode mode,
                                                                           const ConceptNameList &descriptors,
                                                                           const OFBool check)
{
    OFCondition result = EC_Normal;
    /* get main sequence from (Enhanced) PET Isotope Module */
    DcmSequenceOfItems *radiopharmaceuticalInformationSequence = NULL;
    if (dataset.findAndGetSequence(DCM_RadiopharmaceuticalInformationSequence, radiopharmaceuticalInformationSequence, OFTrue /*searchIntoSub*/).good())
    {
        /* tbd: sequence may contain multiple items! */
        DcmItem *item = radiopharmaceuticalInformationSequence->getItem(0);
        if (item != NULL)
        {
            /* TID 1607 (Image Library Entry Descriptors for PET) Row 1 */
            if (checkDescriptorToBeAdded(CODE_SRT_Radionuclide, mode, descriptors))
                CHECK_RESULT(addCodeContentItemFromDataset(tree, *item, DCM_RadionuclideCodeSequence, CODE_SRT_Radionuclide, "TID 1607 - Row 1", check));
            /* TID 1607 (Image Library Entry Descriptors for PET) Row 2 */
            if (checkDescriptorToBeAdded(CODE_SRT_RadiopharmaceuticalAgent, mode, descriptors))
                CHECK_RESULT(addCodeContentItemFromDataset(tree, *item, DCM_RadiopharmaceuticalCodeSequence, CODE_SRT_RadiopharmaceuticalAgent, "TID 1607 - Row 2", check));
            /* TID 1607 (Image Library Entry Descriptors for PET) Row 3 */
            if (checkDescriptorToBeAdded(CODE_SRT_HalfLifeOfRadiopharmaceutical, mode, descriptors))
                CHECK_RESULT(addNumericContentItemFromDataset(tree, *item, DCM_RadionuclideHalfLife, 0 /*pos*/, CODE_SRT_HalfLifeOfRadiopharmaceutical, CODE_UCUM_s, "TID 1607 - Row 3", check));
            /* TID 1607 (Image Library Entry Descriptors for PET) Row 4 */
            if (checkDescriptorToBeAdded(CODE_DCM_RadiopharmaceuticalStartDateTime, mode, descriptors))
                CHECK_RESULT(addStringContentItemFromDataset(tree, *item, DCM_RadiopharmaceuticalStartDateTime, 0 /*pos*/, VT_DateTime, CODE_DCM_RadiopharmaceuticalStartDateTime, "TID 1607 - Row 4", check));
            /* TID 1607 (Image Library Entry Descriptors for PET) Row 5 */
            if (checkDescriptorToBeAdded(CODE_DCM_RadiopharmaceuticalStopDateTime, mode, descriptors))
                CHECK_RESULT(addStringContentItemFromDataset(tree, *item, DCM_RadiopharmaceuticalStopDateTime, 0 /*pos*/, VT_DateTime, CODE_DCM_RadiopharmaceuticalStopDateTime, "TID 1607 - Row 5", check));
            /* TID 1607 (Image Library Entry Descriptors for PET) Row 6 */
            if (checkDescriptorToBeAdded(CODE_DCM_RadiopharmaceuticalVolume, mode, descriptors))
                CHECK_RESULT(addNumericContentItemFromDataset(tree, *item, DCM_RadiopharmaceuticalVolume, 0 /*pos*/, CODE_DCM_RadiopharmaceuticalVolume, CODE_UCUM_cm3 /*might change with a CP*/, "TID 1607 - Row 6", check));
            /* TID 1607 (Image Library Entry Descriptors for PET) Row 7 */
            if (checkDescriptorToBeAdded(CODE_DCM_RadionuclideTotalDose, mode, descriptors))
                CHECK_RESULT(addNumericContentItemFromDataset(tree, *item, DCM_RadionuclideTotalDose, 0 /*pos*/, CODE_DCM_RadionuclideTotalDose, CODE_UCUM_Bq, "TID 1607 - Row 7", check));
            /* TID 1607 (Image Library Entry Descriptors for PET) Row 8 */
            if (checkDescriptorToBeAdded(CODE_DCM_RadiopharmaceuticalSpecificActivity, mode, descriptors))
                CHECK_RESULT(addNumericContentItemFromDataset(tree, *item, DCM_RadiopharmaceuticalSpecificActivity, 0 /*pos*/, CODE_DCM_RadiopharmaceuticalSpecificActivity, CODE_UCUM_BqPerMol, "TID 1607 - Row 8", check));
            /* TID 1607 (Image Library Entry Descriptors for PET) Row 9 */
            if (checkDescriptorToBeAdded(CODE_SRT_RouteOfAdministration, mode, descriptors))
                CHECK_RESULT(addCodeContentItemFromDataset(tree, *item, DCM_AdministrationRouteCodeSequence, CODE_SRT_RouteOfAdministration, "TID 1607 - Row 9", check));
        }
    }
    /* TID 1607 (Image Library Entry Descriptors for PET) Row 10 to 11
     * - contained in TID 15101 (NM/PET Protocol Context), i.e. not available in the image
     */
    /* TID 1607 (Image Library Entry Descriptors for PET) Row 12
     * - "Radionuclide Incubation Time" calculated from time of injection and time of starting the scan
     */
    /* TID 1607 (Image Library Entry Descriptors for PET) Row 13 to 15
     * - contained in TID 15101 (NM/PET Protocol Context), i.e. available from Modality Worklist, or
     * - tbd: in TID 3470 (NM/PET Acquisition Context), i.e. from the Acquisition Context Module
     */
    return result;
}


OFCondition TID1600_ImageLibrary::goAndCheckImageLibraryEntry(const DSRCodedEntryValue &modalityCode)
{
    OFCondition result = CMR_EC_NoImageLibraryEntry;
    /* go to last image library entry (if any) */
    if (gotoEntryFromNodeList(this, LAST_IMAGE_LIBRARY_ENTRY) > 0)
    {
        /* check whether TID 1602 (Image Library Entry Descriptors) Row 1 is present */
        if (gotoNamedChildNode(CODE_DCM_Modality) > 0)
        {
            /* ... and has the expected value */
            if (getCurrentContentItem().getCodeValue() == modalityCode)
                result = EC_Normal;
            else
                result = CMR_EC_WrongImageLibraryEntryDescriptorModality;
        } else {
            /* tbd: could also check whether the Modality is available one level higher */
            result = CMR_EC_MissingImageLibraryEntryDescriptorModality;
        }
    }
    return result;
}


OFCondition TID1600_ImageLibrary::setStringContentItemFromValue(const E_ValueType valueType,
                                                                const DSRCodedEntryValue &conceptName,
                                                                const OFString &stringValue,
                                                                const OFString &annotationText,
                                                                const OFBool check)
{
    OFCondition result = EC_Normal;
    /* check concept name and coded entry value */
    if (conceptName.isComplete())
    {
        if (!stringValue.empty())
        {
            /* check whether content item is already present.
             * (we assume that the content item we are searching for is a successor of the current one)
             */
            if (gotoNextNamedNode(conceptName, OFFalse /*searchIntoSub*/) == 0)
            {
                CHECK_RESULT(addContentItem(RT_hasAcqContext, valueType, conceptName, check));
            } else {
                /* make sure that the value type of the existing content item is correct */
                if (getCurrentContentItem().getValueType() == valueType)
                {
                    DCMSR_CMR_DEBUG("Replacing value of '" << conceptName.getCodeMeaning()
                        << "' content item (" << annotationText << ") with \"" << stringValue << "\"");
                } else {
                    DCMSR_CMR_WARN("Cannot replace value of '" << conceptName.getCodeMeaning()
                        << "' content item (" << annotationText << ") ... wrong value type");
                    result = SR_EC_InvalidContentItem;
                }
            }
            CHECK_RESULT(getCurrentContentItem().setStringValue(stringValue, check));
            CHECK_RESULT(getCurrentContentItem().setAnnotationText(annotationText));
        } else
            result = SR_EC_InvalidValue;
    } else
        result = SR_EC_InvalidConceptName;
    return result;
}


OFCondition TID1600_ImageLibrary::setCodeContentItemFromValue(const DSRCodedEntryValue &conceptName,
                                                              const DSRCodedEntryValue &codeValue,
                                                              const OFString &annotationText,
                                                              const OFBool check)
{
    OFCondition result = EC_Normal;
    /* check concept name and coded entry value */
    if (conceptName.isComplete())
    {
        if (codeValue.isComplete())
        {
            /* check whether content item is already present.
             * (we assume that the content item we are searching for is a successor of the current one)
             */
            if (gotoNextNamedNode(conceptName, OFFalse /*searchIntoSub*/) == 0)
            {
                CHECK_RESULT(addContentItem(RT_hasAcqContext, VT_Code, conceptName, check));
            } else {
                /* make sure that the value type of the existing content item is correct */
                if (getCurrentContentItem().getValueType() == VT_Code)
                {
                    DCMSR_CMR_DEBUG("Replacing value of '" << conceptName.getCodeMeaning()
                        << "' content item (" << annotationText << ") with " << codeValue);
                } else {
                    DCMSR_CMR_WARN("Cannot replace value of '" << conceptName.getCodeMeaning()
                        << "' content item (" << annotationText << ") ... wrong value type");
                    result = SR_EC_InvalidContentItem;
                }
            }
            CHECK_RESULT(getCurrentContentItem().setCodeValue(codeValue, check));
            CHECK_RESULT(getCurrentContentItem().setAnnotationText(annotationText));
        } else
            result = SR_EC_InvalidValue;
    } else
        result = SR_EC_InvalidConceptName;
    return result;
}


OFCondition TID1600_ImageLibrary::setNumericContentItemFromValue(const DSRCodedEntryValue &conceptName,
                                                                 const DSRNumericMeasurementValue &numericValue,
                                                                 const DSRCodedEntryValue &measurementUnit,
                                                                 const OFString &annotationText,
                                                                 const OFBool check)
{
    OFCondition result = EC_Normal;
    /* check concept name and coded entry value */
    if (conceptName.isComplete())
    {
        if (numericValue.isComplete())
        {
            /* check whether content item is already present.
             * (we assume that the content item we are searching for is a successor of the current one)
             */
            if (gotoNextNamedNode(conceptName, OFFalse /*searchIntoSub*/) == 0)
            {
                CHECK_RESULT(addContentItem(RT_hasAcqContext, VT_Num, conceptName, check));
            } else {
                /* make sure that the value type of the existing content item is correct */
                if (getCurrentContentItem().getValueType() == VT_Num)
                {
                    DCMSR_CMR_DEBUG("Replacing value of '" << conceptName.getCodeMeaning()
                        << "' content item (" << annotationText << ") with " << numericValue);
                } else {
                    DCMSR_CMR_WARN("Cannot replace value of '" << conceptName.getCodeMeaning()
                        << "' content item (" << annotationText << ") ... wrong value type");
                    result = SR_EC_InvalidContentItem;
                }
            }
            /* check whether measurement unit meets the template constraint */
            if (result.good() && !measurementUnit.isEmpty() && (numericValue.getMeasurementUnit() != measurementUnit))
            {
                DCMSR_CMR_WARN("Wrong measurement unit for '" << conceptName.getCodeMeaning()
                    << "' content item (" << annotationText << ")");
                result = SR_EC_ValueSetConstraintViolated;
            }
            CHECK_RESULT(getCurrentContentItem().setNumericValue(numericValue, check));
            CHECK_RESULT(getCurrentContentItem().setAnnotationText(annotationText));
        } else
            result = SR_EC_InvalidValue;
    } else
        result = SR_EC_InvalidConceptName;
    return result;
}


// static helper functions

OFBool TID1600_ImageLibrary::anyDescriptorsToBeAdded(const AddImageMode mode,
                                                     const ConceptNameList &descriptors)
{
    /* check whether there are any descriptors to be added (at all) */
    return (mode == withAllDescriptors) || ((mode == withSelectedDescriptors) && !descriptors.empty()) || (mode == withoutSelectedDescriptors);
}


OFBool TID1600_ImageLibrary::checkDescriptorToBeAdded(const DSRBasicCodedEntry &conceptName,
                                                      const AddImageMode mode,
                                                      const ConceptNameList &descriptors)
{
    OFBool result = OFFalse;
    /* check which "add mode" to use */
    switch (mode)
    {
        case withoutDescriptors:
            result = OFFalse;
            break;
        case withAllDescriptors:
            result = OFTrue;
            break;
        case withSelectedDescriptors:
        {
            OFListConstIterator(DSRBasicCodedEntry) iter = descriptors.begin();
            const OFListConstIterator(DSRBasicCodedEntry) last = descriptors.end();
            const DSRCodedEntryValue codedEntry(conceptName);
            while ((iter != last) && (codedEntry !=  *iter))
                ++iter;
            result = (iter != last);
            break;
        }
        case withoutSelectedDescriptors:
        {
            OFListConstIterator(DSRBasicCodedEntry) iter = descriptors.begin();
            const OFListConstIterator(DSRBasicCodedEntry) last = descriptors.end();
            const DSRCodedEntryValue codedEntry(conceptName);
            while ((iter != last) && (codedEntry !=  *iter))
                ++iter;
            result = (iter == last);
            break;
        }
    }
    return result;
}


OFCondition TID1600_ImageLibrary::addStringContentItemFromDataset(DSRDocumentSubTree &tree,
                                                                  DcmItem &dataset,
                                                                  const DcmTagKey &tagKey,
                                                                  const signed long pos,
                                                                  const E_ValueType valueType,
                                                                  const DSRCodedEntryValue &conceptName,
                                                                  const OFString &annotationText,
                                                                  const OFBool check)
{
    OFCondition result = EC_Normal;
    OFString stringValue;
    /* get element value from dataset (textual content only) */
    if (getStringValueFromDataset(dataset, tagKey, stringValue, pos).good() && !stringValue.empty())
    {
        /* create new content item, set concept name and value */
        CHECK_RESULT(tree.addContentItem(RT_hasAcqContext, valueType, conceptName, check));
        CHECK_RESULT(tree.getCurrentContentItem().setStringValue(stringValue, check));
        if (!annotationText.empty())
            CHECK_RESULT(tree.getCurrentContentItem().setAnnotationText(annotationText));
    }
    return result;
}


OFCondition TID1600_ImageLibrary::addCodeContentItemFromDataset(DSRDocumentSubTree &tree,
                                                                DcmItem &dataset,
                                                                const DcmTagKey &tagKey,
                                                                const DSRCodedEntryValue &conceptName,
                                                                const OFString &annotationText,
                                                                const OFBool check)
{
    OFCondition result = EC_Normal;
    DSRCodedEntryValue codedEntry;
    /* get coded entry from code sequence in dataset */
    if (codedEntry.readSequence(dataset, tagKey, "3" /*type*/).good() && codedEntry.isComplete())
    {
        CHECK_RESULT(tree.addContentItem(RT_hasAcqContext, VT_Code, conceptName, check));
        CHECK_RESULT(tree.getCurrentContentItem().setCodeValue(codedEntry, check));
        if (!annotationText.empty())
            CHECK_RESULT(tree.getCurrentContentItem().setAnnotationText(annotationText));
    }
    return result;
}


OFCondition TID1600_ImageLibrary::addNumericContentItemFromDataset(DSRDocumentSubTree &tree,
                                                                   DcmItem &dataset,
                                                                   const DcmTagKey &tagKey,
                                                                   const signed long pos,
                                                                   const DSRCodedEntryValue &conceptName,
                                                                   const DSRCodedEntryValue &measurementUnit,
                                                                   const OFString &annotationText,
                                                                   const OFBool check)
{
    OFCondition result = EC_Normal;
    OFString numericValue;
    /* get element value from dataset (in text format) */
    if (getStringValueFromDataset(dataset, tagKey, numericValue, pos).good() && !numericValue.empty())
    {
        /* create new content item, set concept name and value */
        CHECK_RESULT(tree.addContentItem(RT_hasAcqContext, VT_Num, conceptName, check));
        CHECK_RESULT(tree.getCurrentContentItem().setNumericValue(DSRNumericMeasurementValue(numericValue, measurementUnit), check));
        if (!annotationText.empty())
            CHECK_RESULT(tree.getCurrentContentItem().setAnnotationText(annotationText));
    }
    return result;
}
