#include <string>
#include <vector>

#include "eckit/log/Bytes.h"
#include "eckit/log/ResourceUsage.h"
#include "eckit/system/ResourceUsage.h"

#include "atlas/domain.h"
#include "atlas/grid.h"
#include "atlas/runtime/Trace.h"
#include "atlas/runtime/trace/StopWatch.h"
#include "tests/AtlasTestEnvironment.h"

namespace atlas {
namespace test {

// Computation of gaussian latitudes for large grids can be quite costly in terms of
// time and memory.
// Atlas has a number of commonly used resolutions precomputed which can speed up Gaussian grids
// and prevents large peaks in memory usage. See gaussian Spacing.
//
// If more high resolution Gaussian latitudes need to be precomputed they can be added there, or in
// a dynamic library with self-registration.

struct Trace {
    struct Indentor {
        Indentor() { Log::info().indent(); }
        ~Indentor() { Log::info().unindent(); }
    } indentor;
    eckit::ResourceUsage resource_usage;
    atlas::Trace trace;

    Trace(const eckit::CodeLocation& where, const std::string& what):
        indentor{}, resource_usage{what, Log::debug()}, trace{where, what} {}

    double elapsed() const { return trace.elapsed(); }
    static size_t peakMemory() { return eckit::system::ResourceUsage().maxResidentSetSize(); }
    void stopAndReport() {
        trace.stop();
        Log::info() << "Time taken:        " << elapsed() << " sec" << std::endl;
        Log::info() << "Peak memory usage: " << eckit::Bytes(peakMemory()) << std::endl;
    }
};

constexpr size_t Mbytes = 1000000;

CASE("test resources for cropping large grids") {
    std::vector<std::string> gridnames{"L40000x20000", "N8000", "O8000"};

    for (auto& gridname : gridnames) {
        EXPECT(Trace::peakMemory() < 100 * Mbytes);

        SECTION(std::string("section") + gridname) {
            Trace trace(Here(), "Grid{" + gridname + ", GlobalDomain{-180}}");
            auto grid = Grid{gridname, GlobalDomain{-180}};
            trace.stopAndReport();
            EXPECT(trace.elapsed() < 10.);
            EXPECT(trace.peakMemory() < 100 * Mbytes);
        }
    }
}


}  // namespace test
}  // namespace atlas


int main(int argc, char* argv[]) {
    return atlas::test::run(argc, argv);
}
