// Created on: 1995-11-20
// Created by: Laurent BOURESCHE
// Copyright (c) 1995-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 <Law.hxx>

#include <Adaptor3d_Curve.hxx>
#include <BSplCLib.hxx>
#include <gp_Pnt.hxx>
#include <Law_BSpFunc.hxx>
#include <Law_BSpline.hxx>
#include <Law_Interpolate.hxx>
#include <Law_Linear.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <TColStd_HArray1OfBoolean.hxx>
#include <TColStd_HArray1OfReal.hxx>

Handle(Law_BSpFunc) Law::MixBnd(const Handle(Law_Linear)& Lin)
{
  Standard_Real f, l;
  Lin->Bounds(f, l);
  TColStd_Array1OfReal    Knots(1, 4);
  TColStd_Array1OfInteger Mults(1, 4);
  Knots(1) = f;
  Knots(4) = l;
  Knots(2) = 0.75 * f + 0.25 * l;
  Knots(3) = 0.25 * f + 0.75 * l;
  Mults(1) = Mults(4) = 4;
  Mults(2) = Mults(3)               = 1;
  Handle(TColStd_HArray1OfReal) pol = Law::MixBnd(3, Knots, Mults, Lin);
  Handle(Law_BSpline)           bs  = new Law_BSpline(pol->Array1(), Knots, Mults, 3);
  Handle(Law_BSpFunc)           bsf = new Law_BSpFunc();
  bsf->SetCurve(bs);
  return bsf;
}

Handle(TColStd_HArray1OfReal) Law::MixBnd(const Standard_Integer         Degree,
                                          const TColStd_Array1OfReal&    Knots,
                                          const TColStd_Array1OfInteger& Mults,
                                          const Handle(Law_Linear)&      Lin)
{
  Standard_Integer nbpol = 0, nbfk = 0, i, j, k = 0;
  for (i = Mults.Lower(); i <= Mults.Upper(); i++)
  {
    nbfk += Mults(i);
  }
  TColStd_Array1OfReal fk(1, nbfk);
  for (i = Mults.Lower(); i <= Mults.Upper(); i++)
  {
    for (j = 1; j <= Mults(i); j++)
    {
      fk(++k) = Knots(i);
    }
  }
  nbpol = nbfk - Degree - 1;
  TColStd_Array1OfReal par(1, nbpol);
  BSplCLib::BuildSchoenbergPoints(Degree, fk, par);
  Handle(TColStd_HArray1OfReal) res = new TColStd_HArray1OfReal(1, nbpol);
  TColStd_Array1OfReal&         pol = res->ChangeArray1();
  for (i = 1; i <= nbpol; i++)
  {
    pol(i) = Lin->Value(par(i));
  }
  TColStd_Array1OfInteger ord(1, nbpol);
  ord.Init(0);
  BSplCLib::Interpolate(Degree, fk, par, ord, 1, pol(1), i);
  if (nbpol >= 4)
  {
    pol(2)         = pol(1);
    pol(nbpol - 1) = pol(nbpol);
  }
  return res;
}

static Standard_Real eval1(const Standard_Real    p,
                           const Standard_Real    first,
                           const Standard_Real    last,
                           const Standard_Real    piv,
                           const Standard_Boolean nulr)
{
  if ((nulr && p >= piv) || (!nulr && p <= piv))
    return 0.;
  else if (nulr)
  {
    Standard_Real a = piv - first;
    a *= a;
    a               = 1. / a;
    Standard_Real b = p - first;
    a *= b;
    b = piv - p;
    a *= b;
    a *= b;
    return a;
  }
  else
  {
    Standard_Real a = last - piv;
    a *= a;
    a               = 1. / a;
    Standard_Real b = last - p;
    a *= b;
    b = p - piv;
    a *= b;
    a *= b;
    return a;
  }
}

Handle(TColStd_HArray1OfReal) Law::MixTgt(const Standard_Integer         Degree,
                                          const TColStd_Array1OfReal&    Knots,
                                          const TColStd_Array1OfInteger& Mults,
                                          const Standard_Boolean         NulOnTheRight,
                                          const Standard_Integer         Index)
{
  Standard_Real    first = Knots(Knots.Lower());
  Standard_Real    last  = Knots(Knots.Upper());
  Standard_Real    piv   = Knots(Index);
  Standard_Integer nbpol = 0, nbfk = 0, i, j, k = 0;
  for (i = Mults.Lower(); i <= Mults.Upper(); i++)
  {
    nbfk += Mults(i);
  }
  TColStd_Array1OfReal fk(1, nbfk);
  for (i = Mults.Lower(); i <= Mults.Upper(); i++)
  {
    for (j = 1; j <= Mults(i); j++)
    {
      fk(++k) = Knots(i);
    }
  }
  nbpol = nbfk - Degree - 1;
  TColStd_Array1OfReal par(1, nbpol);
  BSplCLib::BuildSchoenbergPoints(Degree, fk, par);
  Handle(TColStd_HArray1OfReal) res = new TColStd_HArray1OfReal(1, nbpol);
  TColStd_Array1OfReal&         pol = res->ChangeArray1();
  for (i = 1; i <= nbpol; i++)
  {
    pol(i) = eval1(par(i), first, last, piv, NulOnTheRight);
  }
  TColStd_Array1OfInteger ord(1, nbpol);
  ord.Init(0);
  BSplCLib::Interpolate(Degree, fk, par, ord, 1, pol(1), i);
  return res;
}

Handle(Law_BSpline) Law::Reparametrize(const Adaptor3d_Curve& Curve,
                                       const Standard_Real    First,
                                       const Standard_Real    Last,
                                       const Standard_Boolean HasDF,
                                       const Standard_Boolean HasDL,
                                       const Standard_Real    DFirst,
                                       const Standard_Real    DLast,
                                       const Standard_Boolean Rev,
                                       const Standard_Integer NbPoints)
{
  // On evalue la longeur approximative de la courbe.

  Standard_Integer i;
  Standard_Real    DDFirst = DFirst, DDLast = DLast;
  if (HasDF && Rev)
    DDFirst = -DFirst;
  if (HasDL && Rev)
    DDLast = -DLast;
  TColStd_Array1OfReal cumdist(1, 2 * NbPoints);
  TColStd_Array1OfReal ucourbe(1, 2 * NbPoints);
  gp_Pnt               P1, P2;
  Standard_Real        U1 = Curve.FirstParameter();
  Standard_Real        U2 = Curve.LastParameter();
  Standard_Real        U, DU, Length = 0.;
  if (!Rev)
  {
    P1 = Curve.Value(U1);
    U  = U1;
    DU = (U2 - U1) / (2 * NbPoints - 1);
  }
  else
  {
    P1 = Curve.Value(U2);
    U  = U2;
    DU = (U1 - U2) / (2 * NbPoints - 1);
  }
  for (i = 1; i <= 2 * NbPoints; i++)
  {
    P2 = Curve.Value(U);
    Length += P2.Distance(P1);
    cumdist(i) = Length;
    ucourbe(i) = U;
    U += DU;
    P1 = P2;
  }
  if (Rev)
    ucourbe(2 * NbPoints) = U1;
  else
    ucourbe(2 * NbPoints) = U2;

  Handle(TColStd_HArray1OfReal) point = new TColStd_HArray1OfReal(1, NbPoints);
  Handle(TColStd_HArray1OfReal) param = new TColStd_HArray1OfReal(1, NbPoints);

  Standard_Real    DCorde = Length / (NbPoints - 1);
  Standard_Real    Corde  = DCorde;
  Standard_Integer Index  = 1;
  Standard_Real    Alpha;
  Standard_Real    fac = 1. / (NbPoints - 1);

  point->SetValue(1, ucourbe(1));
  param->SetValue(1, First);
  point->SetValue(NbPoints, ucourbe(2 * NbPoints));
  param->SetValue(NbPoints, Last);

  for (i = 2; i < NbPoints; i++)
  {
    while (cumdist(Index) < Corde)
      Index++;

    Alpha = (Corde - cumdist(Index - 1)) / (cumdist(Index) - cumdist(Index - 1));
    U     = ucourbe(Index - 1) + Alpha * (ucourbe(Index) - ucourbe(Index - 1));
    point->SetValue(i, U);
    param->SetValue(i, ((NbPoints - i) * First + (i - 1) * Last) * fac);
    Corde = i * DCorde;
  }
  Law_Interpolate inter(point, param, 0, 1.e-9);
  if (HasDF || HasDL)
  {
    TColStd_Array1OfReal             tgt(1, NbPoints);
    Handle(TColStd_HArray1OfBoolean) flag = new TColStd_HArray1OfBoolean(1, NbPoints);
    flag->ChangeArray1().Init(0);
    if (HasDF)
    {
      flag->SetValue(1, 1);
      tgt.SetValue(1, DDFirst);
    }
    if (HasDL)
    {
      flag->SetValue(NbPoints, 1);
      tgt.SetValue(NbPoints, DDLast);
    }
    inter.Load(tgt, flag);
  }
  inter.Perform();
  if (!inter.IsDone())
    throw Standard_Failure("Law::Reparametrize echec interpolation");
  Handle(Law_BSpline) bs = inter.Curve();
  return bs;
}

static Standard_Real eval2(const Standard_Real p,
                           //			   const Standard_Real        first,
                           const Standard_Real,
                           //			   const Standard_Real        last,
                           const Standard_Real,
                           const Standard_Real        mil,
                           const Standard_Boolean     hasfirst,
                           const Standard_Boolean     haslast,
                           const Handle(Law_BSpline)& bs1,
                           const Handle(Law_BSpline)& bs2)
{
  if (hasfirst && p < mil)
    return bs1->Value(p);
  else if (haslast && p > mil)
    return bs2->Value(p);
  else
    return 1.;
}

Handle(Law_BSpline) Law::Scale(const Standard_Real    First,
                               const Standard_Real    Last,
                               const Standard_Boolean HasF,
                               const Standard_Boolean HasL,
                               const Standard_Real    VFirst,
                               const Standard_Real    VLast)
{
  Standard_Integer        i;
  Standard_Real           Milieu = 0.5 * (First + Last);
  TColStd_Array1OfReal    knot(1, 3);
  TColStd_Array1OfReal    fknot(1, 10);
  TColStd_Array1OfInteger mult(1, 3);
  knot(1)  = First;
  knot(2)  = Milieu;
  knot(3)  = Last;
  fknot(1) = fknot(2) = fknot(3) = fknot(4) = First;
  fknot(10) = fknot(9) = fknot(8) = fknot(7) = Last;
  fknot(5) = fknot(6) = Milieu;
  mult(1)             = 4;
  mult(3)             = 4;
  mult(2)             = 2;

  TColStd_Array1OfReal    pbs(1, 4);
  TColStd_Array1OfReal    kbs(1, 2);
  TColStd_Array1OfInteger mbs(1, 2);
  mbs(1) = mbs(2) = 4;
  Handle(Law_BSpline) bs1, bs2;
  if (HasF)
  {
    pbs(1) = pbs(2) = VFirst;
    pbs(3) = pbs(4) = 1.;
    kbs(1)          = First;
    kbs(2)          = Milieu;
    bs1             = new Law_BSpline(pbs, kbs, mbs, 3);
  }
  if (HasL)
  {
    pbs(1) = pbs(2) = 1.;
    pbs(3) = pbs(4) = VLast;
    kbs(1)          = Milieu;
    kbs(2)          = Last;
    bs2             = new Law_BSpline(pbs, kbs, mbs, 3);
  }

  TColStd_Array1OfReal pol(1, 6);
  TColStd_Array1OfReal par(1, 6);
  BSplCLib::BuildSchoenbergPoints(3, fknot, par);
  for (i = 1; i <= 6; i++)
  {
    pol(i) = eval2(par(i), First, Last, Milieu, HasF, HasL, bs1, bs2);
  }
  TColStd_Array1OfInteger ord(1, 6);
  ord.Init(0);
  BSplCLib::Interpolate(3, fknot, par, ord, 1, pol(1), i);
  bs1 = new Law_BSpline(pol, knot, mult, 3);
  return bs1;
}

Handle(Law_BSpline) Law::ScaleCub(const Standard_Real    First,
                                  const Standard_Real    Last,
                                  const Standard_Boolean HasF,
                                  const Standard_Boolean HasL,
                                  const Standard_Real    VFirst,
                                  const Standard_Real    VLast)
{
  // Standard_Integer i;
  Standard_Real           Milieu = 0.5 * (First + Last);
  TColStd_Array1OfReal    pol(1, 5);
  TColStd_Array1OfReal    knot(1, 3);
  TColStd_Array1OfInteger mult(1, 3);
  knot(1) = First;
  knot(2) = Milieu;
  knot(3) = Last;
  mult(1) = 4;
  mult(3) = 4;
  mult(2) = 1;

  Handle(Law_BSpline) bs;
  if (HasF)
  {
    pol(1) = pol(2) = VFirst;
  }
  else
  {
    pol(1) = pol(2) = 1.;
  }
  if (HasL)
  {
    pol(4) = pol(5) = VLast;
  }
  else
  {
    pol(4) = pol(5) = 1.;
  }

  pol(3) = 1.;

  bs = new Law_BSpline(pol, knot, mult, 3);
  return bs;
}
