#include "LayerTableRowModel.h"
#include "GlobalUIModel.h"
#include "IRISApplication.h"
#include "GenericImageData.h"
#include "ImageWrapperBase.h"
#include "NumericPropertyToggleAdaptor.h"
#include "DisplayMappingPolicy.h"
#include "DisplayLayoutModel.h"
#include "ImageIOWizardModel.h"
#include "ColorMapModel.h"
#include "IntensityCurveModel.h"
#include "LayerGeneralPropertiesModel.h"
#include "IncreaseDimensionImageFilter.h"
#include "SNAPImageData.h"
#include "ImageMeshLayers.h"
#include "StandaloneMeshWrapper.h"
#include "MomentTextures.h"
#include "SegmentationMeshWrapper.h"
#include "GuidedNativeImageIO.h"
#include "MeshWrapperBase.h"

AbstractLayerTableRowModel::AbstractLayerTableRowModel()
{
  m_LayerOpacityModel = wrapGetterSetterPairAsProperty(
        this,
        &Self::GetLayerOpacityValueAndRange,
        &Self::SetLayerOpacityValue);

  m_NicknameModel = wrapGetterSetterPairAsProperty(
        this,
        &Self::GetNicknameValue,
        &Self::SetNicknameValue);

  m_StickyModel = wrapGetterSetterPairAsProperty(
        this,
        &Self::GetStickyValue,
        &Self::SetStickyValue);

  m_ColorMapPresetModel = wrapGetterSetterPairAsProperty(
        this,
        &Self::GetColorMapPresetValue,
        &Self::SetColorMapPresetValue);


  m_VisibilityToggleModel = NewNumericPropertyToggleAdaptor(
        m_LayerOpacityModel.GetPointer(), 0, 50);


  m_LayerRole = NO_ROLE;
  m_LayerPositionInRole = -1;
  m_ImageData = NULL;
}

bool AbstractLayerTableRowModel::CheckState(UIState state)
{
  // Are we in tiling mode?
  /* commenting out unused code to avoid warnings
  bool tiling = (
        m_ParentModel->GetGlobalState()->GetSliceViewLayerLayout() ==
        LAYOUT_TILED); */

  bool snapmode = m_ParentModel->GetDriver()->IsSnakeModeActive();

  // Check the states
  switch(state)
    {
    case AbstractLayerTableRowModel::UIF_MOVABLE_UP:
      return (m_LayerRole == OVERLAY_ROLE && m_LayerPositionInRole > 0);

    case AbstractLayerTableRowModel::UIF_MOVABLE_DOWN:
      return (m_LayerRole == OVERLAY_ROLE && m_LayerPositionInRole < m_LayerNumberOfLayersInRole - 1);

    case AbstractLayerTableRowModel::UIF_CLOSABLE:
      return !snapmode;

    case AbstractLayerTableRowModel::UIF_FILE_RELOADABLE:
      return (m_LayerRole != MESH_ROLE &&
          !(strcmp(m_Layer->GetFileName(), "") == 0));

    case AbstractLayerTableRowModel::UIF_FILE_REVEALABLE:
      return (m_Layer && !(strcmp(m_Layer->GetFileName(), "") == 0));

    default:
      break;
    }

  return false;
}


void AbstractLayerTableRowModel::Initialize(GlobalUIModel *parentModel, WrapperBase *layer)
{
  m_ParentModel = parentModel;
  m_Layer = layer;
  m_ImageData = parentModel->GetDriver()->GetCurrentImageData();

  // For some of the functions, it is useful to know the role and the index
  // in the role of this layer. We shouldn't have to worry about this info
  // changing, since the rows get rebuilt when LayerChangeEvent() is fired.
  UpdateRoleInfo();

  // Listen to cosmetic events from the layer
  Rebroadcast(layer, WrapperMetadataChangeEvent(), ModelUpdateEvent());
  Rebroadcast(layer, WrapperDisplayMappingChangeEvent(), ModelUpdateEvent());

  // What happens if the layer is deleted? The model should be notified, and
  // it should update its state to a NULL state before something bad happens
  // in the GUI...
  Rebroadcast(layer, itk::DeleteEvent(), ModelUpdateEvent());

  // The state of this model only depends on wrapper's position in the list of
  // layers, not on the wrapper metadata
  Rebroadcast(m_ParentModel->GetDriver(), LayerChangeEvent(),
              StateMachineChangeEvent());

  // The state also depends on the current tiling mode
  Rebroadcast(m_ParentModel->GetDisplayLayoutModel()->GetSliceViewLayerLayoutModel(),
              ValueChangedEvent(),
              StateMachineChangeEvent());

  Rebroadcast(layer, WrapperMetadataChangeEvent(), StateMachineChangeEvent());
}

bool AbstractLayerTableRowModel::IsMainLayer()
{
  return m_LayerRole == MAIN_ROLE;
}

bool AbstractLayerTableRowModel::GetNicknameValue(std::string &value)
{
  if(!m_Layer) return false;

  value = m_Layer->GetNickname();
  return true;
}

void AbstractLayerTableRowModel::SetNicknameValue(std::string value)
{
  m_Layer->SetCustomNickname(value);
}


void AbstractLayerTableRowModel::OnUpdate()
{
  // Has our layer been deleted?
  if(this->m_EventBucket->HasEvent(itk::DeleteEvent(), m_Layer))
    {
    m_Layer = NULL;
    m_LayerRole = NO_ROLE;
    m_LayerPositionInRole = -1;
    m_LayerNumberOfLayersInRole = -1;
    }
  else if(this->m_EventBucket->HasEvent(LayerChangeEvent()))
    {
    this->UpdateRoleInfo();
    }
}



bool AbstractLayerTableRowModel::GetLayerOpacityValueAndRange(int &value, NumericValueRange<int> *domain)
{
  // For opacity to be defined, the layer must be sticky
  if(!m_Layer) return false;

  // Meaning of 'visible' is different for sticky and non-sticky layers
  value = (int)(100.0 * m_Layer->GetAlpha());

  if(domain)
    domain->Set(0, 100, 5);

  return true;
}
 void AbstractLayerTableRowModel::SetLayerOpacityValue(int value)
{
  assert(m_Layer);
  m_Layer->SetAlpha(value / 100.0);
}



/*
 * ===============================================
 *   ImageLayerTableRowModel Implementation
 * ===============================================
*/

ImageLayerTableRowModel::ImageLayerTableRowModel()
{
  m_ComponentNameModel = wrapGetterSetterPairAsProperty(
        this,
        &Self::GetComponentNameValue);

  m_DisplayModeModel = wrapGetterSetterPairAsProperty(
        this,
        &Self::GetDisplayModeValue,
        &Self::SetDisplayModeValue);

  m_ColorMapPresetModel = wrapGetterSetterPairAsProperty(
        this,
        &Self::GetColorMapPresetValue,
        &Self::SetColorMapPresetValue);

  m_VolumeRenderingEnabledModel = wrapGetterSetterPairAsProperty(
        this,
        &Self::GetVolumeRenderingEnabledValue,
        &Self::SetVolumeRenderingEnabledValue);
}

void ImageLayerTableRowModel::UpdateDisplayModeList()
{
  m_AvailableDisplayModes.clear();
  if(m_Layer && m_ImageLayer->GetNumberOfComponents() > 1)
    {
    for(std::size_t i = 0; i < m_ImageLayer->GetNumberOfComponents(); i++)
      m_AvailableDisplayModes.push_back(MultiChannelDisplayMode(false, false, SCALAR_REP_COMPONENT, i));

    m_AvailableDisplayModes.push_back(
          MultiChannelDisplayMode(false, false, SCALAR_REP_MAGNITUDE, 0));
    m_AvailableDisplayModes.push_back(
          MultiChannelDisplayMode(false, false, SCALAR_REP_MAX, 0));
    m_AvailableDisplayModes.push_back(
          MultiChannelDisplayMode(false, false, SCALAR_REP_AVERAGE, 0));

    int nc = m_ImageLayer->GetNumberOfComponents();
    if(nc == 3 || nc == 2)
      {
      m_AvailableDisplayModes.push_back(
            MultiChannelDisplayMode(true, false, SCALAR_REP_COMPONENT));
      m_AvailableDisplayModes.push_back(
            MultiChannelDisplayMode(false, true, SCALAR_REP_COMPONENT));
      }
    }
}

bool
ImageLayerTableRowModel::GetDisplayModeValue(MultiChannelDisplayMode &value)
{
  if(m_Layer && m_ImageLayer->GetNumberOfComponents() > 1)
    {
    AbstractMultiChannelDisplayMappingPolicy *dp = dynamic_cast<
        AbstractMultiChannelDisplayMappingPolicy *>(m_Layer->GetDisplayMapping());
    value = dp->GetDisplayMode();
    return true;
    }
  return false;
}

void
ImageLayerTableRowModel::SetDisplayModeValue(MultiChannelDisplayMode value)
{
  AbstractMultiChannelDisplayMappingPolicy *dp = dynamic_cast<
      AbstractMultiChannelDisplayMappingPolicy *>(m_Layer->GetDisplayMapping());
  dp->SetDisplayMode(value);
}

MultiChannelDisplayMode
ImageLayerTableRowModel::GetDisplayMode()
{
  AbstractMultiChannelDisplayMappingPolicy *dp = dynamic_cast<
      AbstractMultiChannelDisplayMappingPolicy *>(m_Layer->GetDisplayMapping());
  return dp->GetDisplayMode();
}

bool
ImageLayerTableRowModel::CheckState(UIState state)
{
  switch (state)
    {
    // Opacity can be edited for all layers except the main image layer
    case AbstractLayerTableRowModel::UIF_OPACITY_EDITABLE:
      return (m_LayerRole != LABEL_ROLE && m_ImageLayer->IsSticky());

    // Pinnable means it's not sticky and may be overlayed (i.e., not main)
    case AbstractLayerTableRowModel::UIF_PINNABLE:
      return (m_LayerRole != MAIN_ROLE && m_LayerRole != LABEL_ROLE && !m_ImageLayer->IsSticky());

    // Unpinnable means it's not sticky and may be overlayed (i.e., not main)
    case AbstractLayerTableRowModel::UIF_UNPINNABLE:
      return (m_LayerRole != MAIN_ROLE && m_LayerRole != LABEL_ROLE && m_ImageLayer->IsSticky());

    case AbstractLayerTableRowModel::UIF_MULTICOMPONENT:
      return (m_Layer && m_ImageLayer->GetNumberOfComponents() > 1);

    case AbstractLayerTableRowModel::UIF_CONTRAST_ADJUSTABLE:
      {
      return (m_Layer && m_Layer->GetDisplayMapping()->GetIntensityCurve());
      }


    case AbstractLayerTableRowModel::UIF_COLORMAP_ADJUSTABLE:
      return (m_Layer && m_Layer->GetDisplayMapping()->GetColorMap());

    case AbstractLayerTableRowModel::UIF_VOLUME_RENDERABLE:
      return m_LayerRole != LABEL_ROLE;

    case AbstractLayerTableRowModel::UIF_IMAGE:
      return true;

    case AbstractLayerTableRowModel::UIF_SAVABLE:
      return true;

    case AbstractLayerTableRowModel::UIF_IS_4D:
      return (m_Layer && m_ImageLayer->GetNumberOfTimePoints() > 1);

    default:
      return Superclass::CheckState(state); // Children override Parents
    }

  return false;
}

void
ImageLayerTableRowModel::Initialize(GlobalUIModel *parentModel, WrapperBase *layer)
{
  // Initialize superclass first
  Superclass::Initialize(parentModel, layer);

  // Downcast wrapper. It has to be a ImageWrapperBase or raise error
  ImageWrapperBase *img_wrapper = static_cast<ImageWrapperBase*>(layer);
  m_ImageLayer = img_wrapper;

  // Update the list of display modes (again, should not change during the
  // lifetime of this object
  UpdateDisplayModeList();
}

void
ImageLayerTableRowModel::SetActivated(bool activated)
{
  // Odd why this would ever happen, but it does...
  if(!m_ImageLayer)
    return;

  // If the layer is selected and is not sticky, we set is as the currently visible
  // layer in the render views
  if(m_LayerRole == LABEL_ROLE && activated)
    {
    m_ParentModel->GetGlobalState()->SetSelectedSegmentationLayerId(m_ImageLayer->GetUniqueId());
    }
  else if(activated && !m_ImageLayer->IsSticky())
    {
    m_ParentModel->GetGlobalState()->SetSelectedLayerId(m_ImageLayer->GetUniqueId());
    }
}

bool
ImageLayerTableRowModel::IsActivated() const
{
  // Odd why this would ever happen, but it does...
  if(!m_Layer)
    return false;

  // Selection is based on id
  unsigned long uid = m_ImageLayer->GetUniqueId();

  // If the layer is selected and is not sticky, we set is as the currently visible
  // layer in the render views
  if(m_LayerRole == LABEL_ROLE)
    {
    if(uid == m_ParentModel->GetGlobalState()->GetSelectedSegmentationLayerId())
      return true;
    }
  else
    {
    if(uid == m_ParentModel->GetGlobalState()->GetSelectedLayerId())
      return true;
    }

  return false;
}

bool
ImageLayerTableRowModel::GetComponentNameValue(std::string &value)
{
  // Get the name of the compomnent
  if(m_ImageLayer && m_ImageLayer->GetNumberOfComponents() > 1)
    {
    value = this->GetDisplayModeString(this->GetDisplayMode());
    return true;
    }

  return false;
}

std::string
ImageLayerTableRowModel::GetDisplayModeString(const MultiChannelDisplayMode &mode)
{
  if(mode.UseRGB)
    {
    return "RGB";
    }

  if(mode.RenderAsGrid)
    {
    return "Grid";
    }

  std::ostringstream oss;
  switch(mode.SelectedScalarRep)
    {
    case SCALAR_REP_COMPONENT:
      oss << (1 + mode.SelectedComponent) << "/" << m_ImageLayer->GetNumberOfComponents();
      return oss.str();

    case SCALAR_REP_MAGNITUDE:
      return "Mag";

    case SCALAR_REP_MAX:
      return "Max";

    case SCALAR_REP_AVERAGE:
      return "Avg";

    case NUMBER_OF_SCALAR_REPS:
      break;
    };

  return "";
}

void
ImageLayerTableRowModel::MoveLayerUp()
{
  m_ParentModel->GetDriver()->ChangeOverlayPosition(m_ImageLayer, -1);
}

void
ImageLayerTableRowModel::MoveLayerDown()
{
  m_ParentModel->GetDriver()->ChangeOverlayPosition(m_ImageLayer, +1);
}

void
ImageLayerTableRowModel::UpdateRoleInfo()
{
  LayerIterator it(m_ImageData);
  it.Find(static_cast<ImageWrapperBase*>(m_Layer.GetPointer()));
  if(!it.IsAtEnd())
    {
    m_LayerRole = it.GetRole();
    m_LayerPositionInRole = it.GetPositionInRole();
    m_LayerNumberOfLayersInRole = it.GetNumberOfLayersInRole();
    }
}

bool
ImageLayerTableRowModel::GetStickyValue(bool &value)
{
  if(!m_Layer) return false;

  value = m_ImageLayer->IsSticky();
  return true;
}

void
ImageLayerTableRowModel::SetStickyValue(bool value)
{
  // Make sure the selected ID is legitimate
  if(m_ParentModel->GetGlobalState()->GetSelectedLayerId() == m_ImageLayer->GetUniqueId())
    {
    m_ParentModel->GetGlobalState()->SetSelectedLayerId(
          m_ParentModel->GetDriver()->GetCurrentImageData()->GetMain()->GetUniqueId());
    }
  m_ImageLayer->SetSticky(value);
}

SmartPtr<ImageIOWizardModel>
ImageLayerTableRowModel::CreateIOWizardModelForSave()
{
  return m_ParentModel->CreateIOWizardModelForSave(m_ImageLayer, m_LayerRole);
}

bool
ImageLayerTableRowModel::GetColorMapPresetValue(std::string &value)
{
  if(m_Layer && m_Layer->GetDisplayMapping()->GetColorMap())
    {
    value = ColorMap::GetPresetName(
          m_Layer->GetDisplayMapping()->GetColorMap()->GetSystemPreset());
    return true;
    }
  return false;
}

void
ImageLayerTableRowModel::SetColorMapPresetValue(std::string value)
{
  // TODO: this is a cheat! The current way of handling color map presets in
  // snap is hacky, and I really don't like it. We need a common class that
  // can handle system and user presets for a variety of objects, one that
  // can interface nicely with the GUI models. Here we are going through
  // the functionality provided by the ColorMapModel class.
  ColorMapModel *cmm = m_ParentModel->GetColorMapModel();
  WrapperBase *currentLayer = cmm->GetLayer();
  cmm->SetLayer(m_ImageLayer);
  cmm->SelectPreset(value);
  cmm->SetLayer(currentLayer);
}

void
ImageLayerTableRowModel::AutoAdjustContrast()
{
  if(m_Layer && m_Layer->GetDisplayMapping()->GetIntensityCurve())
    {
    // TODO: this is a bit of a hack, since we go through a different model
    // and have to swap out that model's current layer, which adds some overhead
    IntensityCurveModel *icm = m_ParentModel->GetIntensityCurveModel();
    WrapperBase *currentLayer = icm->GetLayer();
    icm->SetLayer(m_ImageLayer);
    icm->OnAutoFitWindow();
    icm->SetLayer(currentLayer);
    }
}

void
ImageLayerTableRowModel::GenerateTextureFeatures()
{
  ImageWrapperBase *iw = dynamic_cast<ImageWrapperBase *>(m_Layer.GetPointer());
  if(iw && iw->GetNumberOfComponents() == 1)
    {
    // Cast the image to floating point
    auto *float_src = iw->CreateCastToFloatPipeline("GenerateTextureFeatures");

    // Create a radius - hard-coded for now
    itk::Size<3> radius; radius.Fill(2);

    // The texture image should be floating point
    typedef AnatomicImageWrapperTraits<float> TextureWrapperTraitsType;
    typedef TextureWrapperTraitsType::ImageType TextureImageType;
    typedef TextureWrapperTraitsType::Image4DType TextureImage4DType;
    typedef TextureWrapperTraitsType::WrapperType TextureWrapperType;

    // Create a filter to generate textures
    typedef bilwaj::MomentTextureFilter<
        ImageWrapperBase::FloatImageType, TextureImageType> MomentFilterType;

    MomentFilterType::Pointer filter = MomentFilterType::New();
    filter->SetInput(float_src);
    filter->SetRadius(radius);
    filter->SetHighestDegree(3);

    // Up the image dimension
    typedef IncreaseDimensionImageFilter<TextureImageType, TextureImage4DType> UpDimFilter;
    typename UpDimFilter::Pointer updim = UpDimFilter::New();
    updim->SetInput(filter->GetOutput());
    updim->Update();

    // Create a new image wrapper
    SmartPtr<TextureWrapperType> newWrapper = TextureWrapperType::New();
    newWrapper->InitializeToWrapper(m_ImageLayer, updim->GetOutput());
    newWrapper->SetDefaultNickname("Textures");
    this->GetParentModel()->GetDriver()->AddDerivedOverlayImage(m_ImageLayer, newWrapper, false);

    // Release the cast pipeline
    iw->ReleaseInternalPipeline("GenerateTextureFeatures");
    }
}

void
ImageLayerTableRowModel::CloseLayer()
{
  // If this is an overlay, we unload it like this
  if(m_LayerRole == OVERLAY_ROLE)
    m_ParentModel->GetDriver()->UnloadOverlay(m_ImageLayer);

  // The main image can also be unloaded
  else if(m_LayerRole == MAIN_ROLE)
    m_ParentModel->GetDriver()->UnloadMainImage();

  // Segmentations can be unloaded
  else if (m_LayerRole == LABEL_ROLE)
    m_ParentModel->GetDriver()->UnloadSegmentation(m_ImageLayer);

  m_ImageLayer = NULL;
  m_Layer = NULL;
}

void ImageLayerTableRowModel::ReloadAsMultiComponent()
{
  auto driver = m_ParentModel->GetDriver();
  auto filename = driver->GetMainImage()->GetFileName();

  SmartPtr<LoadMainImageDelegate> delegate = LoadMainImageDelegate::New();
  delegate->Initialize(driver);
  delegate->SetLoad4DAsMultiComponent(true);

  IRISWarningList warningList;

  driver->OpenImageViaDelegate(filename, delegate, warningList);

  this->Initialize(m_ParentModel, driver->GetMainImage());

}

void ImageLayerTableRowModel::ReloadAs4D()
{
  auto driver = m_ParentModel->GetDriver();
  auto filename = driver->GetMainImage()->GetFileName();

  SmartPtr<LoadMainImageDelegate> delegate = LoadMainImageDelegate::New();
  delegate->Initialize(driver);
  delegate->SetLoadMultiComponentAs4D(true);

  IRISWarningList warningList;

  driver->OpenImageViaDelegate(filename, delegate, warningList);

  this->Initialize(m_ParentModel, driver->GetMainImage());

}

bool ImageLayerTableRowModel::GetVolumeRenderingEnabledValue(bool &value)
{
  auto imageLayer = dynamic_cast<ImageWrapperBase*>(m_Layer.GetPointer());
  if(imageLayer)
    {
    value = imageLayer->GetDefaultScalarRepresentation()->IsVolumeRenderingEnabled();
    return true;
    }
  return false;
}

void ImageLayerTableRowModel::SetVolumeRenderingEnabledValue(bool value)
{
  auto imageLayer = dynamic_cast<ImageWrapperBase*>(m_Layer.GetPointer());
  if(imageLayer)
    imageLayer->GetDefaultScalarRepresentation()->SetVolumeRenderingEnabled(value);
}

void
ImageLayerTableRowModel
::ReloadWrapperFromFile(IRISWarningList &wl)
{
  SmartPtr<AbstractReloadWrapperDelegate> delegate;

  if (m_LayerRole == LABEL_ROLE)
    delegate = ReloadSegmentationWrapperDelegate::New().GetPointer();
  else
    delegate = ReloadAnatomicWrapperDelegate::New().GetPointer();

  ImageWrapperBase *imgWrapper = dynamic_cast<ImageWrapperBase*>(m_Layer.GetPointer());

  if (!imgWrapper)
    return;

  delegate->Initialize(m_ParentModel->GetDriver(), imgWrapper);
  delegate->ValidateHeader(wl);
  delegate->UpdateWrapper();
}

/*
 * ===============================================
 *   MeshLayerTableRowModel Implementation
 * ===============================================
*/
MeshLayerTableRowModel
::MeshLayerTableRowModel()
{
  m_ActivePropertyModel = wrapGetterSetterPairAsProperty(this, &Self::GetActivePropertyValue);

  m_ActiveMeshLayerDataPropertyIdModel = wrapGetterSetterPairAsProperty(
        this,
        &Self::GetActiveMeshLayerDataPropertyIdValueAndRange,
        &Self::SetActiveMeshLayerDataPropertyIdValue);

  m_MeshVectorModeModel = wrapGetterSetterPairAsProperty(
    this,
    &Self::GetMeshVectorModeValueAndRange,
    &Self::SetMeshVectorModeValue);
}

void
MeshLayerTableRowModel::Initialize(GlobalUIModel *parentModel, WrapperBase *layer)
{
  Superclass::Initialize(parentModel, layer);

  // Downcast wrapper. It has to be a MeshWrapperBase or raise error
  MeshWrapperBase *mesh_wrapper = static_cast<MeshWrapperBase*>(layer);
  m_MeshLayer = mesh_wrapper;
}

bool
MeshLayerTableRowModel::CheckState(UIState state)
{
  bool hasGenericDMP =
    (dynamic_cast<GenericMeshDisplayMappingPolicy *>(m_MeshLayer->GetDisplayMapping()) != NULL);

  auto prop = m_MeshLayer->GetActiveDataArrayProperty();

  switch (state)
  {
    // This is a mesh
    case AbstractLayerTableRowModel::UIF_MESH:
      return true;

    // Allow opacity editing for all mesh layers
    case AbstractLayerTableRowModel::UIF_OPACITY_EDITABLE:
      return false;

    // We don't need to pin a mesh layer for now
    case AbstractLayerTableRowModel::UIF_PINNABLE:
      return false;

    // We don't need to pin a mesh layer for now
    case AbstractLayerTableRowModel::UIF_UNPINNABLE:
      return false;

    case AbstractLayerTableRowModel::UIF_VOLUME_RENDERABLE:
      return false;

    case AbstractLayerTableRowModel::UIF_MESH_MULTICOMPONENT:
      return prop && prop->IsMultiComponent();

    case AbstractLayerTableRowModel::UIF_CONTRAST_ADJUSTABLE:
      return hasGenericDMP & prop.IsNotNull();

    case AbstractLayerTableRowModel::UIF_COLORMAP_ADJUSTABLE:
      return hasGenericDMP & prop.IsNotNull();

    case AbstractLayerTableRowModel::UIF_MESH_HAS_DATA:
      return hasGenericDMP;

    case AbstractLayerTableRowModel::UIF_MESH_SOLID_COLOR:
      return hasGenericDMP ? prop.IsNull() : false;

    case AbstractLayerTableRowModel::UIF_SAVABLE:
      return !hasGenericDMP; // Mesh layer is currently read only

    default:
      return Superclass::CheckState(state); // Children override parents
  }

  return false;
}

bool
MeshLayerTableRowModel::GetStickyValue(bool &)
{
  return false;
}

void
MeshLayerTableRowModel::SetStickyValue(bool )
{
}

void
MeshLayerTableRowModel::SetActivated(bool activated)
{
  if(!m_MeshLayer)
    return;

  if (activated)
    {
    m_ImageData->GetMeshLayers()->SetActiveLayerId(m_MeshLayer->GetUniqueId());
    }
}

bool
MeshLayerTableRowModel::IsActivated() const
{
  if(!m_Layer)
    return false;

  return (m_Layer->GetUniqueId() ==
          m_ParentModel->GetDriver()->GetCurrentImageData()->GetMeshLayers()->
          GetActiveLayerId());
}

void
MeshLayerTableRowModel::AutoAdjustContrast()
{
  auto genericDMP = dynamic_cast<GenericMeshDisplayMappingPolicy*>(m_Layer->GetDisplayMapping());
  if(m_Layer && genericDMP && genericDMP->GetIntensityCurve())
    {
    genericDMP->AutoFitContrast();
    }
}

void
MeshLayerTableRowModel::UpdateRoleInfo()
{
  m_LayerRole = MESH_ROLE;
  m_LayerPositionInRole = m_Layer->GetUniqueId();
  m_LayerNumberOfLayersInRole = m_ImageData->GetMeshLayers()->size();
}

void
MeshLayerTableRowModel::CloseLayer()
{
  m_ParentModel->GetDriver()->UnloadMeshLayer(m_MeshLayer->GetUniqueId());
  m_MeshLayer = NULL;
  m_Layer = NULL;
}

bool
MeshLayerTableRowModel::GetColorMapPresetValue(std::string &value)
{
  if(m_Layer && m_Layer->GetDisplayMapping()->GetColorMap())
    {
    value = ColorMap::GetPresetName(
          m_Layer->GetDisplayMapping()->GetColorMap()->GetSystemPreset());
    return true;
    }
  return false;
}

void
MeshLayerTableRowModel::SetColorMapPresetValue(std::string value)
{
  ColorMapModel *cmm = m_ParentModel->GetColorMapModel();
  WrapperBase *currentLayer = cmm->GetLayer();
  cmm->SetLayer(m_MeshLayer);
  cmm->SelectPreset(value);
  cmm->SetLayer(currentLayer);
}

bool
MeshLayerTableRowModel::GetActivePropertyValue(std::string &value)
{
  if(!m_MeshLayer)
    return false;

  auto *mesh = dynamic_cast<StandaloneMeshWrapper *>(m_MeshLayer.GetPointer());
  if(!mesh)
    return false;

  auto prop = m_MeshLayer->GetActiveDataArrayProperty();
  value = prop ? prop->GetName() : "Solid Color";
  return true;
}


bool
MeshLayerTableRowModel::GetActiveMeshLayerDataPropertyIdValueAndRange(int                 &value,
                                                                      MeshDataArrayDomain *domain)
{
  // The current layer has to be a mesh layer
  auto *mesh_layer = dynamic_cast<StandaloneMeshWrapper *>(m_Layer.GetPointer());
  if (!mesh_layer)
    return false;

  // Get the value
  value = mesh_layer->GetActiveMeshLayerDataPropertyId();

  // Populate the domain. In the future, we should wrap the domain around a container stored
  // in the model instead of creating a new domain every time
  if (domain)
  {
    domain->clear();

           // Add solid color
    MeshDataArrayDescriptor solid_color = {
      true, std::string(), AbstractMeshDataArrayProperty::COUNT
    };
    (*domain)[-1] = solid_color;

    // Add all of the other arrays
    for (auto &kv : mesh_layer->GetCombinedDataProperty())
    {
      MeshDataArrayDescriptor prop = { false, kv.second->GetName(), kv.second->GetType() };
      (*domain)[kv.first] = prop;
    }
  }

  return true;
}

void
MeshLayerTableRowModel::SetActiveMeshLayerDataPropertyIdValue(int value)
{
  // The current layer has to be a mesh layer
  auto *mesh = dynamic_cast<StandaloneMeshWrapper *>(m_MeshLayer.GetPointer());
  if (mesh)
  {
    mesh->SetActiveMeshLayerDataPropertyId(value);
    this->InvokeEvent(StateMachineChangeEvent());
    // this->GetMeshVectorModeModel()->InvokeEvent(DomainChangedEvent());
  }
}

bool
MeshLayerTableRowModel::GetMeshVectorModeValueAndRange(vtkIdType &value, MeshVectorModeDomain *domain)
{
  // The current layer has to be a mesh layer
  StandaloneMeshWrapper *mesh = dynamic_cast<StandaloneMeshWrapper *>(m_MeshLayer.GetPointer());
  if (!mesh)
    return false;

  using VectorMode = MeshLayerDataArrayProperty::VectorMode;

  auto layer_prop = mesh->GetActiveDataArrayProperty();
  if (!layer_prop)
    return false;

  size_t nc = layer_prop->GetNumberOfComponents();

  // Start populating the domain
  size_t domain_ind = 0;

  if (domain)
  {
    (*domain)[domain_ind++] = { MeshLayerDataArrayProperty::MAGNITUDE, -1 };
    for (size_t i = 0; i < nc; ++i)
      (*domain)[domain_ind++] = { MeshLayerDataArrayProperty::COMPONENT, (int) i };
  }

  // Process current value
  switch (layer_prop->GetActiveVectorMode())
  {
    case VectorMode::MAGNITUDE:
      value = 0;
      break;
    default: // COMPONENT Mode
    {
      int shift = 1; // skip magnitude 0
      value = layer_prop->GetActiveComponentId() + shift;
    }
  }

  return true;
}

void
MeshLayerTableRowModel::SetMeshVectorModeValue(vtkIdType value)
{
  // The current layer has to be a mesh layer
  StandaloneMeshWrapper *mesh = dynamic_cast<StandaloneMeshWrapper *>(m_MeshLayer.GetPointer());
  if (!mesh)
    return;

  assert(value >= 0);

  auto layer_prop = mesh->GetActiveDataArrayProperty();
  using VectorMode = MeshLayerDataArrayProperty::VectorMode;

  if (value == 0) // magnitude
    layer_prop->SetActiveVectorMode(VectorMode::MAGNITUDE);
  else
  {
    const int shift = 1; // skip magnitude 0
    // process individual components
    layer_prop->SetActiveVectorMode(VectorMode::COMPONENT, value - shift);
  }

  mesh->InvokeEvent(WrapperDisplayMappingChangeEvent());
}

void
MeshLayerTableRowModel
::ReloadWrapperFromFile(IRISWarningList &)
{
  /*
  not implemented for now
  reloading mesh data could cause dramatic change of wrapper
  all of the data arrays and properties could chnage
  should recreate the wrapper entirely instead
  */
}

bool
MeshLayerTableRowModel::MeshDataArrayDescriptor::operator!=(const MeshDataArrayDescriptor &other) const
{
  return SolidColor != other.SolidColor || ArrayName != other.ArrayName ||
         MeshDataType != other.MeshDataType;
}

bool
MeshLayerTableRowModel::MeshDataArrayDescriptor::operator==(const MeshDataArrayDescriptor &other) const
{
  return !(*this != other);
}


bool
MeshLayerTableRowModel::MeshVectorModeDescriptor::operator!=(const MeshVectorModeDescriptor &other) const
{
  return Mode != other.Mode || Component != other.Component;
}

bool
MeshLayerTableRowModel::MeshVectorModeDescriptor::operator==(const MeshVectorModeDescriptor &other) const
{
  return !(*this != other);
}
