// Copyright (c) 2025 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 <gtest/gtest.h>

#include <Geom_BSplineSurface.hxx>
#include <gp_Pnt.hxx>
#include <NCollection_Array2.hxx>
#include <NCollection_Array1.hxx>
#include <Standard_Integer.hxx>

class Geom_BSplineSurface_Test : public ::testing::Test
{
protected:
  void SetUp() override
  {
    // Create a simple BSpline surface for testing
    NCollection_Array2<gp_Pnt> aPoles(1, 3, 1, 3);
    for (int i = 1; i <= 3; ++i)
    {
      for (int j = 1; j <= 3; ++j)
      {
        aPoles(i, j) = gp_Pnt(i, j, (i + j) * 0.1);
      }
    }

    NCollection_Array1<double> anUKnots(1, 2);
    anUKnots(1) = 0.0;
    anUKnots(2) = 1.0;

    NCollection_Array1<double> aVKnots(1, 2);
    aVKnots(1) = 0.0;
    aVKnots(2) = 1.0;

    NCollection_Array1<int> anUMults(1, 2);
    anUMults(1) = 3;
    anUMults(2) = 3;

    NCollection_Array1<int> aVMults(1, 2);
    aVMults(1) = 3;
    aVMults(2) = 3;

    myOriginalSurface = new Geom_BSplineSurface(aPoles, anUKnots, aVKnots, anUMults, aVMults, 2, 2);
  }

  occ::handle<Geom_BSplineSurface> myOriginalSurface;
};

TEST_F(Geom_BSplineSurface_Test, CopyConstructorBasicProperties)
{
  // Test copy constructor
  occ::handle<Geom_BSplineSurface> aCopiedSurface = new Geom_BSplineSurface(*myOriginalSurface);

  // Verify basic properties are identical
  EXPECT_EQ(myOriginalSurface->UDegree(), aCopiedSurface->UDegree());
  EXPECT_EQ(myOriginalSurface->VDegree(), aCopiedSurface->VDegree());
  EXPECT_EQ(myOriginalSurface->NbUPoles(), aCopiedSurface->NbUPoles());
  EXPECT_EQ(myOriginalSurface->NbVPoles(), aCopiedSurface->NbVPoles());
  EXPECT_EQ(myOriginalSurface->NbUKnots(), aCopiedSurface->NbUKnots());
  EXPECT_EQ(myOriginalSurface->NbVKnots(), aCopiedSurface->NbVKnots());
  EXPECT_EQ(myOriginalSurface->IsUPeriodic(), aCopiedSurface->IsUPeriodic());
  EXPECT_EQ(myOriginalSurface->IsVPeriodic(), aCopiedSurface->IsVPeriodic());
}

TEST_F(Geom_BSplineSurface_Test, CopyConstructorPoles)
{
  occ::handle<Geom_BSplineSurface> aCopiedSurface = new Geom_BSplineSurface(*myOriginalSurface);

  // Verify all poles are identical
  for (int i = 1; i <= myOriginalSurface->NbUPoles(); ++i)
  {
    for (int j = 1; j <= myOriginalSurface->NbVPoles(); ++j)
    {
      gp_Pnt anOrigPole = myOriginalSurface->Pole(i, j);
      gp_Pnt aCopyPole  = aCopiedSurface->Pole(i, j);
      EXPECT_TRUE(anOrigPole.IsEqual(aCopyPole, 1e-10));
    }
  }
}

TEST_F(Geom_BSplineSurface_Test, CopyConstructorKnots)
{
  occ::handle<Geom_BSplineSurface> aCopiedSurface = new Geom_BSplineSurface(*myOriginalSurface);

  // Verify U knots are identical
  for (int i = 1; i <= myOriginalSurface->NbUKnots(); ++i)
  {
    EXPECT_DOUBLE_EQ(myOriginalSurface->UKnot(i), aCopiedSurface->UKnot(i));
    EXPECT_EQ(myOriginalSurface->UMultiplicity(i), aCopiedSurface->UMultiplicity(i));
  }

  // Verify V knots are identical
  for (int i = 1; i <= myOriginalSurface->NbVKnots(); ++i)
  {
    EXPECT_DOUBLE_EQ(myOriginalSurface->VKnot(i), aCopiedSurface->VKnot(i));
    EXPECT_EQ(myOriginalSurface->VMultiplicity(i), aCopiedSurface->VMultiplicity(i));
  }
}

TEST_F(Geom_BSplineSurface_Test, CopyMethodUsesOptimizedConstructor)
{
  // Test that Copy() method uses the optimized copy constructor
  occ::handle<Geom_Geometry>       aCopiedGeom = myOriginalSurface->Copy();
  occ::handle<Geom_BSplineSurface> aCopiedSurface =
    occ::down_cast<Geom_BSplineSurface>(aCopiedGeom);

  EXPECT_FALSE(aCopiedSurface.IsNull());

  // Verify the copy is functionally identical
  EXPECT_EQ(myOriginalSurface->UDegree(), aCopiedSurface->UDegree());
  EXPECT_EQ(myOriginalSurface->VDegree(), aCopiedSurface->VDegree());

  // Test evaluation at several points
  for (double u = 0.0; u <= 1.0; u += 0.5)
  {
    for (double v = 0.0; v <= 1.0; v += 0.5)
    {
      gp_Pnt anOrigPnt = myOriginalSurface->Value(u, v);
      gp_Pnt aCopyPnt  = aCopiedSurface->Value(u, v);
      EXPECT_TRUE(anOrigPnt.IsEqual(aCopyPnt, 1e-10));
    }
  }
}

TEST_F(Geom_BSplineSurface_Test, CopyIndependence)
{
  occ::handle<Geom_BSplineSurface> aCopiedSurface = new Geom_BSplineSurface(*myOriginalSurface);

  // Modify the original surface
  gp_Pnt aNewPole(10, 10, 10);
  myOriginalSurface->SetPole(2, 2, aNewPole);

  // Verify the copied surface is not affected
  gp_Pnt anOrigPole = aCopiedSurface->Pole(2, 2);
  EXPECT_FALSE(anOrigPole.IsEqual(aNewPole, 1e-10));
}