//=========================================================================
//  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 "vtkModelEdge.h"

#include "vtkModel.h"
#include "vtkModelEdgeUse.h"
#include "vtkModelFace.h"
#include "vtkModelItemGenericIterator.h"
#include "vtkModelItemIterator.h"
#include "vtkModelLoopUse.h"
#include "vtkModelVertex.h"
#include "vtkModelVertexUse.h"
#include "vtkObjectFactory.h"

#include <set>


vtkModelEdge::vtkModelEdge()
{
}

//-----------------------------------------------------------------------------
vtkModelEdge::~vtkModelEdge()
{
}

//-----------------------------------------------------------------------------
int vtkModelEdge::GetType()
{
  return vtkModelEdgeType;
}

//-----------------------------------------------------------------------------
int vtkModelEdge::GetNumberOfModelEdgeUses()
{
  return this->GetNumberOfAssociations(vtkModelEdgeUseType);
}

//-----------------------------------------------------------------------------
vtkModelEdgeUse* vtkModelEdge::GetModelEdgeUse(int which)
{ // 0 <= which < number of modeledgeuses
  if(which >= this->GetNumberOfAssociations(vtkModelEdgeUseType) || which < 0)
    {
    return NULL;
    }
  vtkModelItemIterator* iter =
    this->NewIterator(vtkModelEdgeUseType);
  int counter = 0;
  vtkModelEdgeUse* edgeUse = 0;
  for(iter->Begin();!iter->IsAtEnd();iter->Next())
    {
    if(counter == which)
      {
      edgeUse = vtkModelEdgeUse::SafeDownCast(iter->GetCurrentItem());
      break;
      }
    counter++;
    }
  iter->Delete();
  return edgeUse;
}

//-----------------------------------------------------------------------------
vtkModelItemIterator* vtkModelEdge::NewModelEdgeUseIterator()
{
  return this->NewIterator(vtkModelEdgeUseType);
}

//-----------------------------------------------------------------------------
int vtkModelEdge::GetNumberOfModelVertexUses()
{
  vtkModelEdgeUse* edgeUse = this->GetModelEdgeUse(0);
  return edgeUse->GetNumberOfAssociations(vtkModelVertexUseType);
}

//-----------------------------------------------------------------------------
vtkModelVertex* vtkModelEdge::GetAdjacentModelVertex(int which)
{
  if(vtkModelVertexUse* vertexUse =
     this->GetModelEdgeUse(1)->GetModelVertexUse(which))
    {
    return vertexUse->GetModelVertex();
    }
  return 0;
}

//-----------------------------------------------------------------------------
void vtkModelEdge::Initialize(vtkModelVertex* vertex0, vtkModelVertex* vertex1,
                              vtkIdType edgeId)
{
  vtkModelEdgeUse* edgeUse0 = vtkModelEdgeUse::New();
  vtkModelEdgeUse* edgeUse1 = vtkModelEdgeUse::New();
  edgeUse0->Initialize(vertex1, vertex0, edgeUse1, 0); //0 is for opposite direction
  edgeUse1->Initialize(vertex0, vertex1, edgeUse0, 1); //1 is for same direction
  this->AddAssociation(edgeUse0);
  edgeUse0->Delete();
  this->AddAssociation(edgeUse1);
  edgeUse1->Delete();
  this->SetUniquePersistentId(edgeId);
}

//-----------------------------------------------------------------------------
vtkModelEdgeUse* vtkModelEdge::BuildModelEdgeUsePair()
{
  if(this->GetNumberOfModelEdgeUses() == 0)
    {
    vtkErrorMacro("Must have an existing edge use before calling BuildModelEdgeUse.");
    return 0;
    }
  vtkModelEdgeUse* firstEdgeUse = this->GetModelEdgeUse(0);

  // first edge use is in opposite direction of edge
  vtkModelVertexUse* vertexUse1 = firstEdgeUse->GetModelVertexUse(0);
  vtkModelVertexUse* vertexUse0 = firstEdgeUse->GetModelVertexUse(1);
  vtkModelVertex *vertex0 = 0, *vertex1 = 0;
  if(vertexUse0)
    {
    vertex0 = vertexUse0->GetModelVertex();
    }
  if(vertexUse1)
    {
    vertex1 = vertexUse1->GetModelVertex();
    }

  vtkModelEdgeUse* edgeUse0 = vtkModelEdgeUse::New();
  vtkModelEdgeUse* edgeUse1 = vtkModelEdgeUse::New();
  edgeUse0->Initialize(vertex1, vertex0, edgeUse1, 0);
  edgeUse1->Initialize(vertex0, vertex1, edgeUse0, 1);
  this->AddAssociation(edgeUse0);
  this->AddAssociation(edgeUse1);
  edgeUse0->Delete();
  edgeUse1->Delete();
  return edgeUse1;
}

//-----------------------------------------------------------------------------
void vtkModelEdge::DestroyModelEdgeUse(vtkModelEdgeUse* edgeUse)
{
  this->RemoveAssociation(edgeUse);
  this->Modified();
}

//-----------------------------------------------------------------------------
void vtkModelEdge::SplitModelEdge(
  vtkModelVertex* newVertex, vtkModelEdge* newEdge)
{
  // make sure that we only store a single edge of the paired edges
  // that edge use is in the same direction as the model edge (dir=1)
  std::set<vtkModelEdgeUse*> edgeUsePairs;

  vtkModelItemIterator* edgeUses = this->NewIterator(vtkModelEdgeUseType);
  for(edgeUses->Begin();!edgeUses->IsAtEnd();edgeUses->Next())
    {
    vtkModelEdgeUse* edgeUse = vtkModelEdgeUse::SafeDownCast(
      edgeUses->GetCurrentItem());
    if(edgeUse->GetDirection() == 1)
      {
      edgeUsePairs.insert(edgeUse);
      }
    else
      {
      edgeUsePairs.insert(edgeUse->GetPairedModelEdgeUse());
      }
    }
  edgeUses->Delete();

  // we already have an existing edge use pair for the first pass
  std::set<vtkModelEdgeUse*>::iterator it=edgeUsePairs.begin();
  vtkModelEdgeUse* newEdgeUse = newEdge->GetModelEdgeUse(1);

  vtkModelVertexUse* originalVertexUse0 = (*it)->GetModelVertexUse(0);
  vtkModelVertexUse* originalVertexUse1 = (*it)->GetModelVertexUse(1);
  vtkModelVertexUse* vertexUse = newVertex->BuildModelVertexUse();
  this->SplitModelEdgeUse(*it, newEdgeUse, originalVertexUse0,
                          vertexUse, originalVertexUse1);
  int index = (*it)->GetModelLoopUse()->GetModelEdgeUseIndex(*it);
  (*it)->GetModelLoopUse()->InsertModelEdgeUse(index+1, newEdgeUse);

  originalVertexUse0 = (*it)->GetPairedModelEdgeUse()->GetModelVertexUse(0);
  originalVertexUse1 = (*it)->GetPairedModelEdgeUse()->GetModelVertexUse(1);
  vertexUse = newVertex->BuildModelVertexUse();
  vtkModelEdgeUse* oldPairedModelEdgeUse = (*it)->GetPairedModelEdgeUse();
  this->SplitModelEdgeUse(
    newEdgeUse->GetPairedModelEdgeUse(), oldPairedModelEdgeUse,
    originalVertexUse0, vertexUse, originalVertexUse1);
  index = oldPairedModelEdgeUse->GetModelLoopUse()
    ->GetModelEdgeUseIndex(oldPairedModelEdgeUse);
  oldPairedModelEdgeUse->GetModelLoopUse()->InsertModelEdgeUse(
    index, newEdgeUse->GetPairedModelEdgeUse());

  for(it++ ;it!=edgeUsePairs.end();it++)
    {
    newEdgeUse = newEdge->BuildModelEdgeUsePair();

    // BuildeModelEdgeUsePair creates model vertex uses that we don't
    // want so we remove them now
    vtkModelVertexUse* vertexUse0 = newEdgeUse->GetModelVertexUse(0);
    vtkModelVertexUse* vertexUse1 = newEdgeUse->GetModelVertexUse(1);
    newEdgeUse->SetModelVertexUses(0, 0);
    if(vertexUse0 && vertexUse0->GetNumberOfAssociations(vtkModelEdgeUseType) == 0)
      {
      vertexUse0->GetModelVertex()->DestroyModelVertexUse(vertexUse0);
      }
    if(vertexUse1 && vertexUse1 != vertexUse0 &&
       vertexUse1->GetNumberOfAssociations(vtkModelEdgeUseType) == 0)
      {
      vertexUse1->GetModelVertex()->DestroyModelVertexUse(vertexUse1);
      }

    vertexUse0 = newEdgeUse->GetPairedModelEdgeUse()->GetModelVertexUse(0);
    vertexUse1 = newEdgeUse->GetPairedModelEdgeUse()->GetModelVertexUse(1);
    newEdgeUse->GetPairedModelEdgeUse()->SetModelVertexUses(0, 0);
    if(vertexUse0 && vertexUse0->GetNumberOfAssociations(vtkModelEdgeUseType) == 0)
      {
      vertexUse0->GetModelVertex()->DestroyModelVertexUse(vertexUse0);
      }
    if(vertexUse1 && vertexUse1 != vertexUse0 &&
       vertexUse1->GetNumberOfAssociations(vtkModelEdgeUseType) == 0)
      {
      vertexUse1->GetModelVertex()->DestroyModelVertexUse(vertexUse1);
      }

    originalVertexUse0 = (*it)->GetModelVertexUse(0);
    originalVertexUse1 = (*it)->GetModelVertexUse(1);
    vertexUse = newVertex->BuildModelVertexUse();
    this->SplitModelEdgeUse(*it, newEdgeUse, originalVertexUse0,
                            vertexUse, originalVertexUse1);

    index = (*it)->GetModelLoopUse()->GetModelEdgeUseIndex(*it);
    (*it)->GetModelLoopUse()->InsertModelEdgeUse(index+1, newEdgeUse);

    originalVertexUse0 = (*it)->GetPairedModelEdgeUse()->GetModelVertexUse(0);
    originalVertexUse1 = (*it)->GetPairedModelEdgeUse()->GetModelVertexUse(1);
    vertexUse = newVertex->BuildModelVertexUse();
    this->SplitModelEdgeUse(
      newEdgeUse->GetPairedModelEdgeUse(), (*it)->GetPairedModelEdgeUse(),
      originalVertexUse0, vertexUse, originalVertexUse1);

    oldPairedModelEdgeUse = (*it)->GetPairedModelEdgeUse();
    index = oldPairedModelEdgeUse->GetModelLoopUse()
      ->GetModelEdgeUseIndex(oldPairedModelEdgeUse);
    oldPairedModelEdgeUse->GetModelLoopUse()->InsertModelEdgeUse(
      index, newEdgeUse->GetPairedModelEdgeUse());
    }
}

//-----------------------------------------------------------------------------
void vtkModelEdge::SplitModelEdgeUse(
  vtkModelEdgeUse* firstEdgeUse, vtkModelEdgeUse* secondEdgeUse,
  vtkModelVertexUse* vertexUse0, vtkModelVertexUse* vertexUse1,
  vtkModelVertexUse* vertexUse2)
{
  firstEdgeUse->SetModelVertexUses(vertexUse0, vertexUse1);
  secondEdgeUse->SetModelVertexUses(vertexUse1, vertexUse2);
}

//-----------------------------------------------------------------------------
bool vtkModelEdge::SplitModelEdgeLoop(vtkModelVertex* vertex)
{
  vtkModelItemIterator* edgeUses = this->NewIterator(vtkModelEdgeUseType);
  for(edgeUses->Begin();!edgeUses->IsAtEnd();edgeUses->Next())
    {
    vtkModelVertexUse* vertexUse = vertex->BuildModelVertexUse();
    vtkModelEdgeUse* edgeUse =
      vtkModelEdgeUse::SafeDownCast(edgeUses->GetCurrentItem());
    edgeUse->SetModelVertexUses(vertexUse, vertexUse);
    }
  edgeUses->Delete();
  this->GetModel()->InvokeModelGeometricEntityEvent(
    ModelGeometricEntityBoundaryModified, this);
  return 1;
}

//-----------------------------------------------------------------------------
int vtkModelEdge::GetNumberOfAdjacentModelFaces()
{
  std::set<vtkModelFace*> faces;
  vtkModelItemIterator* edgeUses =
    this->NewIterator(vtkModelEdgeUseType);
  for(edgeUses->Begin();!edgeUses->IsAtEnd();edgeUses->Next())
    {
    vtkModelItemIterator* loopUses =
      edgeUses->GetCurrentItem()->NewIterator(vtkModelLoopUseType);
    for(loopUses->Begin();!loopUses->IsAtEnd();loopUses->Next())
      {
      faces.insert(vtkModelLoopUse::SafeDownCast(loopUses->GetCurrentItem())->
                   GetModelFace());
      }
    loopUses->Delete();
    }
  edgeUses->Delete();

  return static_cast<int>(faces.size());
}

//-----------------------------------------------------------------------------
vtkModelItemIterator* vtkModelEdge::NewAdjacentModelFaceIterator()
{
  vtkModelItemGenericIterator* faces = vtkModelItemGenericIterator::New();
  vtkModelItemIterator* edgeUses =
    this->NewIterator(vtkModelEdgeUseType);
  for(edgeUses->Begin();!edgeUses->IsAtEnd();edgeUses->Next())
    {
    vtkModelItemIterator* loopUses =
      edgeUses->GetCurrentItem()->NewIterator(vtkModelLoopUseType);
    for(loopUses->Begin();!loopUses->IsAtEnd();loopUses->Next())
      {
      faces->AddUniqueModelItem(
        vtkModelLoopUse::SafeDownCast(loopUses->GetCurrentItem())->
        GetModelFace());
      }
    loopUses->Delete();
    }
  edgeUses->Delete();

  return faces;
}

//-----------------------------------------------------------------------------
void vtkModelEdge::Serialize(vtkSerializer* ser)
{
  this->Superclass::Serialize(ser);
}

//-----------------------------------------------------------------------------
void vtkModelEdge::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
}

