/*
    libmaus2
    Copyright (C) 2020 German Tischler-Höhle

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <libmaus2/arch/PageSize.hpp>
#include <stdexcept>
#include <sstream>
#include <cstring>

#include <libmaus2/LibMausConfig.hpp>

#if defined(_WIN32)
#include <libmaus2/LibMausWindows.hpp>
#endif

#if defined(LIBMAUS2_HAVE_UNISTD_H)
#include <unistd.h>
#endif

#if defined(__APPLE__)
#include <sys/sysctl.h>
#endif

static int64_t getPageSizeLocal()
{
	#if defined(_WIN32)
	SYSTEM_INFO system_info;
	GetSystemInfo (&system_info);
	return system_info.dwPageSize;
	#elif defined(LIBMAUS2_HAVE_SYSCONF) && defined(_SC_PAGESIZE)
	long const v = sysconf(_SC_PAGESIZE);

	if ( v < 0 )
	{
		int const error = errno;
		std::ostringstream ostr;
		ostr << "libmaus2::arch::PageSize::getPageSize() failed: " << strerror(error);
		throw std::runtime_error(ostr.str());
	}

	return static_cast<int64_t>(v);
	#elif defined(LIBMAUS2_HAVE_SYSCONF) && defined(PAGE_SIZE)
	long const v = sysconf(PAGE_SIZE);

	if ( v < 0 )
	{
		int const error = errno;
		std::ostringstream ostr;
		ostr << "libmaus2::arch::PageSize::getPageSize() failed: " << strerror(error);
		throw std::runtime_error(ostr.str());
	}

	return static_cast<int64_t>(v);
	#elif defined(LIBMAUS2_HAVE_GETPAGESIZE)
	return getpagesize();
	#else
	throw std::runtime_error("libmaus2::arch::PageSize::getPageSize(): not supported.");
	#endif
}


std::mutex libmaus2::arch::PageSize::pagesizelock;
std::atomic<int> libmaus2::arch::PageSize::pagesizeinitcomplete(0);
std::atomic<unsigned int> libmaus2::arch::PageSize::pagesize(0);

std::size_t libmaus2::arch::PageSize::getPageSize()
{
	std::lock_guard<std::mutex> slock(pagesizelock);

	if ( ! pagesizeinitcomplete.load() )
	{
		pagesize.store(getPageSizeLocal());
		pagesizeinitcomplete.store(1);
	}

	std::size_t const lpagesize = pagesize.load();

	return lpagesize;
}
