// Created on: 1999-05-21
// Created by: Denis PASCAL
// Copyright (c) 1999-1999 Matra Datavision
// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#include <TDocStd_XLinkTool.hxx>

#include <TDataStd_TreeNode.hxx>
#include <TDF_ClosureMode.hxx>
#include <TDF_ClosureTool.hxx>
#include <TDF_CopyTool.hxx>
#include <TDF_DataSet.hxx>
#include <TDF_IDFilter.hxx>
#include <TDF_Label.hxx>
#include <TDF_Reference.hxx>
#include <TDF_RelocationTable.hxx>
#include <TDF_Tool.hxx>
#include <TDocStd_Document.hxx>
#include <TDocStd_XLink.hxx>

// standard copy
//=================================================================================================

TDocStd_XLinkTool::TDocStd_XLinkTool()
{
  isDone = false;
  myRT   = new TDF_RelocationTable();
}

//=================================================================================================

void TDocStd_XLinkTool::Copy(const TDF_Label& target, const TDF_Label& source)
{
  occ::handle<TDocStd_Document> TARGET, SOURCE;
  TARGET = TDocStd_Document::Get(target);
  SOURCE = TDocStd_Document::Get(source);
  if (TARGET != SOURCE)
  {
    if (!TDF_Tool::IsSelfContained(source))
    {
      throw Standard_DomainError("TDocStd_XLinkTool::Copy : not self-contained");
    }
  }

  // Remove TreeNode, then restore, if present
  occ::handle<TDataStd_TreeNode> aFather, aPrev, aNext;
  occ::handle<TDataStd_TreeNode> anOldFather, anOldPrev, anOldNext;
  occ::handle<TDataStd_TreeNode> aNode, anOldNode;
  if (TDataStd_TreeNode::Find(source, aNode))
  {
    aFather = aNode->Father();
    aPrev   = aNode->Previous();
    aNext   = aNode->Next();
    aNode->Remove();
  }
  if (TDataStd_TreeNode::Find(target, anOldNode))
  {
    anOldFather = anOldNode->Father();
    anOldPrev   = anOldNode->Previous();
    anOldNext   = anOldNode->Next();
    anOldNode->Remove();
  }

  myRT                        = new TDF_RelocationTable(true);
  myDS                        = new TDF_DataSet;
  occ::handle<TDF_DataSet> DS = new TDF_DataSet();
  TDF_ClosureMode          mode(true); // descendant plus reference
  myDS->AddLabel(source);
  myRT->SetRelocation(source, target);
  TDF_IDFilter filter(false); // on prend tout
  TDF_ClosureTool::Closure(myDS, filter, mode);
  TDF_CopyTool::Copy(myDS, myRT);
  // NCollection_DataMap<TopoDS_Shape, TopoDS_Shape, TopTools_ShapeMapHasher> M; // removed to avoid
  // dependence with TNaming TNaming::ChangeShapes(target,M);// should be used as postfix after Copy

  if (!aNode.IsNull())
  {
    if (!aPrev.IsNull())
      aPrev->InsertAfter(aNode);
    else if (!aNext.IsNull())
    {
      aNext->InsertBefore(aNode);
    }
    else if (!aFather.IsNull())
      aNode->SetFather(aFather);
  }

  if (!anOldNode.IsNull())
  {
    if (TDataStd_TreeNode::Find(target, anOldNode))
    {
      if (!anOldPrev.IsNull())
        anOldPrev->InsertAfter(anOldNode);
      else if (!anOldNext.IsNull())
      {
        anOldNext->InsertBefore(anOldNode);
      }
      else if (!anOldFather.IsNull())
        anOldNode->SetFather(anOldFather);
    }
  }

  isDone = true;
}

//=================================================================================================

void TDocStd_XLinkTool::CopyWithLink(const TDF_Label& target, const TDF_Label& source)
{
  occ::handle<TDF_Reference> REF;
  if (target.FindAttribute(TDF_Reference::GetID(), REF))
  {
    throw Standard_DomainError(" TDocStd_CopyWithLink : already a ref");
  }
  Copy(target, source);
  if (isDone)
  {
    TCollection_AsciiString xlabelentry, xdocentry;
    TDF_Tool::Entry(source, xlabelentry);
    occ::handle<TDocStd_Document> aSourceD  = TDocStd_Document::Get(source);
    occ::handle<TDocStd_Document> aTargetD  = TDocStd_Document::Get(target);
    int                           aDocEntry = 0;
    if (aSourceD != aTargetD)
      aDocEntry = aTargetD->CreateReference(aSourceD);
    xdocentry = aDocEntry;

    occ::handle<TDocStd_XLink> X = TDocStd_XLink::Set(target);
    X->LabelEntry(xlabelentry);
    X->DocumentEntry(xdocentry);
    TDF_Reference::Set(target, source);
    isDone = true;
  }
}

//=================================================================================================

void TDocStd_XLinkTool::UpdateLink(const TDF_Label& label)
{
  occ::handle<TDF_Reference> REF;
  if (!label.FindAttribute(TDF_Reference::GetID(), REF))
  {
    throw Standard_DomainError(" TDocStd_XLinkTool::UpdateLink : not ref registered");
  }
  TDocStd_XLinkTool XLinkTool;
  Copy(label, REF->Get());
}

//=================================================================================================

bool TDocStd_XLinkTool::IsDone() const
{
  return isDone;
}

//=================================================================================================

occ::handle<TDF_RelocationTable> TDocStd_XLinkTool::RelocationTable() const
{
  return myRT;
}

//=================================================================================================

occ::handle<TDF_DataSet> TDocStd_XLinkTool::DataSet() const
{
  return myDS;
}
