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


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

#include "dcmtk/dcmsr/cmr/tid1500.h"
#include "dcmtk/dcmsr/cmr/tid15def.h"
#include "dcmtk/dcmsr/cmr/logger.h"
#include "dcmtk/dcmsr/codes/dcm.h"
#include "dcmtk/dcmsr/codes/umls.h"
#include "dcmtk/dcmsr/dsrtpltn.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 MEASUREMENT_REPORT               0
#define OBSERVATION_CONTEXT              1
#define LAST_PROCEDURE_REPORTED          2
#define IMAGE_LIBRARY                    3
#define IMAGING_MEASUREMENTS             4
#define LAST_VOLUMETRIC_ROI_MEASUREMENTS 5
#define LAST_MEASUREMENT_GROUP           6
#define QUALITATIVE_EVALUATIONS          7
#define NUMBER_OF_LIST_ENTRIES           8

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


TID1500_MeasurementReport::TID1500_MeasurementReport(const CID7021_MeasurementReportDocumentTitles &title,
                                                     const OFBool check)
  : DSRRootTemplate(DT_EnhancedSR, TEMPLATE_NUMBER, MAPPING_RESOURCE, MAPPING_RESOURCE_UID),
    Language(new TID1204_LanguageOfContentItemAndDescendants()),
    ObservationContext(new TID1001_ObservationContext()),
    ImageLibrary(new TID1600_ImageLibrary(OFFalse /*createLibrary*/)),
    VolumetricROIMeasurements(new TID1411_Measurements(OFFalse /*createGroup*/)),
    MeasurementGroup(new TID1501_Measurements(OFFalse /*createGroup*/))
{
    setExtensible(TEMPLATE_TYPE);
    setOrderSignificant(TEMPLATE_ORDER);
    /* need to store position of various content items */
    reserveEntriesInNodeList(NUMBER_OF_LIST_ENTRIES, OFTrue /*initialize*/);
    /* if specified, create an initial report */
    if (title.hasSelectedValue())
        createMeasurementReport(title, check);
}


void TID1500_MeasurementReport::clear()
{
    DSRRootTemplate::clear();
    Language->clear();
    ObservationContext->clear();
    ImageLibrary->clear();
    VolumetricROIMeasurements->clear();
    MeasurementGroup->clear();
}


OFBool TID1500_MeasurementReport::isValid() const
{
    /* check whether base class is valid and all required content items are present */
    return DSRRootTemplate::isValid() &&
        ObservationContext->isValid() &&
        (hasImagingMeasurements() || hasQualitativeEvaluations()) &&
        (Language->isEmpty() || Language->isValid()) &&
        (ImageLibrary->isEmpty() || ImageLibrary->isValid()) &&
        (VolumetricROIMeasurements->isEmpty() || VolumetricROIMeasurements->isValid()) &&
        (MeasurementGroup->isEmpty() || MeasurementGroup->isValid());
}


OFBool TID1500_MeasurementReport::hasProcedureReported() const
{
    /* check for content item at TID 1500 (Measurement Report) Row 4 */
    return (getEntryFromNodeList(LAST_PROCEDURE_REPORTED) > 0);
}


OFBool TID1500_MeasurementReport::hasImageLibrary(const OFBool checkChildren) const
{
    OFBool result = OFFalse;
    /* need to check for child nodes? */
    if (checkChildren)
    {
        DSRDocumentTreeNodeCursor cursor(getRoot());
        /* go to content item at TID 1500 (Measurement Report) Row 5 */
        if (gotoEntryFromNodeList(cursor, IMAGE_LIBRARY) > 0)
        {
            /* check whether "included TID 1600 template" is non-empty */
            if (cursor.isValid() && (cursor.getNode()->getValueType() == VT_includedTemplate))
            {
                const DSRSubTemplate *subTempl = OFstatic_cast(const DSRIncludedTemplateTreeNode *, cursor.getNode())->getValue().get();
                if (subTempl != NULL)
                    result = !subTempl->isEmpty() && subTempl->compareTemplateIdentication("1600", "DCMR");
            }
        }
    } else {
        /* check for content item at TID 1500 (Measurement Report) Row 5 */
        result = (getEntryFromNodeList(IMAGE_LIBRARY) > 0);
    }
    return result;
}


OFBool TID1500_MeasurementReport::hasImagingMeasurements(const OFBool checkChildren) const
{
    OFBool result = OFFalse;
    /* need to check for child nodes? */
    if (checkChildren)
    {
        DSRDocumentTreeNodeCursor cursor(getRoot());
        /* go to content item at TID 1500 (Measurement Report) Row 6 */
        if (gotoEntryFromNodeList(cursor, IMAGING_MEASUREMENTS) > 0)
        {
            /* check whether any of the "included templates" is non-empty */
            if (cursor.gotoChild())
            {
                while (cursor.isValid() && (cursor.getNode()->getValueType() == VT_includedTemplate))
                {
                    const DSRSubTemplate *subTempl = OFstatic_cast(const DSRIncludedTemplateTreeNode *, cursor.getNode())->getValue().get();
                    if (subTempl != NULL)
                    {
                        result = !subTempl->isEmpty();
                        if (result) break;
                    }
                    if (cursor.gotoNext() == 0)
                    {
                        /* invalidate cursor */
                        cursor.clear();
                    }
                }
            }
        }
    } else {
        /* check for content item at TID 1500 (Measurement Report) Row 6 */
        result = (getEntryFromNodeList(IMAGING_MEASUREMENTS) > 0);
    }
    return result;
}


OFBool TID1500_MeasurementReport::hasVolumetricROIMeasurements(const OFBool checkChildren) const
{
    OFBool result = OFFalse;
    /* need to check for child nodes? */
    if (checkChildren)
    {
        DSRDocumentTreeNodeCursor cursor(getRoot());
        /* go to content item at TID 1500 (Measurement Report) Row 8 */
        if (gotoEntryFromNodeList(cursor, LAST_VOLUMETRIC_ROI_MEASUREMENTS) > 0)
        {
            /* check whether any of the "included TID 1411 templates" is non-empty */
            while (cursor.isValid() && (cursor.getNode()->getValueType() == VT_includedTemplate))
            {
                const DSRSubTemplate *subTempl = OFstatic_cast(const DSRIncludedTemplateTreeNode *, cursor.getNode())->getValue().get();
                if (subTempl != NULL)
                {
                    if (subTempl->compareTemplateIdentication("1411", "DCMR"))
                    {
                        result = !subTempl->isEmpty();
                        if (result) break;
                    } else {
                        /* exit loop */
                        break;
                    }
                }
                if (cursor.gotoPrevious() == 0)
                {
                    /* invalidate cursor */
                    cursor.clear();
                }
            }
        }
    } else {
        /* check for content item at TID 1500 (Measurement Report) Row 8 */
        result = (getEntryFromNodeList(LAST_VOLUMETRIC_ROI_MEASUREMENTS) > 0);
    }
    return result;
}


OFBool TID1500_MeasurementReport::hasIndividualMeasurements(const OFBool checkChildren) const
{
    OFBool result = OFFalse;
    /* need to check for child nodes? */
    if (checkChildren)
    {
        DSRDocumentTreeNodeCursor cursor(getRoot());
        /* go to content item at TID 1500 (Measurement Report) Row 9 */
        if (gotoEntryFromNodeList(cursor, LAST_MEASUREMENT_GROUP) > 0)
        {
            /* check whether any of the "included TID 1501 templates" is non-empty */
            while (cursor.isValid() && (cursor.getNode()->getValueType() == VT_includedTemplate))
            {
                const DSRSubTemplate *subTempl = OFstatic_cast(const DSRIncludedTemplateTreeNode *, cursor.getNode())->getValue().get();
                if (subTempl != NULL)
                {
                    if (subTempl->compareTemplateIdentication("1501", "DCMR"))
                    {
                        result = !subTempl->isEmpty();
                        if (result) break;
                    } else {
                        /* exit loop */
                        break;
                    }
                }
                if (cursor.gotoPrevious() == 0)
                {
                    /* invalidate cursor */
                    cursor.clear();
                }
            }
        }
    } else {
        /* check for content item at TID 1500 (Measurement Report) Row 9 */
        result = (getEntryFromNodeList(LAST_MEASUREMENT_GROUP) > 0);
    }
    return result;
}


OFBool TID1500_MeasurementReport::hasQualitativeEvaluations(const OFBool checkChildren) const
{
    OFBool result = OFFalse;
    /* need to check for child nodes? */
    if (checkChildren)
    {
        DSRDocumentTreeNodeCursor cursor(getRoot());
        /* go to content item at TID 1500 (Measurement Report) Row 12 */
        if (gotoEntryFromNodeList(cursor, QUALITATIVE_EVALUATIONS) > 0)
            result = cursor.hasChildNodes();
    } else {
        /* check for content item at TID 1500 (Measurement Report) Row 12 */
        result = (getEntryFromNodeList(QUALITATIVE_EVALUATIONS) > 0);
    }
    return result;
}


OFCondition TID1500_MeasurementReport::getDocumentTitle(DSRCodedEntryValue &titleCode)
{
    OFCondition result = EC_Normal;
    /* go to content item at TID 1500 (Measurement Report) Row 1 */
    if (gotoEntryFromNodeList(this, MEASUREMENT_REPORT) > 0)
    {
        titleCode = getCurrentContentItem().getConceptName();
    } else {
        /* in case of error, clear the result variable */
        titleCode.clear();
        result = SR_EC_ContentItemNotFound;
    }
    return result;
}


OFCondition TID1500_MeasurementReport::createNewMeasurementReport(const CID7021_MeasurementReportDocumentTitles &title,
                                                                  const OFBool check)
{
    clear();
    /* TID 1500 (Measurement Report) Row 1 ff. */
    return createMeasurementReport(title, check);
}


OFCondition TID1500_MeasurementReport::setLanguage(const CID5000_Languages &language,
                                                   const CID5001_Countries &country,
                                                   const OFBool check)
{
    /* TID 1500 (Measurement Report) Row 2 */
    return getLanguage().setLanguage(language, country, check);
}


OFCondition TID1500_MeasurementReport::addProcedureReported(const CID100_QuantitativeDiagnosticImagingProcedures &procedure,
                                                            const OFBool check)
{
    OFCondition result = EC_IllegalParameter;
    /* make sure that there is a coded entry */
    if (procedure.hasSelectedValue())
    {
        /* go to last content item at TID 1500 (Measurement Report) Row 3 or 4 */
        if (gotoLastEntryFromNodeList(this, LAST_PROCEDURE_REPORTED, OBSERVATION_CONTEXT) > 0)
        {
            /* TID 1500 (Measurement Report) Row 4 */
            STORE_RESULT(addContentItem(RT_hasConceptMod, VT_Code, CODE_DCM_ProcedureReported, check));
            CHECK_RESULT(getCurrentContentItem().setCodeValue(procedure, check));
            CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1500 - Row 4"));
            /* store ID of recently added node for later use */
            GOOD_RESULT(storeEntryInNodeList(LAST_PROCEDURE_REPORTED, getNodeID()));
        } else
            result = CMR_EC_NoMeasurementReport;
    }
    return result;
}


OFCondition TID1500_MeasurementReport::addVolumetricROIMeasurements(const OFBool checkEmpty)
{
    OFCondition result = EC_Normal;
    /* go to content item at TID 1500 (Measurement Report) Row 8 */
    if (gotoEntryFromNodeList(this, LAST_VOLUMETRIC_ROI_MEASUREMENTS) > 0)
    {
        /* check whether the current instance of TID 1411 is non-empty (if needed) */
        if (!checkEmpty || !VolumetricROIMeasurements->isEmpty())
        {
            /* create new instance of TID 1411 (Volumetric ROI Measurements) */
            TID1411_Measurements *subTempl = new TID1411_Measurements(OFFalse /*createGroup*/);
            if (subTempl != NULL)
            {
                /* store (shared) reference to new instance */
                VolumetricROIMeasurements.reset(subTempl);
                /* and add it to the current template (TID 1500 - Row 8) */
                STORE_RESULT(includeTemplate(VolumetricROIMeasurements, AM_afterCurrent, RT_contains));
                CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1500 - Row 8"));
                GOOD_RESULT(storeEntryInNodeList(LAST_VOLUMETRIC_ROI_MEASUREMENTS, getNodeID()));
                /* tbc: what if the call of includeTemplate() fails? */
            } else
                result = EC_MemoryExhausted;
        }
    } else
        result = CMR_EC_NoMeasurementReport;
    return result;
}


OFCondition TID1500_MeasurementReport::addIndividualMeasurements(const OFBool checkEmpty)
{
    OFCondition result = EC_Normal;
    /* go to content item at TID 1500 (Measurement Report) Row 9 */
    if (gotoEntryFromNodeList(this, LAST_MEASUREMENT_GROUP) > 0)
    {
        /* check whether the current instance of TID 1501 is non-empty (if needed) */
        if (!checkEmpty || !MeasurementGroup->isEmpty())
        {
            /* create new instance of TID 1501 (Measurement Group) */
            TID1501_Measurements *subTempl = new TID1501_Measurements(OFFalse /*createGroup*/);
            if (subTempl != NULL)
            {
                /* store (shared) reference to new instance */
                MeasurementGroup.reset(subTempl);
                /* and add it to the current template (TID 1500 - Row 9) */
                STORE_RESULT(includeTemplate(MeasurementGroup, AM_afterCurrent, RT_contains));
                CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1500 - Row 9"));
                GOOD_RESULT(storeEntryInNodeList(LAST_MEASUREMENT_GROUP, getNodeID()));
                /* tbc: what if the call of includeTemplate() fails? */
            } else
                result = EC_MemoryExhausted;
        }
    } else
        result = CMR_EC_NoMeasurementReport;
    return result;
}


OFCondition TID1500_MeasurementReport::addQualitativeEvaluation(const DSRCodedEntryValue &conceptName,
                                                                const DSRCodedEntryValue &codeValue,
                                                                const OFBool check)
{
    OFCondition result = EC_IllegalParameter;
    /* make sure that the parameters are non-empty */
    if (conceptName.isComplete() && codeValue.isComplete())
    {
        /* create content item at TID 1500 (Measurement Report) Row 12 if not existing */
        result = createQualitativeEvaluations();
        /* go to content item at TID 1500 (Measurement Report) Row 12 */
        if (gotoEntryFromNodeList(this, QUALITATIVE_EVALUATIONS) > 0)
        {
            /* TID 1500 (Measurement Report) Row 13 */
            CHECK_RESULT(addChildContentItem(RT_contains, VT_Code, conceptName, check));
            CHECK_RESULT(getCurrentContentItem().setCodeValue(codeValue, check));
            CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1500 - Row 13"));
        } else
            result = CMR_EC_NoMeasurementReport;
    }
    return result;
}


OFCondition TID1500_MeasurementReport::addQualitativeEvaluation(const DSRCodedEntryValue &conceptName,
                                                                const OFString &stringValue,
                                                                const OFBool check)
{
    OFCondition result = EC_IllegalParameter;
    /* make sure that the parameters are non-empty */
    if (conceptName.isComplete() && !stringValue.empty())
    {
        /* create content item at TID 1500 (Measurement Report) Row 12 if not existing */
        result = createQualitativeEvaluations();
        /* go to content item at TID 1500 (Measurement Report) Row 12 */
        if (gotoEntryFromNodeList(this, QUALITATIVE_EVALUATIONS) > 0)
        {
            /* TID 1500 (Measurement Report) Row 14 */
            CHECK_RESULT(addChildContentItem(RT_contains, VT_Text, conceptName, check));
            CHECK_RESULT(getCurrentContentItem().setStringValue(stringValue, check));
            CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1500 - Row 14"));
        } else
            result = CMR_EC_NoMeasurementReport;
    }
    return result;
}


// protected methods

OFCondition TID1500_MeasurementReport::createMeasurementReport(const CID7021_MeasurementReportDocumentTitles &title,
                                                               const OFBool check)
{
    OFCondition result = EC_IllegalParameter;
    /* make sure that there is a coded entry */
    if (title.hasSelectedValue())
    {
        /* reassure that the report is definitely empty */
        if (isEmpty())
        {
            /* TID 1500 (Measurement Report) Row 1 */
            STORE_RESULT(addContentItem(RT_isRoot, VT_Container, title, check));
            CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1500 - Row 1"));
            /* store ID of root node for later use */
            GOOD_RESULT(storeEntryInNodeList(MEASUREMENT_REPORT, getNodeID()));
            /* TID 1500 (Measurement Report) Row 2 */
            CHECK_RESULT(includeTemplate(Language, AM_belowCurrent, RT_hasConceptMod));
            CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1500 - Row 2"));
            /* TID 1500 (Measurement Report) Row 3 */
            CHECK_RESULT(includeTemplate(ObservationContext, AM_afterCurrent, RT_hasObsContext));
            CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1500 - Row 3"));
            GOOD_RESULT(storeEntryInNodeList(OBSERVATION_CONTEXT, getNodeID()));
            /* TID 1500 (Measurement Report) Row 5 */
            CHECK_RESULT(includeTemplate(ImageLibrary, AM_afterCurrent, RT_contains));
            CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1500 - Row 5"));
            GOOD_RESULT(storeEntryInNodeList(IMAGE_LIBRARY, getNodeID()));
            /* TID 1500 (Measurement Report) Row 6 */
            CHECK_RESULT(addContentItem(RT_contains, VT_Container, CODE_DCM_ImagingMeasurements));
            CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1500 - Row 6"));
            GOOD_RESULT(storeEntryInNodeList(IMAGING_MEASUREMENTS, getNodeID()));
            /* TID 1500 (Measurement Report) Row 8 */
            CHECK_RESULT(includeTemplate(VolumetricROIMeasurements, AM_belowCurrent, RT_contains));
            CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1500 - Row 8"));
            GOOD_RESULT(storeEntryInNodeList(LAST_VOLUMETRIC_ROI_MEASUREMENTS, getNodeID()));
            /* TID 1500 (Measurement Report) Row 9 */
            CHECK_RESULT(includeTemplate(MeasurementGroup, AM_afterCurrent, RT_contains));
            CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1500 - Row 9"));
            GOOD_RESULT(storeEntryInNodeList(LAST_MEASUREMENT_GROUP, getNodeID()));
            /* if anything went wrong, clear the report */
            BAD_RESULT(clear());
        } else
            result = SR_EC_InvalidTemplateStructure;
    }
    return result;
}


OFCondition TID1500_MeasurementReport::createQualitativeEvaluations()
{
    OFCondition result = EC_Normal;
    /* check whether content item at TID 1500 (Measurement Report) Row 12 already exists */
    if (!hasQualitativeEvaluations())
    {
        /* if not, go to the preceding content item, which always exists */
        if (gotoEntryFromNodeList(this, IMAGING_MEASUREMENTS) > 0)
        {
            /* ... and add TID 1500 (Measurement Report) Row 12 */
            STORE_RESULT(addContentItem(RT_contains, VT_Container, CODE_UMLS_QualitativeEvaluations));
            CHECK_RESULT(getCurrentContentItem().setAnnotationText("TID 1500 - Row 12"));
            GOOD_RESULT(storeEntryInNodeList(QUALITATIVE_EVALUATIONS, getNodeID()));
        } else
            result = CMR_EC_NoMeasurementReport;
    }
    return result;
}
