/***************************************************************************
                          resolveDistributedMesh.hpp  -  description
                             -------------------
    begin                : Apr 9, 2020
    copyright            : (C) 2020 by Tomas Oberhuber et al.
    email                : tomas.oberhuber@fjfi.cvut.cz
 ***************************************************************************/

/* See Copyright Notice in tnl/Copyright */

#pragma once

#include <experimental/filesystem>

#include <TNL/Meshes/TypeResolver/resolveDistributedMeshType.h>
#include <TNL/Meshes/TypeResolver/resolveMeshType.h>

namespace TNL {
namespace Meshes {

template< typename ConfigTag,
          typename Device,
          typename Functor >
bool
resolveDistributedMeshType( Functor&& functor,
                            const std::string& fileName,
                            const std::string& fileFormat )
{
   std::cout << "Detecting distributed mesh from file " << fileName << " ..." << std::endl;

   auto wrapper = [&functor] ( Readers::MeshReader& reader, auto&& localMesh )
   {
      using LocalMesh = std::decay_t< decltype(localMesh) >;
      using DistributedMesh = DistributedMeshes::DistributedMesh< LocalMesh >;
      return std::forward<Functor>(functor)( reader, DistributedMesh{ std::move(localMesh) } );
   };

   return resolveMeshType< ConfigTag, Device >( wrapper, fileName, fileFormat );
}

template< typename ConfigTag,
          typename Device,
          typename Functor >
bool
resolveAndLoadDistributedMesh( Functor&& functor,
                               const std::string& fileName,
                               const std::string& fileFormat )
{
   auto wrapper = [&]( Readers::MeshReader& reader, auto&& mesh ) -> bool
   {
      using MeshType = std::decay_t< decltype(mesh) >;
      std::cout << "Loading a mesh from the file " << fileName << " ..." << std::endl;
      try {
         if( reader.getMeshType() == "Meshes::DistributedMesh" )
            dynamic_cast<Readers::PVTUReader&>(reader).loadMesh( mesh );
         else if( reader.getMeshType() == "Meshes::DistributedGrid" )
            dynamic_cast<Readers::PVTIReader&>(reader).loadMesh( mesh );
         else
            throw std::runtime_error( "Unknown type of a distributed mesh: " + reader.getMeshType() );
      }
      catch( const Meshes::Readers::MeshReaderError& e ) {
         std::cerr << "Failed to load the mesh from the file " << fileName << ". The error is:\n" << e.what() << std::endl;
         return false;
      }
      return functor( reader, std::forward<MeshType>(mesh) );
   };
   return resolveDistributedMeshType< ConfigTag, Device >( wrapper, fileName, fileFormat );
}

template< typename Mesh >
bool
loadDistributedMesh( DistributedMeshes::DistributedMesh< Mesh >& distributedMesh,
                     const std::string& fileName,
                     const std::string& fileFormat )
{
   namespace fs = std::experimental::filesystem;
   std::string format = fileFormat;
   if( format == "auto" ) {
      format = fs::path(fileName).extension();
      if( format.length() > 0 )
         // remove dot from the extension
         format = format.substr(1);
   }

   if( format == "pvtu" ) {
      Readers::PVTUReader reader( fileName );
      reader.loadMesh( distributedMesh );
      return true;
   }
   else if( format == "pvti" ) {
      Readers::PVTIReader reader( fileName );
      reader.loadMesh( distributedMesh );
      return true;
   }
   else {
      if( fileFormat == "auto" )
         std::cerr << "File '" << fileName << "' has unsupported format (based on the file extension): " << format << ".";
      else
         std::cerr << "Unsupported fileFormat parameter: " << fileFormat << ".";
      std::cerr << " Supported formats are 'pvtu' and 'pvti'." << std::endl;
      return false;
   }
}

} // namespace Meshes
} // namespace TNL
