/***************************************************************************
                          SuperentityStorageInitializer.h  -  description
                             -------------------
    begin                : Feb 27, 2014
    copyright            : (C) 2014 by Tomas Oberhuber et al.
    email                : tomas.oberhuber@fjfi.cvut.cz
 ***************************************************************************/

/* See Copyright Notice in tnl/Copyright */

/***
 * Authors:
 * Oberhuber Tomas, tomas.oberhuber@fjfi.cvut.cz
 * Zabka Vitezslav, zabkav@gmail.com
 */

#pragma once

#include <set>
#include <unordered_map>

#include <TNL/Meshes/DimensionTag.h>
#include <TNL/Meshes/MeshDetails/traits/MeshSuperentityTraits.h>

namespace TNL {
namespace Meshes {

template< typename MeshConfig >
class Initializer;

template< typename MeshConfig,
          typename SubdimensionTag,
          typename SuperdimensionTag >
class SuperentityStorageInitializer
{
   using MeshTraitsType            = MeshTraits< MeshConfig >;
   using InitializerType           = Initializer< MeshConfig >;
   using GlobalIndexType           = typename MeshTraitsType::GlobalIndexType;
   using LocalIndexType            = typename MeshTraitsType::LocalIndexType;
   using EntityTraitsType          = typename MeshTraitsType::template EntityTraits< SubdimensionTag::value >;
   using EntityTopology            = typename EntityTraitsType::EntityTopology;
   using EntityType                = typename EntityTraitsType::EntityType;
   using SuperentityTraitsType     = typename MeshTraitsType::template SuperentityTraits< EntityTopology, SuperdimensionTag::value >;
   using SuperentityStorageNetwork = typename SuperentityTraitsType::StorageNetworkType;

public:
   void addSuperentity( GlobalIndexType entityIndex, GlobalIndexType superentityIndex)
   {
      //std::cout << "Adding superentity with " << SuperdimensionTag::value << " dimension of entity with " << SubdimensionTag::value << " dimension: entityIndex = " << entityIndex << ", superentityIndex = " << superentityIndex << std::endl;
      auto& indexSet = this->dynamicStorageNetwork[ entityIndex ];
      TNL_ASSERT( indexSet.count( superentityIndex ) == 0,
                  std::cerr << "Superentity " << superentityIndex << " with dimension " << SuperdimensionTag::value
                            << " of entity " << entityIndex << " with dimension " << SubdimensionTag::value
                            << " has been already added. This is probably a bug in the mesh initializer." << std::endl; );
      indexSet.insert( superentityIndex );
   }

   void initSuperentities( InitializerType& meshInitializer )
   {
      TNL_ASSERT_GT( dynamicStorageNetwork.size(), (size_t) 0,
                     "No superentity indices were collected. This is a bug in the mesh initializer." );
      TNL_ASSERT_EQ( (size_t) getMaxSuperentityIndex(), dynamicStorageNetwork.size() - 1,
                     "Superentities for some entities are missing. "
                     "This is probably a bug in the mesh initializer." );

      SuperentityStorageNetwork& superentityStorageNetwork = meshInitializer.template meshSuperentityStorageNetwork< EntityTopology, SuperdimensionTag::value >();
      TNL_ASSERT_EQ( (size_t) superentityStorageNetwork.getKeysRange(), dynamicStorageNetwork.size(),
                     "Sizes of the static and dynamic storage networks don't match. "
                     "This is probably a bug in the mesh initializer." );

      typename SuperentityStorageNetwork::ValuesAllocationVectorType storageNetworkAllocationVector;
      storageNetworkAllocationVector.setSize( superentityStorageNetwork.getKeysRange() );
      for( auto it = dynamicStorageNetwork.cbegin(); it != dynamicStorageNetwork.cend(); it++ )
         storageNetworkAllocationVector[ it->first ] = it->second.size();
      superentityStorageNetwork.allocate( storageNetworkAllocationVector );

      for( auto it = dynamicStorageNetwork.cbegin(); it != dynamicStorageNetwork.cend(); it++ ) {
         auto superentitiesIndices = superentityStorageNetwork.getValues( it->first );
         LocalIndexType i = 0;
         for( auto v_it = it->second.cbegin(); v_it != it->second.cend(); v_it++ )
            superentitiesIndices[ i++ ] = *v_it;
      }

      dynamicStorageNetwork.clear();
   }

private:
   using DynamicIndexSet = std::set< GlobalIndexType >;
   std::unordered_map< GlobalIndexType, DynamicIndexSet > dynamicStorageNetwork;

   GlobalIndexType getMaxSuperentityIndex()
   {
      GlobalIndexType max = 0;
      for( auto it = dynamicStorageNetwork.cbegin(); it != dynamicStorageNetwork.cend(); it++ ) {
         if( it->first > max )
            max = it->first;
      }
      return max;
   }
};

} // namespace Meshes
} // namespace TNL
