#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_all.hpp>

#include <MeshDataBaseForTests.hpp>
#include <mesh/Connectivity.hpp>
#include <mesh/Mesh.hpp>
#include <mesh/SubItemValuePerItemVariant.hpp>
#include <utils/Messenger.hpp>

// clazy:excludeall=non-pod-global-static

TEST_CASE("SubItemValuePerItemVariant", "[mesh]")
{
  std::shared_ptr mesh = MeshDataBaseForTests::get().hybrid3DMesh();

  const Connectivity<3>& connectivity = *mesh->shared_connectivity();

  using R1   = TinyVector<1>;
  using R2   = TinyVector<2>;
  using R3   = TinyVector<3>;
  using R1x1 = TinyMatrix<1>;
  using R2x2 = TinyMatrix<2>;
  using R3x3 = TinyMatrix<3>;

  SECTION("NodeValuePerCell<double>")
  {
    NodeValuePerCell<double> node_value{connectivity};
    SubItemValuePerItemVariant v(node_value);
    REQUIRE_NOTHROW(v.get<NodeValuePerCell<double>>());
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<int64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<uint64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<R1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<R2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<R3>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<R1x1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<R2x2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<R3x3>>(), "error: invalid SubItemValuePerItem type");
  }

  SECTION("NodeValuePerFace<R1>")
  {
    NodeValuePerFace<R1> node_value{connectivity};
    SubItemValuePerItemVariant v(node_value);
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<const double>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<const int64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<const uint64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_NOTHROW(v.get<NodeValuePerFace<const R1>>());
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<const R2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<const R3>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<const R1x1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<const R2x2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<const R3x3>>(), "error: invalid SubItemValuePerItem type");
  }

  SECTION("NodeValuePerEdge<int64_t>")
  {
    NodeValuePerEdge<int64_t> node_value{connectivity};
    SubItemValuePerItemVariant v(node_value);
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<double>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_NOTHROW(v.get<NodeValuePerEdge<int64_t>>());
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<uint64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<R1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<R2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<R3>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<R1x1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<R2x2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<R3x3>>(), "error: invalid SubItemValuePerItem type");
  }

  SECTION("EdgeValuePerCell<R2>")
  {
    EdgeValuePerCell<R2> node_value{connectivity};
    SubItemValuePerItemVariant v(node_value);
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerCell<double>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerCell<int64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerCell<uint64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerCell<R1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_NOTHROW(v.get<EdgeValuePerCell<R2>>());
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerCell<R3>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerCell<R1x1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerCell<R2x2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerCell<R3x3>>(), "error: invalid SubItemValuePerItem type");
  }

  SECTION("EdgeValuePerFace<R1>")
  {
    EdgeValuePerFace<R1> node_value{connectivity};
    SubItemValuePerItemVariant v(node_value);
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerFace<const double>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerFace<int64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerFace<uint64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_NOTHROW(v.get<EdgeValuePerFace<R1>>());
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerFace<R2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerFace<R3>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerFace<R1x1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerFace<R2x2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerFace<R3x3>>(), "error: invalid SubItemValuePerItem type");
  }

  SECTION("EdgeValuePerNode<double>")
  {
    EdgeValuePerNode<double> node_value{connectivity};
    SubItemValuePerItemVariant v(node_value);
    REQUIRE_NOTHROW(v.get<EdgeValuePerNode<double>>());
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerNode<int64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerNode<uint64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerNode<R1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerNode<R2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerNode<R3>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerNode<R1x1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerNode<R2x2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<EdgeValuePerNode<R3x3>>(), "error: invalid SubItemValuePerItem type");
  }

  SECTION("FaceValuePerCell<R3x3>")
  {
    FaceValuePerCell<R3x3> node_value{connectivity};
    SubItemValuePerItemVariant v(node_value);
    REQUIRE_THROWS_WITH(v.get<FaceValuePerCell<double>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerCell<int64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerCell<uint64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerCell<R1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerCell<R2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerCell<R3>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerCell<R1x1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerCell<R2x2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_NOTHROW(v.get<FaceValuePerCell<R3x3>>());
  }

  SECTION("FaceValuePerEdge<R2x2>")
  {
    FaceValuePerEdge<R2x2> node_value{connectivity};
    SubItemValuePerItemVariant v(node_value);
    REQUIRE_THROWS_WITH(v.get<FaceValuePerEdge<double>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerEdge<int64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerEdge<uint64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerEdge<R1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerEdge<R2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerEdge<R3>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerEdge<R1x1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_NOTHROW(v.get<FaceValuePerEdge<R2x2>>());
    REQUIRE_THROWS_WITH(v.get<FaceValuePerEdge<R3x3>>(), "error: invalid SubItemValuePerItem type");
  }

  SECTION("FaceValuePerNode<uint64_t>")
  {
    FaceValuePerNode<uint64_t> node_value{connectivity};
    SubItemValuePerItemVariant v(node_value);
    REQUIRE_THROWS_WITH(v.get<FaceValuePerNode<double>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerNode<int64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_NOTHROW(v.get<FaceValuePerNode<uint64_t>>());
    REQUIRE_THROWS_WITH(v.get<FaceValuePerNode<R1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerNode<R2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerNode<R3>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerNode<R1x1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerNode<R2x2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<FaceValuePerNode<R3x3>>(), "error: invalid SubItemValuePerItem type");
  }

  SECTION("NodeValuePerCell<R1x1>")
  {
    NodeValuePerCell<R1x1> node_value{connectivity};
    SubItemValuePerItemVariant v(node_value);
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<double>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<int64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<uint64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<R1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<R2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<R3>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_NOTHROW(v.get<NodeValuePerCell<R1x1>>());
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<R2x2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerCell<R3x3>>(), "error: invalid SubItemValuePerItem type");
  }

  SECTION("NodeValuePerFace<R3>")
  {
    NodeValuePerFace<R3> node_value{connectivity};
    SubItemValuePerItemVariant v(node_value);
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<double>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<int64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<uint64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<R1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<R2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_NOTHROW(v.get<NodeValuePerFace<R3>>());
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<R1x1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<R2x2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerFace<R3x3>>(), "error: invalid SubItemValuePerItem type");
  }

  SECTION("NodeValuePerEdge<double>")
  {
    NodeValuePerEdge<double> node_value{connectivity};
    SubItemValuePerItemVariant v(node_value);
    REQUIRE_NOTHROW(v.get<NodeValuePerEdge<double>>());
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<int64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<uint64_t>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<R1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<R2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<R3>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<R1x1>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<R2x2>>(), "error: invalid SubItemValuePerItem type");
    REQUIRE_THROWS_WITH(v.get<NodeValuePerEdge<R3x3>>(), "error: invalid SubItemValuePerItem type");
  }
}