///////////////////////////////////////////////////////////////////////////////
//
// File: ExpListHomogeneous2D.h
//
// For more information, please see: http://www.nektar.info
//
// The MIT License
//
// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
// Department of Aeronautics, Imperial College London (UK), and Scientific
// Computing and Imaging Institute, University of Utah (USA).
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
// Description: Base class for expansions which are homogeneous in 2
// directions
//
///////////////////////////////////////////////////////////////////////////////

#ifndef EXPLISTHOMO2D_H
#define EXPLISTHOMO2D_H
#include <LibUtilities/Communication/Transposition.h>
#include <LibUtilities/FFT/NektarFFT.h>
#include <MultiRegions/ExpList.h>
#include <MultiRegions/MultiRegions.hpp>
#include <MultiRegions/MultiRegionsDeclspec.h>
#include <vector>

namespace Nektar::MultiRegions
{

enum Homogeneous2DMatType
{
    eForwardsCoeffSpaceY1D,
    eForwardsCoeffSpaceZ1D,
    eBackwardsCoeffSpaceY1D,
    eBackwardsCoeffSpaceZ1D,
    eForwardsPhysSpaceY1D,
    eForwardsPhysSpaceZ1D,
    eBackwardsPhysSpaceY1D,
    eBackwardsPhysSpaceZ1D
};

/// A map between homo matrix keys and their associated block
/// matrices.
typedef std::map<Homogeneous2DMatType, DNekBlkMatSharedPtr>
    Homo2DBlockMatrixMap;
/// A shared pointer to a BlockMatrixMap.
typedef std::shared_ptr<Homo2DBlockMatrixMap> Homo2DBlockMatrixMapShPtr;

// Forward declaration for typedefs
class ExpListHomogeneous2D;

/// Shared pointer to an ExpList3DHomogeneous2D object.
typedef std::shared_ptr<ExpListHomogeneous2D> ExpListHomogeneous2DSharedPtr;
/// Vector of pointers to ExpList3DHomogeneous2D objects.
typedef std::vector<ExpListHomogeneous2DSharedPtr> ExpListHomogeneous2DVector;

/// Abstraction of a two-dimensional multi-elemental expansion which
/// is merely a collection of local expansions.
class ExpListHomogeneous2D : public ExpList
{
public:
    /// Default constructor.
    MULTI_REGIONS_EXPORT ExpListHomogeneous2D(const ExpansionType type);

    MULTI_REGIONS_EXPORT ExpListHomogeneous2D(
        const ExpansionType type,
        const LibUtilities::SessionReaderSharedPtr &pSession,
        const LibUtilities::BasisKey &HomoBasis_y,
        const LibUtilities::BasisKey &HomoBasis_z, const NekDouble ly,
        const NekDouble lz, const bool useFFT, const bool dealiasing);

    /// Copy constructor.
    MULTI_REGIONS_EXPORT ExpListHomogeneous2D(const ExpListHomogeneous2D &In);

    MULTI_REGIONS_EXPORT ExpListHomogeneous2D(
        const ExpListHomogeneous2D &In, const std::vector<unsigned int> &eIDs);
    /// Destructor.
    MULTI_REGIONS_EXPORT ~ExpListHomogeneous2D() override;

    MULTI_REGIONS_EXPORT void Homogeneous2DTrans(
        const int npts, const Array<OneD, const NekDouble> &inarray,
        Array<OneD, NekDouble> &outarray, bool IsForwards, bool Shuff = true,
        bool UnShuff = true);

    MULTI_REGIONS_EXPORT void SetPaddingBase(void);

    MULTI_REGIONS_EXPORT void PhysDeriv(
        const Array<OneD, const NekDouble> &inarray,
        Array<OneD, NekDouble> &out_d0, Array<OneD, NekDouble> &out_d1,
        Array<OneD, NekDouble> &out_d2);

    MULTI_REGIONS_EXPORT void PhysDeriv(
        Direction edir, const Array<OneD, const NekDouble> &inarray,
        Array<OneD, NekDouble> &out_d);

    /// FFT variables
    bool m_useFFT;
    LibUtilities::NektarFFTSharedPtr m_FFT_y;
    LibUtilities::NektarFFTSharedPtr m_FFT_z;
    Array<OneD, NekDouble> m_tmpIN;
    Array<OneD, NekDouble> m_tmpOUT;

    LibUtilities::TranspositionSharedPtr m_transposition;
    LibUtilities::CommSharedPtr m_Ycomm;
    LibUtilities::CommSharedPtr m_Zcomm;

protected:
    /// Definition of the total number of degrees of freedom and
    /// quadrature points. Sets up the storage for \a m_coeff and \a
    ///  m_phys.
    LibUtilities::BasisSharedPtr
        m_homogeneousBasis_y; ///< Base expansion in y direction
    LibUtilities::BasisSharedPtr
        m_homogeneousBasis_z; ///< Base expansion in z direction
    LibUtilities::BasisSharedPtr
        m_paddingBasis_y; ///< Base expansion in y direction
    LibUtilities::BasisSharedPtr
        m_paddingBasis_z; ///< Base expansion in z direction
    NekDouble m_lhom_y;   ///< Width of homogeneous direction y
    NekDouble m_lhom_z;   ///< Width of homogeneous direction z
    Homo2DBlockMatrixMapShPtr m_homogeneous2DBlockMat;
    Array<OneD, ExpListSharedPtr>
        m_lines; ///< Vector of ExpList, will be filled with ExpList1D
    int m_ny;    ///< Number of modes = number of poitns in y direction
    int m_nz;    ///< Number of modes = number of poitns in z direction

    DNekBlkMatSharedPtr GenHomogeneous2DBlockMatrix(
        Homogeneous2DMatType mattype) const;

    DNekBlkMatSharedPtr GetHomogeneous2DBlockMatrix(
        Homogeneous2DMatType mattype) const;

    //  virtual functions
    size_t v_GetNumElmts(void) override
    {
        return m_lines[0]->GetExpSize();
    }

    void v_FwdTrans(const Array<OneD, const NekDouble> &inarray,
                    Array<OneD, NekDouble> &outarray) override;

    void v_FwdTransLocalElmt(const Array<OneD, const NekDouble> &inarray,
                             Array<OneD, NekDouble> &outarray) override;

    void v_FwdTransBndConstrained(const Array<OneD, const NekDouble> &inarray,
                                  Array<OneD, NekDouble> &outarray) override;

    void v_BwdTrans(const Array<OneD, const NekDouble> &inarray,
                    Array<OneD, NekDouble> &outarray) override;

    void v_IProductWRTBase(const Array<OneD, const NekDouble> &inarray,
                           Array<OneD, NekDouble> &outarray) override;

    std::vector<LibUtilities::FieldDefinitionsSharedPtr> v_GetFieldDefinitions(
        void) override;

    void v_GetFieldDefinitions(
        std::vector<LibUtilities::FieldDefinitionsSharedPtr> &fielddef)
        override;

    void v_AppendFieldData(LibUtilities::FieldDefinitionsSharedPtr &fielddef,
                           std::vector<NekDouble> &fielddata) override;

    void v_AppendFieldData(LibUtilities::FieldDefinitionsSharedPtr &fielddef,
                           std::vector<NekDouble> &fielddata,
                           Array<OneD, NekDouble> &coeffs) override;

    void v_ExtractDataToCoeffs(
        LibUtilities::FieldDefinitionsSharedPtr &fielddef,
        std::vector<NekDouble> &fielddata, std::string &field,
        Array<OneD, NekDouble> &coeffs,
        std::unordered_map<int, int> zIdToPlane) override;

    void v_WriteVtkPieceData(std::ostream &outfile, int expansion,
                             std::string var) override;

    void v_HomogeneousFwdTrans(const int npts,
                               const Array<OneD, const NekDouble> &inarray,
                               Array<OneD, NekDouble> &outarray,
                               bool Shuff = true, bool UnShuff = true) override;

    void v_HomogeneousBwdTrans(const int npts,
                               const Array<OneD, const NekDouble> &inarray,
                               Array<OneD, NekDouble> &outarray,
                               bool Shuff = true, bool UnShuff = true) override;

    void v_DealiasedProd(const int num_dofs,
                         const Array<OneD, NekDouble> &inarray1,
                         const Array<OneD, NekDouble> &inarray2,
                         Array<OneD, NekDouble> &outarray) override;

    void v_DealiasedDotProd(
        const int num_dofs, const Array<OneD, Array<OneD, NekDouble>> &inarray1,
        const Array<OneD, Array<OneD, NekDouble>> &inarray2,
        Array<OneD, Array<OneD, NekDouble>> &outarray) override;

    void v_PhysDeriv(const Array<OneD, const NekDouble> &inarray,
                     Array<OneD, NekDouble> &out_d0,
                     Array<OneD, NekDouble> &out_d1,
                     Array<OneD, NekDouble> &out_d2) override;

    void v_PhysDeriv(Direction edir,
                     const Array<OneD, const NekDouble> &inarray,
                     Array<OneD, NekDouble> &out_d) override;

private:
    // Padding operations variables
    bool m_dealiasing;
    int m_padsize_y;
    int m_padsize_z;
    DNekMatSharedPtr MatFwdPAD;
    DNekMatSharedPtr MatBwdPAD;
};

} // namespace Nektar::MultiRegions

#endif // EXPLISTHOMO2D_H
