/*
 * (C) Copyright 2013 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation
 * nor does it submit to any jurisdiction.
 */

#include "atlas/trans/Cache.h"
#include <cstdlib>

#include "eckit/io/DataHandle.h"

#include "atlas/runtime/Exception.h"
#include "atlas/runtime/Log.h"
#include "atlas/runtime/Trace.h"
#include "atlas/trans/Trans.h"

namespace atlas {
namespace trans {

TransCacheFileEntry::TransCacheFileEntry( const eckit::PathName& path ) : buffer_( path.size() ) {
    ATLAS_TRACE();
    Log::debug() << "Loading cache from file " << path << std::endl;
    std::unique_ptr<eckit::DataHandle> dh( path.fileHandle() );
    dh->openForRead();
    dh->read( buffer_.data(), buffer_.size() );
    dh->close();
}

TransCacheMemoryEntry::TransCacheMemoryEntry( const void* data, size_t size ) : data_( data ), size_( size ) {
    ATLAS_ASSERT( data_ );
    ATLAS_ASSERT( size_ );
}

LegendreFFTCache::LegendreFFTCache( const void* legendre_address, size_t legendre_size, const void* fft_address,
                                    size_t fft_size ) :
    Cache( std::make_shared<TransCacheMemoryEntry>( legendre_address, legendre_size ),
           std::make_shared<TransCacheMemoryEntry>( fft_address, fft_size ) ) {}

LegendreFFTCache::LegendreFFTCache( const eckit::PathName& legendre_path, const eckit::PathName& fft_path ) :
    Cache( std::shared_ptr<TransCacheEntry>( new TransCacheFileEntry( legendre_path ) ),
           std::shared_ptr<TransCacheEntry>( new TransCacheFileEntry( fft_path ) ) ) {}

LegendreCache::LegendreCache( const eckit::PathName& path ) :
    Cache( std::shared_ptr<TransCacheEntry>( new TransCacheFileEntry( path ) ) ) {}

LegendreCache::LegendreCache( size_t size ) : Cache( std::make_shared<TransCacheOwnedMemoryEntry>( size ) ) {}

LegendreCache::LegendreCache( const void* address, size_t size ) :
    Cache( std::make_shared<TransCacheMemoryEntry>( address, size ) ) {}

Cache::Cache( const std::shared_ptr<TransCacheEntry>& legendre ) :
    trans_( nullptr ), legendre_( legendre ), fft_( new EmptyCacheEntry() ) {}

Cache::Cache( const std::shared_ptr<TransCacheEntry>& legendre, const std::shared_ptr<TransCacheEntry>& fft ) :
    trans_( nullptr ), legendre_( legendre ), fft_( fft ) {}

Cache::Cache( const TransImpl* trans ) :
    trans_( trans ), legendre_( new EmptyCacheEntry() ), fft_( new EmptyCacheEntry() ) {}

Cache::Cache() : trans_( nullptr ), legendre_( new EmptyCacheEntry() ), fft_( new EmptyCacheEntry() ) {}

Cache::operator bool() const {
    return trans_ || bool( legendre() );
}

Cache::~Cache() = default;

TransCache::TransCache( const Trans& trans ) : Cache( trans.get() ) {}

TransCacheOwnedMemoryEntry::TransCacheOwnedMemoryEntry( size_t size ) : size_( size ) {
    if ( size_ ) {
        data_ = std::malloc( size_ );
    }
}

TransCacheOwnedMemoryEntry::~TransCacheOwnedMemoryEntry() {
    if ( size_ ) {
        std::free( data_ );
    }
}

}  // namespace trans
}  // namespace atlas
