// Created on: 1996-07-02
// Created by: Joelle CHAUVET
// Copyright (c) 1996-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 <AdvApp2Var_Iso.hxx>

#include <AdvApp2Var_ApproxF2var.hxx>
#include <AdvApp2Var_Context.hxx>
#include <AdvApp2Var_Node.hxx>
#include <gp_Pnt.hxx>
#include <TColStd_HArray1OfInteger.hxx>
#include <TColStd_HArray1OfReal.hxx>
#include <TColStd_HArray2OfReal.hxx>

IMPLEMENT_STANDARD_RTTIEXT(AdvApp2Var_Iso, Standard_Transient)

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

AdvApp2Var_Iso::AdvApp2Var_Iso()
    : myType(GeomAbs_IsoU),
      myConstPar(0.5),
      myU0(0.),
      myU1(1.),
      myV0(0.),
      myV1(1.),
      myPosition(0),
      myExtremOrder(2),
      myDerivOrder(2),
      myNbCoeff(0),
      myApprIsDone(Standard_False),
      myHasResult(Standard_False)
{
}

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

AdvApp2Var_Iso::AdvApp2Var_Iso(const GeomAbs_IsoType  type,
                               const Standard_Real    cte,
                               const Standard_Real    Ufirst,
                               const Standard_Real    Ulast,
                               const Standard_Real    Vfirst,
                               const Standard_Real    Vlast,
                               const Standard_Integer pos,
                               const Standard_Integer iu,
                               const Standard_Integer iv)
    : myType(type),
      myConstPar(cte),
      myU0(Ufirst),
      myU1(Ulast),
      myV0(Vfirst),
      myV1(Vlast),
      myPosition(pos),
      myNbCoeff(0),
      myApprIsDone(Standard_False),
      myHasResult(Standard_False)
{
  if (myType == GeomAbs_IsoU)
  {
    myExtremOrder = iv;
    myDerivOrder  = iu;
  }
  else
  {
    myExtremOrder = iu;
    myDerivOrder  = iv;
  }
}

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

Standard_Boolean AdvApp2Var_Iso::IsApproximated() const
{
  return myApprIsDone;
}

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

Standard_Boolean AdvApp2Var_Iso::HasResult() const
{
  return myHasResult;
}

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

void AdvApp2Var_Iso::MakeApprox(const AdvApp2Var_Context&           Conditions,
                                const Standard_Real                 U0,
                                const Standard_Real                 U1,
                                const Standard_Real                 V0,
                                const Standard_Real                 V1,
                                const AdvApp2Var_EvaluatorFunc2Var& Func,
                                AdvApp2Var_Node&                    NodeBegin,
                                AdvApp2Var_Node&                    NodeEnd)
{
  // fixed values
  Standard_Integer NBCRMX = 1, NBCRBE;
  // data stored in the Context
  Standard_Integer NDIMEN, NBSESP, NDIMSE;
  NDIMEN = Conditions.TotalDimension();
  NBSESP = Conditions.TotalNumberSSP();
  // Attention : works only in 3D
  NDIMSE = 3;
  // the domain of the grid
  Standard_Real UVFONC[4];
  UVFONC[0] = U0;
  UVFONC[1] = U1;
  UVFONC[2] = V0;
  UVFONC[3] = V1;

  // data related to the processed iso
  Standard_Integer IORDRE = myExtremOrder, IDERIV = myDerivOrder;
  Standard_Real    TCONST = myConstPar;

  // data related to the type of the iso
  Standard_Integer              ISOFAV = 0, NBROOT = 0, NDGJAC = 0, NCFLIM = 1;
  Standard_Real                 TABDEC[2];
  Handle(TColStd_HArray1OfReal) HUROOT = Conditions.URoots();
  Handle(TColStd_HArray1OfReal) HVROOT = Conditions.VRoots();
  Standard_Real*                ROOTLG = NULL;
  switch (myType)
  {
    case GeomAbs_IsoV:
      ISOFAV    = 2;
      TABDEC[0] = myU0;
      TABDEC[1] = myU1;
      UVFONC[0] = myU0;
      UVFONC[1] = myU1;
      NBROOT    = (Conditions.URoots())->Length();
      if (myExtremOrder > -1)
        NBROOT -= 2;
      ROOTLG = (Standard_Real*)&HUROOT->ChangeArray1()(HUROOT->Lower());
      NDGJAC = Conditions.UJacDeg();
      NCFLIM = Conditions.ULimit();
      break;
    case GeomAbs_IsoU:
      ISOFAV    = 1;
      TABDEC[0] = myV0;
      TABDEC[1] = myV1;
      UVFONC[2] = myV0;
      UVFONC[3] = myV1;
      NBROOT    = (Conditions.VRoots())->Length();
      if (myExtremOrder > -1)
        NBROOT -= 2;
      ROOTLG = (Standard_Real*)&HVROOT->ChangeArray1()(HVROOT->Lower());
      NDGJAC = Conditions.VJacDeg();
      NCFLIM = Conditions.VLimit();
      break;
      // #ifndef OCCT_DEBUG
      // pkv f
    case GeomAbs_NoneIso:
      // pkv t
    default:
      break;
      // #endif
  }

  // data relative to the position of iso (front or cut line)
  Handle(TColStd_HArray1OfReal) HEPSAPR = new TColStd_HArray1OfReal(1, NBSESP);
  Standard_Integer              iesp;
  switch (myPosition)
  {
    case 0:
      for (iesp = 1; iesp <= NBSESP; iesp++)
      {
        HEPSAPR->SetValue(iesp, (Conditions.CToler())->Value(iesp, 1));
      }
      break;
    case 1:
      for (iesp = 1; iesp <= NBSESP; iesp++)
      {
        HEPSAPR->SetValue(iesp, (Conditions.FToler())->Value(iesp, 1));
      }
      break;
    case 2:
      for (iesp = 1; iesp <= NBSESP; iesp++)
      {
        HEPSAPR->SetValue(iesp, (Conditions.FToler())->Value(iesp, 2));
      }
      break;
    case 3:
      for (iesp = 1; iesp <= NBSESP; iesp++)
      {
        HEPSAPR->SetValue(iesp, (Conditions.FToler())->Value(iesp, 3));
      }
      break;
    case 4:
      for (iesp = 1; iesp <= NBSESP; iesp++)
      {
        HEPSAPR->SetValue(iesp, (Conditions.FToler())->Value(iesp, 4));
      }
      break;
  }
  Standard_Real* EPSAPR = (Standard_Real*)&HEPSAPR->ChangeArray1()(HEPSAPR->Lower());

  // the tables of approximations
  Standard_Integer              SZCRB   = NDIMEN * NCFLIM;
  Handle(TColStd_HArray1OfReal) HCOURBE = new TColStd_HArray1OfReal(1, SZCRB * (IDERIV + 1));
  Standard_Real*                COURBE = (Standard_Real*)&HCOURBE->ChangeArray1()(HCOURBE->Lower());
  Standard_Real*                CRBAPP = COURBE;
  Standard_Integer              SZTAB  = (1 + NBROOT / 2) * NDIMEN;
  Handle(TColStd_HArray1OfReal) HSOMTAB = new TColStd_HArray1OfReal(1, SZTAB * (IDERIV + 1));
  Standard_Real*                SOMTAB = (Standard_Real*)&HSOMTAB->ChangeArray1()(HSOMTAB->Lower());
  Standard_Real*                SOMAPP = SOMTAB;
  Handle(TColStd_HArray1OfReal) HDIFTAB = new TColStd_HArray1OfReal(1, SZTAB * (IDERIV + 1));
  Standard_Real*                DIFTAB = (Standard_Real*)&HDIFTAB->ChangeArray1()(HDIFTAB->Lower());
  Standard_Real*                DIFAPP = DIFTAB;
  Handle(TColStd_HArray1OfReal) HCONTR1 = new TColStd_HArray1OfReal(1, (IORDRE + 2) * NDIMEN);
  Standard_Real*                CONTR1 = (Standard_Real*)&HCONTR1->ChangeArray1()(HCONTR1->Lower());
  Handle(TColStd_HArray1OfReal) HCONTR2 = new TColStd_HArray1OfReal(1, (IORDRE + 2) * NDIMEN);
  Standard_Real*                CONTR2 = (Standard_Real*)&HCONTR2->ChangeArray1()(HCONTR2->Lower());
  Handle(TColStd_HArray2OfReal) HERRMAX = new TColStd_HArray2OfReal(1, NBSESP, 1, IDERIV + 1);
  Standard_Real*                EMXAPP  = new Standard_Real[NBSESP];
  Handle(TColStd_HArray2OfReal) HERRMOY = new TColStd_HArray2OfReal(1, NBSESP, 1, IDERIV + 1);
  // #ifdef OCCT_DEBUG
  // Standard_Real *ERRMOY =
  // #endif
  //   (Standard_Real *) &HERRMOY->ChangeArray2()(HERRMOY ->LowerRow(),HERRMOY ->LowerCol());
  Standard_Real* EMYAPP = new Standard_Real[NBSESP];
  //
  // the approximations
  //
  Standard_Integer IERCOD = 0, NCOEFF = 0;
  Standard_Integer iapp, ncfapp, ierapp;
  //  Standard_Integer id,ic,ideb;
  for (iapp = 0; iapp <= IDERIV; iapp++)
  {
    //   approximation of the derivative of order iapp
    ncfapp = 0;
    ierapp = 0;
    // GCC 3.0 would not accept this line without the void
    // pointer cast.  Perhaps the real problem is a definition
    // somewhere that has a void * in it.
    AdvApp2Var_ApproxF2var::mma2fnc_(&NDIMEN,
                                     &NBSESP,
                                     &NDIMSE,
                                     UVFONC,
                                     /*(void *)*/ Func,
                                     &TCONST,
                                     &ISOFAV,
                                     &NBROOT,
                                     ROOTLG,
                                     &IORDRE,
                                     &iapp,
                                     &NDGJAC,
                                     &NBCRMX,
                                     &NCFLIM,
                                     EPSAPR,
                                     &ncfapp,
                                     CRBAPP,
                                     &NBCRBE,
                                     SOMAPP,
                                     DIFAPP,
                                     CONTR1,
                                     CONTR2,
                                     TABDEC,
                                     EMXAPP,
                                     EMYAPP,
                                     &ierapp);
    //   error and coefficient management.
    if (ierapp > 0)
    {
      myApprIsDone = Standard_False;
      myHasResult  = Standard_False;
      goto FINISH;
    }
    if (NCOEFF <= ncfapp)
      NCOEFF = ncfapp;
    if (ierapp == -1)
      IERCOD = -1;
    //   return constraints of order 0 to IORDRE of extremities
    Standard_Integer ider, jpos = HCONTR1->Lower();
    for (ider = 0; ider <= IORDRE; ider++)
    {
      gp_Pnt pt(HCONTR1->Value(jpos), HCONTR1->Value(jpos + 1), HCONTR1->Value(jpos + 2));
      if (ISOFAV == 2)
      {
        NodeBegin.SetPoint(ider, iapp, pt);
      }
      else
      {
        NodeBegin.SetPoint(iapp, ider, pt);
      }
      jpos += 3;
    }
    jpos = HCONTR2->Lower();
    for (ider = 0; ider <= IORDRE; ider++)
    {
      gp_Pnt pt(HCONTR2->Value(jpos), HCONTR2->Value(jpos + 1), HCONTR2->Value(jpos + 2));
      if (ISOFAV == 2)
      {
        NodeEnd.SetPoint(ider, iapp, pt);
      }
      else
      {
        NodeEnd.SetPoint(iapp, ider, pt);
      }
      jpos += 3;
    }
    //   return errors
    for (iesp = 1; iesp <= NBSESP; iesp++)
    {
      HERRMAX->SetValue(iesp, iapp + 1, EMXAPP[iesp - 1]);
      HERRMOY->SetValue(iesp, iapp + 1, EMYAPP[iesp - 1]);
    }
    // passage to the approximation of higher order
    CRBAPP += SZCRB;
    SOMAPP += SZTAB;
    DIFAPP += SZTAB;
  }

  // management of results
  if (IERCOD == 0)
  {
    //   all approximations are correct
    myApprIsDone = Standard_True;
    myHasResult  = Standard_True;
  }
  else if (IERCOD == -1)
  {
    //   at least one approximation is not correct
    myApprIsDone = Standard_False;
    myHasResult  = Standard_True;
  }
  else
  {
    myApprIsDone = Standard_False;
    myHasResult  = Standard_False;
  }
  if (myHasResult)
  {
    myEquation  = HCOURBE;
    myNbCoeff   = NCOEFF;
    myMaxErrors = HERRMAX;
    myMoyErrors = HERRMOY;
    mySomTab    = HSOMTAB;
    myDifTab    = HDIFTAB;
  }
FINISH:
  delete[] EMXAPP;
  delete[] EMYAPP;
}

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

void AdvApp2Var_Iso::ChangeDomain(const Standard_Real a, const Standard_Real b)
{
  if (myType == GeomAbs_IsoU)
  {
    myV0 = a;
    myV1 = b;
  }
  else
  {
    myU0 = a;
    myU1 = b;
  }
}

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

void AdvApp2Var_Iso::ChangeDomain(const Standard_Real a,
                                  const Standard_Real b,
                                  const Standard_Real c,
                                  const Standard_Real d)
{
  myU0 = a;
  myU1 = b;
  myV0 = c;
  myV1 = d;
}

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

void AdvApp2Var_Iso::SetConstante(const Standard_Real newcte)
{
  myConstPar = newcte;
}

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

void AdvApp2Var_Iso::SetPosition(const Standard_Integer newpos)
{
  myPosition = newpos;
}

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

void AdvApp2Var_Iso::ResetApprox()
{
  myApprIsDone = Standard_False;
  myHasResult  = Standard_False;
}

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

void AdvApp2Var_Iso::OverwriteApprox()
{
  if (myHasResult)
    myApprIsDone = Standard_True;
}

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

GeomAbs_IsoType AdvApp2Var_Iso::Type() const
{
  return myType;
}

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

Standard_Real AdvApp2Var_Iso::Constante() const
{
  return myConstPar;
}

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

Standard_Real AdvApp2Var_Iso::T0() const
{
  if (myType == GeomAbs_IsoU)
  {
    return myV0;
  }
  else
  {
    return myU0;
  }
}

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

Standard_Real AdvApp2Var_Iso::T1() const
{
  if (myType == GeomAbs_IsoU)
  {
    return myV1;
  }
  else
  {
    return myU1;
  }
}

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

Standard_Real AdvApp2Var_Iso::U0() const
{
  return myU0;
}

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

Standard_Real AdvApp2Var_Iso::U1() const
{
  return myU1;
}

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

Standard_Real AdvApp2Var_Iso::V0() const
{
  return myV0;
}

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

Standard_Real AdvApp2Var_Iso::V1() const
{
  return myV1;
}

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

Standard_Integer AdvApp2Var_Iso::UOrder() const
{
  if (Type() == GeomAbs_IsoU)
    return myDerivOrder;
  else
    return myExtremOrder;
}

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

Standard_Integer AdvApp2Var_Iso::VOrder() const
{
  if (Type() == GeomAbs_IsoV)
    return myDerivOrder;
  else
    return myExtremOrder;
}

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

Standard_Integer AdvApp2Var_Iso::Position() const
{
  return myPosition;
}

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

Standard_Integer AdvApp2Var_Iso::NbCoeff() const
{
  return myNbCoeff;
}

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

const Handle(TColStd_HArray1OfReal)& AdvApp2Var_Iso::Polynom() const
{
  return myEquation;
}

Handle(TColStd_HArray1OfReal) AdvApp2Var_Iso::SomTab() const
{
  return mySomTab;
}

Handle(TColStd_HArray1OfReal) AdvApp2Var_Iso::DifTab() const
{
  return myDifTab;
}

Handle(TColStd_HArray2OfReal) AdvApp2Var_Iso::MaxErrors() const
{
  return myMaxErrors;
}

Handle(TColStd_HArray2OfReal) AdvApp2Var_Iso::MoyErrors() const
{
  return myMoyErrors;
}
