/**
 * \file
 * \copyright
 * Copyright (c) 2012-2023, OpenGeoSys Community (http://www.opengeosys.org)
 *            Distributed under a Modified BSD License.
 *              See accompanying file LICENSE.txt or
 *              http://www.opengeosys.org/project/license
 *
 * Created on May 9, 2023, 2:01 PM
 */

#include "ComputeElementVolumeNumerically.h"

#include <typeinfo>

#include "MeshLib/Elements/Element.h"
#include "MeshLib/Elements/Hex.h"
#include "MeshLib/Elements/Line.h"
#include "MeshLib/Elements/Prism.h"
#include "MeshLib/Elements/Pyramid.h"
#include "MeshLib/Elements/Quad.h"
#include "MeshLib/Elements/Tet.h"
#include "MeshLib/Elements/Tri.h"
#include "MeshLib/MeshEnums.h"
#include "NumLib/Fem/InitShapeMatrices.h"
#include "NumLib/Fem/Integration/IntegrationMethodRegistry.h"
#include "NumLib/Fem/ShapeFunction/ShapeHex20.h"
#include "NumLib/Fem/ShapeFunction/ShapeHex8.h"
#include "NumLib/Fem/ShapeFunction/ShapeLine2.h"
#include "NumLib/Fem/ShapeFunction/ShapeLine3.h"
#include "NumLib/Fem/ShapeFunction/ShapePrism15.h"
#include "NumLib/Fem/ShapeFunction/ShapePrism6.h"
#include "NumLib/Fem/ShapeFunction/ShapePyra13.h"
#include "NumLib/Fem/ShapeFunction/ShapePyra5.h"
#include "NumLib/Fem/ShapeFunction/ShapeQuad4.h"
#include "NumLib/Fem/ShapeFunction/ShapeQuad8.h"
#include "NumLib/Fem/ShapeFunction/ShapeQuad9.h"
#include "NumLib/Fem/ShapeFunction/ShapeTet10.h"
#include "NumLib/Fem/ShapeFunction/ShapeTet4.h"
#include "NumLib/Fem/ShapeFunction/ShapeTri3.h"
#include "NumLib/Fem/ShapeFunction/ShapeTri6.h"
#include "NumLib/Fem/ShapeMatrixPolicy.h"

namespace MeshToolsLib
{
template <typename ShapeFunction>
double computeElementVolumeNumerically(MeshLib::Element const& e)
{
    // Space dimension is set to 3 in case that 1D or 2D element is inclined.
    constexpr int space_dim = 3;
    using ShapeMatricesType = ShapeMatrixPolicyType<ShapeFunction, space_dim>;

    // Integration order is set to 3:
    auto const& integration_method =
        NumLib::IntegrationMethodRegistry::template getIntegrationMethod<
            typename ShapeFunction::MeshElement>(NumLib::IntegrationOrder{3});

    auto const shape_function_data =
        NumLib::initShapeMatrices<ShapeFunction, ShapeMatricesType, space_dim>(
            e, false /*is_axially_symmetric*/, integration_method);

    auto const n_integration_points = integration_method.getNumberOfPoints();
    double volume = 0.0;
    for (unsigned ip = 0; ip < n_integration_points; ++ip)
    {
        auto const weight = integration_method.getWeightedPoint(ip).getWeight();
        volume += shape_function_data[ip].detJ * weight;
    }

    return volume;
}

double computeElementVolumeNumerically(MeshLib::Element const& e)
{
    switch (e.getCellType())
    {
        case MeshLib::CellType::LINE2:
            return computeElementVolumeNumerically<NumLib::ShapeLine2>(e);
        case MeshLib::CellType::LINE3:
            return computeElementVolumeNumerically<NumLib::ShapeLine3>(e);
        case MeshLib::CellType::TRI3:
            return computeElementVolumeNumerically<NumLib::ShapeTri3>(e);
        case MeshLib::CellType::TRI6:
            return computeElementVolumeNumerically<NumLib::ShapeTri6>(e);
        case MeshLib::CellType::QUAD4:
            return computeElementVolumeNumerically<NumLib::ShapeQuad4>(e);
        case MeshLib::CellType::QUAD8:
            return computeElementVolumeNumerically<NumLib::ShapeQuad8>(e);
        case MeshLib::CellType::QUAD9:
            return computeElementVolumeNumerically<NumLib::ShapeQuad9>(e);
        case MeshLib::CellType::TET4:
            return computeElementVolumeNumerically<NumLib::ShapeTet4>(e);
        case MeshLib::CellType::HEX8:
            return computeElementVolumeNumerically<NumLib::ShapeHex8>(e);
        case MeshLib::CellType::HEX20:
            return computeElementVolumeNumerically<NumLib::ShapeHex20>(e);
        case MeshLib::CellType::TET10:
            return computeElementVolumeNumerically<NumLib::ShapeTet10>(e);
        case MeshLib::CellType::PRISM6:
            return computeElementVolumeNumerically<NumLib::ShapePrism6>(e);
        case MeshLib::CellType::PRISM15:
            return computeElementVolumeNumerically<NumLib::ShapePrism15>(e);
        case MeshLib::CellType::PYRAMID5:
            return computeElementVolumeNumerically<NumLib::ShapePyra5>(e);
        case MeshLib::CellType::PYRAMID13:
            return computeElementVolumeNumerically<NumLib::ShapePyra13>(e);
        default:
            OGS_FATAL(
                "Numerical volume calculation is not available for element "
                "with type {}. ",
                MeshLib::CellType2String(e.getCellType()));
    }
}

}  // namespace MeshToolsLib