//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================
#include "smtk/session/aeva/RegisterStyleFunction.h"

#include "smtk/session/aeva/CellSelection.h"

#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/ReferenceItem.h"
#include "smtk/extension/vtk/geometry/Backend.h"
#include "smtk/geometry/queries/SelectionFootprint.h"
#include "smtk/model/Entity.h"
#include "smtk/resource/Component.h"
#include "smtk/resource/Manager.h"
#include "smtk/resource/query/BadTypeError.h"
#include "smtk/resource/query/Queries.h"
#include "smtk/view/Selection.h"

#include <vtkCompositeDataDisplayAttributes.h>
#include <vtkCompositePolyDataMapper2.h>
#include <vtkPVConfig.h>

namespace smtk
{
namespace session
{
namespace aeva
{
namespace
{
// AEVA logo color #ff6600
double CellSelectionColor[3] = { 1., 0.4, 0.0 };

// borrowed from vtkSMTKResourceRepresentation, CellSelectionColor added.
void SetSelectedState(vtkDataObject* data,
  int selectionValue,
  bool isGlyph,
  bool isCellSelection,
  vtkSMTKResourceRepresentation* self)
{
  // glyphs unused in aeva so far, so simplify.
  if (isGlyph)
  {
    return;
  }
  auto* nrm = self->GetEntityMapperDisplayAttributes();
  auto* sel = self->GetSelectedEntityMapperDisplayAttributes();
  if (selectionValue > 0)
  {
    double* blockColor = selectionValue > 1 ? self->GetHoverColor() : self->GetSelectionColor();
    if (isCellSelection)
    {
      blockColor = CellSelectionColor;
    }
    sel->SetBlockColor(data, blockColor);
    // WORKAROUND: Force the selection block to be rendered in a different pass.
    //             For some reason, selection in SMTK has broken with the bump to
    //             ParaView SHA 722af6ba9e4e0941f936f1ba1fbfbc907edcd6e7
    //             (Thu Jun 25 11:16:37 2020). Setting the selected opacity to be
    //             nearly, but not entirely, 1 forces the selection to be
    //             rendered in a second pass.
    if (PARAVIEW_VERSION_MAJOR == 5 &&
        (PARAVIEW_VERSION_MINOR == 8 && PARAVIEW_VERSION_PATCH > 0) ||
      PARAVIEW_VERSION_MINOR >= 9)
    {
      vtkDebugWithObjectMacro(
        nullptr, "WORKAROUND: force the selection block to be rendered in a different pass.");
      sel->SetBlockOpacity(data, .999);
    }
  }
  sel->SetBlockVisibility(data, selectionValue > 0);
  nrm->SetBlockVisibility(data, selectionValue == 0);
}

// borrowed from vtkSMTKResourceRepresentation, isCellSelection added.
bool SelectComponentFootprint(smtk::resource::PersistentObject* item,
  int selnBits,
  vtkSMTKResourceRepresentation::RenderableDataMap& renderables,
  vtkSMTKResourceRepresentation* self)
{
  bool atLeastOneSelected = false;
  if (!item)
  {
    return atLeastOneSelected;
  }

  // Determine the actor-pair we are dealing with (normal or instanced):
  auto entity = item->as<smtk::model::Entity>();
  bool isCellSelection = !!item->as<smtk::session::aeva::CellSelection>();
  bool isGlyphed = entity && entity->isInstance();

  // Determine if the user has hidden the entity
  const auto& smap = self->GetComponentState();
  auto cstate = smap.find(item->id());
  bool hidden = (cstate != smap.end() && !cstate->second.m_visibility);

  auto dataIt = renderables.find(item->id());
  if (dataIt != renderables.end())
  {
    SetSelectedState(dataIt->second, hidden ? -1 : selnBits, isGlyphed, isCellSelection, self);
    atLeastOneSelected |= !hidden;
  }
  return atLeastOneSelected;
}
}

RegisterStyleFunction::StyleFromSelectionFunction RegisterStyleFunction::operator()(
  const smtk::resource::ResourcePtr& /*in*/)
{

  // borrowed from vtkSMTKResourceRepresentation::ApplyDefaultStyle
  return [](const smtk::view::SelectionPtr& seln,
           vtkSMTKResourceRepresentation::RenderableDataMap& renderables,
           vtkSMTKResourceRepresentation* self) {
    bool atLeastOneSelected = false;
    smtk::attribute::Attribute::Ptr attr;
    for (const auto& item : seln->currentSelection())
    {
      if (item.second <= 0)
      {
        // Should never happen.
        continue;
      }

      // If the selected item is a resource, ask for its footprint directly.
      std::unordered_set<smtk::resource::PersistentObject*> footprint;
      auto resource = std::dynamic_pointer_cast<smtk::resource::Resource>(item.first);
      if (resource)
      {
        if (resource->queries().contains<smtk::geometry::SelectionFootprint>())
        {
          auto& query = resource->queries().get<smtk::geometry::SelectionFootprint>();
          query(*resource, footprint, smtk::extension::vtk::geometry::Backend());
        }
        else
        {
          // resource has no footprint query. Try inserting the resource itself.
          footprint.insert(resource.get());
        }
      }

      // If the selected item is a component, ask its resource for the footprint.
      auto component = std::dynamic_pointer_cast<smtk::resource::Component>(item.first);
      if (component)
      {
        if (component->resource()->queries().contains<smtk::geometry::SelectionFootprint>())
        {
          auto& query = component->resource()->queries().get<smtk::geometry::SelectionFootprint>();
          query(*component, footprint, smtk::extension::vtk::geometry::Backend());
        }
        else
        {
          // component has no footprint query. Try inserting the component itself.
          footprint.insert(component.get());
        }
      }

      for (const auto& obj : footprint)
      {
        atLeastOneSelected |= SelectComponentFootprint(obj, /*selnBit TODO*/ 1, renderables, self);
      }
    }

    return atLeastOneSelected;
  };
}

}
}
}
