// Created on: 1991-06-24
// Created by: Didier PIFFAULT
// Copyright (c) 1991-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 <Intf_TangentZone.hxx>

#define DEBUG_TANGENTZONE 0

//=======================================================================
// function : Intf_TangentZone
// purpose  : Constructor of an empty tangent zone.
//=======================================================================

Intf_TangentZone::Intf_TangentZone()
{
  ParamOnFirstMin = ParamOnSecondMin = RealLast();
  ParamOnFirstMax = ParamOnSecondMax = RealFirst();
}

//=======================================================================
// function : Append
// purpose  : Append the section point to the tangent zone.
//=======================================================================

void Intf_TangentZone::Append(const Intf_SectionPoint& Pi)
{
  Result.Append(Pi);
  if (ParamOnFirstMin > Pi.ParamOnFirst())
    ParamOnFirstMin = Pi.ParamOnFirst();
  if (ParamOnSecondMin > Pi.ParamOnSecond())
    ParamOnSecondMin = Pi.ParamOnSecond();

  if (ParamOnFirstMax < Pi.ParamOnFirst())
    ParamOnFirstMax = Pi.ParamOnFirst();
  if (ParamOnSecondMax < Pi.ParamOnSecond())
    ParamOnSecondMax = Pi.ParamOnSecond();
}

//=======================================================================
// function : Append
// purpose  : Merge the TangentZone to the tangent zone.
//=======================================================================

void Intf_TangentZone::Append(const Intf_TangentZone& Tzi)
{
  Standard_Integer Tzi_NumberOfPoints = Tzi.NumberOfPoints();
  for (Standard_Integer ipi = 1; ipi <= Tzi_NumberOfPoints; ipi++)
  {
    PolygonInsert(Tzi.GetPoint(ipi));
  }
}

//=======================================================================
// function : Insert
// purpose  : Insert the section point at his place in the tangent zone.
//=======================================================================

// Standard_Boolean Intf_TangentZone::Insert (const Intf_SectionPoint& Pi)
Standard_Boolean Intf_TangentZone::Insert(const Intf_SectionPoint&)
{
#if DEBUG_TANGENTZONE
  std::cout << " Standard_Boolean Intf_TangentZone::Insert (const Intf_SectionPoint& Pi) ???? "
            << std::endl;
#endif
  Standard_Boolean Inserted = Standard_False;
  /*
    Standard_Integer lpon=0;

    if (Result.Length()<1) {
      Append(Pi);
      Inserted=Standard_True;
    }
    else if (Result.Length()==1) {
      if (Pi.IsOnSameEdge(Result(1))) {
        InsertAfter(1, Pi);
        Inserted=Standard_True;
      }
    }
    else {
      Standard_Integer lp1, lp2;
      Standard_Integer nbptz=NumberOfPoints();
      for (lp1=1; lp1<=nbptz; lp1++) {
        lp2=(lp1%nbptz)+1;
        if (Pi.IsOnSameEdge(Result(lp1))) {
      lpon=lp1;
      if (Pi.IsOnSameEdge(Result(lp2))) {
        InsertAfter(lp1, Pi);
        Inserted=Standard_True;
        break;
      }
        }
      }
    }
    if (!Inserted && lpon>0) {
      InsertAfter(lpon, Pi);
      Inserted=Standard_True;
    }
  */
  return Inserted;
}

//=======================================================================
// function : PolygonInsert
// purpose  : Insert the point at his place in the polygonal tangent zone.
//=======================================================================

void Intf_TangentZone::PolygonInsert(const Intf_SectionPoint& Pi)
{
  //  Standard_Boolean  Inserted=Standard_False;
  Standard_Integer nbpTz = NumberOfPoints();
  //  Standard_Integer lpi;
  if (nbpTz == 0)
  {
    Append(Pi);
    return;
  }
  if (Pi.ParamOnFirst() >= ParamOnFirstMax)
  {
    Append(Pi);
  }
  else if (Pi.ParamOnFirst() >= ParamOnFirstMin)
  {
    InsertBefore(1, Pi);
  }
  else
  {
    /*----- Trop Long lbr le 13 mai 97
        Standard_Real PiParamOnFirst  = Pi.ParamOnFirst();
        Standard_Real PiParamOnSecond = Pi.ParamOnSecond();
        for (lpi=1; lpi<=nbpTz; lpi++) {
          const Intf_SectionPoint& Resultlpi = Result(lpi);
          if (PiParamOnFirst<Resultlpi.ParamOnFirst()) {
        InsertBefore(lpi, Pi);
        Inserted=Standard_True;
        break;
          }
          else if (PiParamOnFirst==Resultlpi.ParamOnFirst()) {
        if (PiParamOnSecond==Resultlpi.ParamOnSecond()) {
          Inserted=Standard_True;
          break;
        }
          }
        }
        if (!Inserted) {
          Append(Pi);
        }
    ------ */
    Append(Pi); //-- On met les points sans les classer
                //-- si on veut on pourra les reclasser
                //-- ensuite avec un TRI.
  }
}

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

void Intf_TangentZone::InsertAfter(const Standard_Integer Index, const Intf_SectionPoint& Pi)
{
  Result.InsertAfter(Index, Pi);
  if (ParamOnFirstMin > Pi.ParamOnFirst())
    ParamOnFirstMin = Pi.ParamOnFirst();
  if (ParamOnSecondMin > Pi.ParamOnSecond())
    ParamOnSecondMin = Pi.ParamOnSecond();

  if (ParamOnFirstMax < Pi.ParamOnFirst())
    ParamOnFirstMax = Pi.ParamOnFirst();
  if (ParamOnSecondMax < Pi.ParamOnSecond())
    ParamOnSecondMax = Pi.ParamOnSecond();
}

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

void Intf_TangentZone::InsertBefore(const Standard_Integer Index, const Intf_SectionPoint& Pi)
{
  Result.InsertBefore(Index, Pi);
  if (ParamOnFirstMin > Pi.ParamOnFirst())
    ParamOnFirstMin = Pi.ParamOnFirst();
  if (ParamOnSecondMin > Pi.ParamOnSecond())
    ParamOnSecondMin = Pi.ParamOnSecond();

  if (ParamOnFirstMax < Pi.ParamOnFirst())
    ParamOnFirstMax = Pi.ParamOnFirst();
  if (ParamOnSecondMax < Pi.ParamOnSecond())
    ParamOnSecondMax = Pi.ParamOnSecond();
}

//=======================================================================
// function : GetPoint
// purpose  : Return the section point of range index in the tangent zone.
//=======================================================================

const Intf_SectionPoint& Intf_TangentZone::GetPoint(const Standard_Integer Index) const
{
  return Result(Index);
}

//=======================================================================
// function : IsEqual
// purpose  : Compare two tangent zone.
//=======================================================================

Standard_Boolean Intf_TangentZone::IsEqual(const Intf_TangentZone& Other) const
{
  if (Result.Length() != Other.Result.Length())
    return Standard_False;
  Standard_Integer i;
  for (i = 1; i <= Result.Length(); i++)
  {
    if (!Result(i).IsEqual(Other.Result(i)))
      return Standard_False;
  }
  return Standard_True;
}

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

Standard_Boolean Intf_TangentZone::Contains(const Intf_SectionPoint& ThePI) const
{
  Standard_Integer i;
  for (i = 1; i <= Result.Length(); i++)
    if (ThePI.IsEqual(Result(i)))
      return Standard_True;
  return Standard_False;
}

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

void Intf_TangentZone::InfoFirst(Standard_Integer& segMin,
                                 Standard_Real&    paraMin,
                                 Standard_Integer& segMax,
                                 Standard_Real&    paraMax) const
{
  ParamOnFirst(paraMin, paraMax);
  segMin  = (Standard_Integer)(IntegerPart(paraMin));
  paraMin = paraMin - (Standard_Real)segMin;
  segMax  = (Standard_Integer)(IntegerPart(paraMax));
  paraMax = paraMax - (Standard_Real)segMax;
}

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

void Intf_TangentZone::InfoSecond(Standard_Integer& segMin,
                                  Standard_Real&    paraMin,
                                  Standard_Integer& segMax,
                                  Standard_Real&    paraMax) const
{
  ParamOnSecond(paraMin, paraMax);
  segMin  = (Standard_Integer)(IntegerPart(paraMin));
  paraMin = paraMin - (Standard_Real)segMin;
  segMax  = (Standard_Integer)(IntegerPart(paraMax));
  paraMax = paraMax - (Standard_Real)segMax;
}

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

Standard_Boolean Intf_TangentZone::RangeContains(const Intf_SectionPoint& ThePI) const
{
  Standard_Real a, b, c, d;
  ParamOnFirst(a, b);
  ParamOnSecond(c, d);
  if (a <= ThePI.ParamOnFirst() && ThePI.ParamOnFirst() <= b && c <= ThePI.ParamOnSecond()
      && ThePI.ParamOnSecond() <= d)
    return Standard_True;
  return Standard_False;
}

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

Standard_Boolean Intf_TangentZone::HasCommonRange(const Intf_TangentZone& Other) const
{
  Standard_Real a1, b1, c1, d1;
  Standard_Real a2, b2, c2, d2;
  ParamOnFirst(a1, b1);
  ParamOnSecond(a2, b2);
  Other.ParamOnFirst(c1, d1);
  Other.ParamOnSecond(c2, d2);

  if (((c1 <= a1 && a1 <= d1) || (c1 <= b1 && b1 <= d1) || (a1 <= c1 && c1 <= b1))
      && ((c2 <= a2 && a2 <= d2) || (c2 <= b2 && b2 <= d2) || (a2 <= c2 && c2 <= b2)))
    return Standard_True;
  return Standard_False;
}

//=======================================================================
// function : Dump
// purpose  : Dump the TangentZone.
//=======================================================================

void Intf_TangentZone::Dump(const Standard_Integer /*Indent*/) const
{
#if DEBUG_TANGENTZONE
  for (Standard_Integer id = 0; id < Indent; id++)
    std::cout << " ";
  std::cout << "TZ \n";
  std::cout << "  ParamOnFirstMin Max    : " << ParamOnFirstMin << " " << ParamOnFirstMax
            << std::endl;
  std::cout << "  ParamOnSecondMin Max   : " << ParamOnSecondMin << " " << ParamOnSecondMax
            << std::endl;
  for (Standard_Integer p = 1; p <= Result.Length(); p++)
  {
    Result(p).Dump(Indent + 2);
  }
#endif
}
