/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef itkQuadEdgeMeshLineCell_h
#define itkQuadEdgeMeshLineCell_h

#include "itkAutoPointer.h"
#include "itkGeometricalQuadEdge.h"

namespace itk
{
/**
 * \class QuadEdgeMeshLineCell
 *
 * \brief Class that connects the QuadEdgeMesh with the Mesh
 *
 * \param TCellInterface Basic type for the itk*Cell.
 *        This usually comes from the MeshTraits.
 *
 * \author  Eric Boix, Alex Gouaillard, Leonardo Florez
 *
 * \ingroup ITKQuadEdgeMesh
 */
template< typename TCellInterface >
class ITK_TEMPLATE_EXPORT QuadEdgeMeshLineCell:
  public TCellInterface, public TCellInterface::CellTraits::QuadEdgeType
{
public:
  ITK_DISALLOW_COPY_AND_ASSIGN(QuadEdgeMeshLineCell);

  /** Standard class type aliases. */
  // itkCellCommonTypedefs
  using Self = QuadEdgeMeshLineCell;
  using ConstSelfAutoPointer = AutoPointer< const Self >;
  using SelfAutoPointer = AutoPointer< Self >;
  using RawPointer = Self *;
  using ConstRawPointer = const Self *;

  // itkCellInheritedTypedefs
  using Superclass = TCellInterface;
  using PixelType = typename Superclass::PixelType;
  using CellType = typename Superclass::CellType;
  using CellAutoPointer = typename Superclass::CellAutoPointer;
  using CellConstAutoPointer = typename Superclass::CellConstAutoPointer;
  using CellRawPointer = typename Superclass::CellRawPointer;
  using CellConstRawPointer = typename Superclass::CellConstRawPointer;
  using CellTraits = typename Superclass::CellTraits;
  using CoordRepType = typename Superclass::CoordRepType;
  using InterpolationWeightType = typename Superclass::InterpolationWeightType;
  using PointIdentifier = typename Superclass::PointIdentifier;
  using CellIdentifier = typename Superclass::CellIdentifier;
  using CellFeatureIdentifier = typename Superclass::CellFeatureIdentifier;
  using CellFeatureCount = typename Superclass::CellFeatureIdentifier;
  using PointType = typename Superclass::PointType;
  using PointsContainer = typename Superclass::PointsContainer;
  using UsingCellsContainer = typename Superclass::UsingCellsContainer;
  using CellGeometry = typename Superclass::CellGeometry;
  using ParametricCoordArrayType = typename Superclass::ParametricCoordArrayType;
  using ShapeFunctionsArrayType = typename Superclass::ShapeFunctionsArrayType;
  static constexpr unsigned int PointDimension = Superclass::PointDimension;
  static constexpr unsigned int CellDimension = 2;

  /** Multivisitor type. */
  using MultiVisitor = typename CellType::MultiVisitor;

  //** */
  using PointIdIterator = typename CellTraits::PointIdIterator;
  using PointIdConstIterator = typename CellTraits::PointIdConstIterator;
  using PointIdInternalIterator = typename CellTraits::PointIdInternalIterator;
  using PointIdInternalConstIterator = typename CellTraits::PointIdInternalConstIterator;

  /** QE types. */
  using QEType = typename CellTraits::QuadEdgeType;
  using VertexRefType = typename QEType::OriginRefType;
  using FaceRefType = typename QEType::DualOriginRefType;
  using PrimalDataType = typename QEType::PrimalDataType;
  using DualDataType = typename QEType::DualDataType;
  using QEDual = typename QEType::DualType;

public:
  /** Standard part of every itk Object. */
  itkTypeMacro(QuadEdgeMeshLineCell, TCellInterface);

  // accessor to the new QEGeom link that replaces now inheritance.
  QEType * GetQEGeom() const { return ( m_QuadEdgeGeom ); }

public:
  /** Object memory management methods. */
  QuadEdgeMeshLineCell();
  ~QuadEdgeMeshLineCell() override;

  /** Accessors for m_Identifier. */
  void SetIdent(CellIdentifier cid);

  CellIdentifier GetIdent();

  /** TCellInterface abstract methods definition. */
  void Accept(CellIdentifier cellId, MultiVisitor *mv) override;

  CellGeometry GetType() const override;

  /** Topology related methods. */
  static int GetTopologyId();

  unsigned int GetDimension() const override;

  unsigned int GetNumberOfPoints() const override;

  CellFeatureCount GetNumberOfBoundaryFeatures(int dimension) const override;

  bool GetBoundaryFeature(int dimension,
                                  CellFeatureIdentifier cellId,
                                  CellAutoPointer & cell) override;

  /** Useless methods. */
  void MakeCopy(CellAutoPointer & cell) const override
  {
    cell.TakeOwnership(new Self);
    cell->SetPointId( 0, this->GetQEGeom()->GetOrigin() );
    cell->SetPointId( 1, this->GetQEGeom()->GetDestination() );
  }

  /** ITK Cell API - Iterator-related methods.
   *  The Set methods will work, not the Get.
   *  Hopefully never used ...
   */
  void SetPointIds(PointIdConstIterator first) override;

  void SetPointIds(PointIdConstIterator first,
                           PointIdConstIterator last) override;

  void SetPointId(int localId, PointIdentifier pId) override;

  PointIdIterator PointIdsBegin() override
  {
    SynchronizePointsAPI();
    return &m_PointIds[0];
  }

  PointIdIterator PointIdsEnd() override
  {
    SynchronizePointsAPI();
    return ( &m_PointIds[1] + 1 );
  }

  PointIdConstIterator GetPointIds() const override
  {
    SynchronizePointsAPI();
    return &m_PointIds[0];
  }

  PointIdConstIterator PointIdsBegin() const override
  {
    SynchronizePointsAPI();
    return &m_PointIds[0];
  }

  PointIdConstIterator PointIdsEnd() const override
  {
    SynchronizePointsAPI();
    return ( &m_PointIds[1] + 1 );
  }

  /** helper for backward compatibility */
  void SynchronizePointsAPI() const
  {
    m_PointIds[0] = GetQEGeom()->GetOrigin();
    m_PointIds[1] = GetQEGeom()->GetDestination();
  }

  /** QuadEdge internal flavor of cell API */
  virtual void InternalSetPointIds(PointIdInternalConstIterator first);

  virtual void InternalSetPointIds(
    PointIdInternalConstIterator first,
    PointIdInternalConstIterator last);

  virtual PointIdInternalIterator InternalPointIdsBegin();

  virtual PointIdInternalIterator InternalPointIdsEnd();

  virtual PointIdInternalConstIterator InternalGetPointIds() const;

  virtual PointIdInternalConstIterator InternalPointIdsBegin() const;

  virtual PointIdInternalConstIterator InternalPointIdsEnd() const;

private:
  /**
   * In order to have constant time access at the itk level instead of
   * of doing a search in the Mesh::Cell container.
   */
  CellIdentifier          m_Identifier;
  QEType *                m_QuadEdgeGeom;
  mutable PointIdentifier m_PointIds[2];
};
} // end namespace itk

#ifndef ITK_MANUAL_INSTANTIATION
#include "itkQuadEdgeMeshLineCell.hxx"
#endif

#endif
