/*
 *
 *  Copyright (C) 1998-2024, OFFIS e.V.
 *  All rights reserved.  See COPYRIGHT file for details.
 *
 *  This software and supporting documentation were developed by
 *
 *    OFFIS e.V.
 *    R&D Division Health
 *    Escherweg 2
 *    D-26121 Oldenburg, Germany
 *
 *
 *  Module: dcmpstat
 *
 *  Author: Marco Eichelberg
 *
 *  Purpose:
 *    classes: DVPSOverlayCurveActivationLayer_PList
 *
 */

#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
#include "dcmtk/dcmpstat/dvpsall.h"
#include "dcmtk/ofstd/ofstring.h"
#include "dcmtk/ofstd/ofstd.h"
#include "dcmtk/dcmpstat/dvpsal.h"      /* for DVPSOverlayCurveActivationLayer */
#include "dcmtk/dcmpstat/dvpsovl.h"     /* for DVPSOverlay_PList */
#include "dcmtk/dcmpstat/dvpsgll.h"     /* for DVPSGraphicLayer_PList */
#include "dcmtk/dcmpstat/dvpsdef.h"     /* for constants and macros */
#include "dcmtk/dcmpstat/dvpsov.h"      /* for DVPSOverlay, needed by MSVC5 with STL */
#include "dcmtk/dcmpstat/dvpsgl.h"      /* for DVPSGraphicLayer, needed by MSVC5 with STL */

DVPSOverlayCurveActivationLayer_PList::DVPSOverlayCurveActivationLayer_PList()
: list_()
{
}

DVPSOverlayCurveActivationLayer_PList::DVPSOverlayCurveActivationLayer_PList(const DVPSOverlayCurveActivationLayer_PList &arg)
: list_()
{
  OFListConstIterator(DVPSOverlayCurveActivationLayer *) first = arg.list_.begin();
  OFListConstIterator(DVPSOverlayCurveActivationLayer *) last = arg.list_.end();
  while (first != last)
  {
    list_.push_back((*first)->clone());
    ++first;
  }
}

DVPSOverlayCurveActivationLayer_PList::~DVPSOverlayCurveActivationLayer_PList()
{
  clear();
}

void DVPSOverlayCurveActivationLayer_PList::clear()
{
  OFListIterator(DVPSOverlayCurveActivationLayer *) first = list_.begin();
  OFListIterator(DVPSOverlayCurveActivationLayer *) last = list_.end();
  while (first != last)
  {
    delete (*first);
    first = list_.erase(first);
  }
}

OFCondition DVPSOverlayCurveActivationLayer_PList::read(DcmItem &dset)
{
  OFCondition result = EC_Normal;
  DcmStack stack;
  DcmTagKey key(DCM_RETIRED_CurveActivationLayer);
  DVPSOverlayCurveActivationLayer *newLayer = NULL;

  Uint16 i = 0x5000;
  while (i < 0x6020)
  {
    if (result==EC_Normal)
    {
      stack.clear();
      key.setGroup(i);
      if (EC_Normal == dset.search(key, stack, ESM_fromHere, OFFalse))
      {
        newLayer = new DVPSOverlayCurveActivationLayer();
        if (newLayer)
        {
          result = newLayer->read(dset,i);
          list_.push_back(newLayer);
        } else result = EC_MemoryExhausted;
      }
    }
    i += 2;
    if (i == 0x5020) i = 0x6000;
  }
  return result;
}


OFCondition DVPSOverlayCurveActivationLayer_PList::write(DcmItem &dset)
{
  OFCondition result = EC_Normal;
  OFListIterator(DVPSOverlayCurveActivationLayer *) first = list_.begin();
  OFListIterator(DVPSOverlayCurveActivationLayer *) last = list_.end();
  while (first != last)
  {
    if (result==EC_Normal) result = (*first)->write(dset);
    ++first;
  }
  return result;
}

OFCondition DVPSOverlayCurveActivationLayer_PList::createFromImage(
    DcmItem &dset,
    DVPSGraphicLayer_PList &gLayerList,
    DVPSOverlay_PList &overlayList,
    DVPSoverlayActivation overlayActivation,
    OFBool                curveActivation,
    DVPSGraphicLayering   layering)
{
  OFCondition result = EC_Normal;
  long currentLayer = 0;
  long lastOverlayLayer = 0;
  char layerName[100];
  char layerDesc[100];
  OFString aString;
  Uint16 dimensions;

  OFBool found;
  DcmStack stack;
  DcmTagKey overlayRows(DCM_OverlayRows);
  DcmTagKey overlayColumns(DCM_OverlayColumns);
  DcmTagKey overlayType(DCM_OverlayType);
  DcmTagKey overlayOrigin(DCM_OverlayOrigin);
  DcmTagKey overlayBitsAllocated(DCM_OverlayBitsAllocated);
  DcmTagKey overlayBitPosition(DCM_OverlayBitPosition);

  DcmTagKey curveDimensions(DCM_RETIRED_CurveDimensions);
  DcmTagKey numberOfPoints(DCM_RETIRED_NumberOfPoints);
  DcmTagKey typeOfData(DCM_RETIRED_TypeOfData);
  DcmTagKey dataValueRepresentation(DCM_RETIRED_DataValueRepresentation);
  DcmTagKey curveData(DCM_RETIRED_CurveData);

  OFBool haveOverlays = OFFalse;
  /* first we handle overlays */
  if ((overlayActivation==DVPSO_referenceOverlays) || (overlayActivation==DVPSO_copyOverlays))
  {
    for (Uint16 group = 0x6000; ((result==EC_Normal)&&(group < 0x6020)); group += 2)
    {
      found = OFFalse;
      /* check if we have an internal overlay with this group */
      if (overlayList.haveOverlayGroup(group)) found=OFTrue;

      /* otherwise check if we have an external overlay with this group */
      if (!found)
      {
        overlayRows.setGroup(group);
        overlayColumns.setGroup(group);
        overlayType.setGroup(group);
        overlayOrigin.setGroup(group);
        overlayBitsAllocated.setGroup(group);
        overlayBitPosition.setGroup(group);
        stack.clear();
        if (EC_Normal == dset.search(overlayRows, stack, ESM_fromHere, OFFalse)) found = OFTrue;
        stack.clear();
        if (EC_Normal != dset.search(overlayColumns, stack, ESM_fromHere, OFFalse)) found = OFFalse;
        stack.clear();
        if (EC_Normal != dset.search(overlayType, stack, ESM_fromHere, OFFalse)) found = OFFalse;
        stack.clear();
        if (EC_Normal != dset.search(overlayOrigin, stack, ESM_fromHere, OFFalse)) found = OFFalse;
        stack.clear();
        if (EC_Normal != dset.search(overlayBitsAllocated, stack, ESM_fromHere, OFFalse)) found = OFFalse;
        stack.clear();
        if (EC_Normal != dset.search(overlayBitPosition, stack, ESM_fromHere, OFFalse)) found = OFFalse;
      }

      /* if found, create graphic layer if necessary. Create activation. */
      if (found)
      {
        switch (layering)
        {
          case DVPSG_oneLayer:
            if (currentLayer==0)
            {
              currentLayer++;
              OFStandard::snprintf(layerName, sizeof(layerName), "LAYER");
              result = gLayerList.addGraphicLayer(layerName, 1, "Overlays and Curves");
            }
            break;
          case DVPSG_twoLayers:
            if (currentLayer==0)
            {
              currentLayer++;
              OFStandard::snprintf(layerName, sizeof(layerName), "OVERLAY");
              result = gLayerList.addGraphicLayer(layerName, 1, "Overlays");
            }
            break;
          case DVPSG_separateLayers:
            currentLayer++;
            OFStandard::snprintf(layerName, sizeof(layerName), "OVERLAY%04ld", (long)currentLayer);
            OFStandard::snprintf(layerDesc, sizeof(layerDesc), "Overlay Layer %ld", (long)currentLayer);
            result = gLayerList.addGraphicLayer(layerName, currentLayer, layerDesc);
            break;
        }
        DVPSOverlayCurveActivationLayer *newLayer = new DVPSOverlayCurveActivationLayer();
        if (newLayer)
        {
          newLayer->setActivationLayer(layerName);
          newLayer->setRepeatingGroup(group);
          list_.push_back(newLayer);
          haveOverlays = OFTrue;
        } else result = EC_MemoryExhausted;
      }
    }
  }

  lastOverlayLayer = currentLayer;
  /* then we handle curves */
  if (curveActivation)
  {
    for (Uint16 group = 0x5000; ((result==EC_Normal)&&(group < 0x5020)); group += 2)
    {
      found = OFFalse;

      /* check if we have an external curve with this group */
      curveDimensions.setGroup(group);
      numberOfPoints.setGroup(group);
      typeOfData.setGroup(group);
      dataValueRepresentation.setGroup(group);
      curveData.setGroup(group);
      stack.clear();
      if (EC_Normal == dset.search(curveDimensions, stack, ESM_fromHere, OFFalse)) found = OFTrue;
      stack.clear();
      if (EC_Normal != dset.search(numberOfPoints, stack, ESM_fromHere, OFFalse)) found = OFFalse;
      stack.clear();
      if (EC_Normal != dset.search(typeOfData, stack, ESM_fromHere, OFFalse)) found = OFFalse;
      stack.clear();
      if (EC_Normal != dset.search(dataValueRepresentation, stack, ESM_fromHere, OFFalse)) found = OFFalse;
      stack.clear();
      if (EC_Normal != dset.search(curveData, stack, ESM_fromHere, OFFalse)) found = OFFalse;

      /* if we have found a curve, make sure that this is a type of curve we can display */
      if (found)
      {
        /* read the curve dimensions and curve type from the dataset */
        DcmUnsignedShort curveDimensionsValue(DCM_RETIRED_CurveDimensions);
        DcmCodeString typeOfDataValue(DCM_RETIRED_TypeOfData);
        curveDimensionsValue.setGTag(group);
        typeOfDataValue.setGTag(group);
        READ_FROM_DATASET(DcmUnsignedShort, EVR_US, curveDimensionsValue)
        READ_FROM_DATASET(DcmCodeString, EVR_CS, typeOfDataValue)

        /* we can only display POLY and ROI curves */
        aString.clear();
        typeOfDataValue.getOFString(aString,0);
        if ((aString != "POLY")&&(aString != "ROI")) found=OFFalse;

        /* we can only display 2D curves */
        dimensions=0;
        curveDimensionsValue.getUint16(dimensions,0);
        if (dimensions != 2) found=OFFalse;
      }

      /* if found, create graphic layer if necessary. Create activation. */
      if (found)
      {
        switch (layering)
        {
          case DVPSG_oneLayer:
            if (currentLayer==0)
            {
              currentLayer++;
              OFStandard::snprintf(layerName, sizeof(layerName), "LAYER");
              result = gLayerList.addGraphicLayer(layerName, 1, "Overlays and Curves");
            }
            break;
          case DVPSG_twoLayers:
            if ((currentLayer==0)||((currentLayer==1)&&(haveOverlays)))
            {
              currentLayer++;
              OFStandard::snprintf(layerName, sizeof(layerName), "CURVE");
              result = gLayerList.addGraphicLayer(layerName, 1, "Curves");
            }
            break;
          case DVPSG_separateLayers:
            currentLayer++;
            OFStandard::snprintf(layerName, sizeof(layerName), "CURVE%04ld", (long)currentLayer-lastOverlayLayer);
            OFStandard::snprintf(layerDesc, sizeof(layerDesc), "Curve Layer %ld", (long)currentLayer-lastOverlayLayer);
            result = gLayerList.addGraphicLayer(layerName, currentLayer, layerDesc);
            break;
        }
        DVPSOverlayCurveActivationLayer *newLayer = new DVPSOverlayCurveActivationLayer();
        if (newLayer)
        {
          newLayer->setActivationLayer(layerName);
          newLayer->setRepeatingGroup(group);
          list_.push_back(newLayer);
        } else result = EC_MemoryExhausted;
      }
    }
  }

  return result;
}

OFCondition DVPSOverlayCurveActivationLayer_PList::setActivation(Uint16 group, const char *layer)
{
  /* first we make sure we have a valid overlay group */
  OFBool result = OFFalse;
  if ((group < 0x6020)&&(group >= 0x6000)&&((group & 1) == 0)) result = OFTrue;
  if ((group < 0x5020)&&(group >= 0x5000)&&((group & 1) == 0)) result = OFTrue;
  if (!result) return EC_IllegalCall;
  if (layer==NULL) return EC_IllegalCall;

  OFListIterator(DVPSOverlayCurveActivationLayer *) first = list_.begin();
  OFListIterator(DVPSOverlayCurveActivationLayer *) last = list_.end();
  while (first != last)
  {
    if ((*first)->isRepeatingGroup(group))
    {
      (*first)->setActivationLayer(layer);
      return EC_Normal;
    }
    ++first;
  }
  DVPSOverlayCurveActivationLayer * newLayer = new DVPSOverlayCurveActivationLayer();
  if (newLayer)
  {
    newLayer->setActivationLayer(layer);
    newLayer->setRepeatingGroup(group);
    list_.push_back(newLayer);
    return EC_Normal;
  }
  return EC_MemoryExhausted;
}

void DVPSOverlayCurveActivationLayer_PList::removeActivation(Uint16 group)
{
  OFListIterator(DVPSOverlayCurveActivationLayer *) first = list_.begin();
  OFListIterator(DVPSOverlayCurveActivationLayer *) last = list_.end();
  while (first != last)
  {
    if ((*first)->isRepeatingGroup(group))
    {
      delete (*first);
      first = list_.erase(first);
    } else ++first;
  }
}

const char *DVPSOverlayCurveActivationLayer_PList::getActivationLayer(Uint16 group)
{
  OFListIterator(DVPSOverlayCurveActivationLayer *) first = list_.begin();
  OFListIterator(DVPSOverlayCurveActivationLayer *) last = list_.end();
  while (first != last)
  {
    if ((*first)->isRepeatingGroup(group)) return (*first)->getActivationLayer();
    ++first;
  }
  return NULL;
}

void DVPSOverlayCurveActivationLayer_PList::renameLayer(const char *oldName, const char *newName)
{
  if ((oldName==NULL)||(newName==NULL)) return;

  OFString aString(oldName);
  OFListIterator(DVPSOverlayCurveActivationLayer *) first = list_.begin();
  OFListIterator(DVPSOverlayCurveActivationLayer *) last = list_.end();
  while (first != last)
  {
    const char *activationLayer = (*first)->getActivationLayer();
    if (activationLayer && (aString == activationLayer))
    {
      (*first)->setActivationLayer(newName);
    }
    ++first;
  }
  return;
}

void DVPSOverlayCurveActivationLayer_PList::removeLayer(const char *name)
{
  if (name==NULL) return;

  OFString aString(name);
  OFListIterator(DVPSOverlayCurveActivationLayer *) first = list_.begin();
  OFListIterator(DVPSOverlayCurveActivationLayer *) last = list_.end();
  while (first != last)
  {
    const char *activationLayer = (*first)->getActivationLayer();
    if (activationLayer && (aString == activationLayer))
    {
      delete (*first);
      first = list_.erase(first);
    } else ++first;
  }
  return;
}

OFBool DVPSOverlayCurveActivationLayer_PList::usesLayerName(const char *name)
{
  if (name==NULL) return OFFalse;

  OFString aString(name);
  OFListIterator(DVPSOverlayCurveActivationLayer *) first = list_.begin();
  OFListIterator(DVPSOverlayCurveActivationLayer *) last = list_.end();
  while (first != last)
  {
    const char *activationLayer = (*first)->getActivationLayer();
    if (activationLayer && (aString == activationLayer)) return OFTrue;
    ++first;
  }
  return OFFalse;
}

size_t DVPSOverlayCurveActivationLayer_PList::getNumberOfActivations(const char *layer, OFBool isCurve)
{
  if (layer==NULL) return 0;

  size_t result = 0;
  Uint16 group;
  OFString aString(layer);
  OFListIterator(DVPSOverlayCurveActivationLayer *) first = list_.begin();
  OFListIterator(DVPSOverlayCurveActivationLayer *) last = list_.end();
  while (first != last)
  {
    const char *activationLayer = (*first)->getActivationLayer();
    if (activationLayer && (aString == activationLayer))
    {
      group = (*first)->getRepeatingGroup();
      if (((isCurve) && (group < 0x6000)) || ((!isCurve) && (group >= 0x6000))) result++;
    }
    ++first;
  }
  return result;
}


Uint16 DVPSOverlayCurveActivationLayer_PList::getActivationGroup(const char *layer, size_t idx, OFBool isCurve)
{
  if (layer==NULL) return 0;

  Uint16 group;
  OFString aString(layer);
  OFListIterator(DVPSOverlayCurveActivationLayer *) first = list_.begin();
  OFListIterator(DVPSOverlayCurveActivationLayer *) last = list_.end();
  while (first != last)
  {
    const char *activationLayer = (*first)->getActivationLayer();
    if (activationLayer && (aString == activationLayer))
    {
      group = (*first)->getRepeatingGroup();
      if (((isCurve) && (group < 0x6000)) || ((!isCurve) && (group >= 0x6000)))
      {
        if (idx==0) return group; else idx--;
      }
    }
    ++first;
  }
  return 0;
}
