/*
    libmaus2
    Copyright (C) 2009-2013 German Tischler
    Copyright (C) 2011-2013 Genome Research Limited

    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/LibMausConfig.hpp>

#if defined(_WIN32)
#include <io.h>
#include <fcntl.h>
#include <cstdio>

struct W64SetMode
{
	W64SetMode()
	{
		_setmode(_fileno(stdin),_O_BINARY);
		_setmode(_fileno(stdout),_O_BINARY);
		_setmode(_fileno(stderr),_O_BINARY);
	}
};

W64SetMode const w64setmod;
#endif

#if defined(LIBMAUS2_HAVE_WINSOCK2_H)
#include <winsock2.h>
#include <iostream>

struct LibMaus2WinSockInit
{
	LibMaus2WinSockInit()
	{
		WORD wVersionRequested;
		WSADATA wsaData;
		int err;

		/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
		wVersionRequested = MAKEWORD(1, 0);

		err = WSAStartup(wVersionRequested, &wsaData);
		if (err != 0)
		{
			std::cerr << "WSAStartup failed with error: " << err << std::endl;
			abort();
		}
	}
	~LibMaus2WinSockInit()
	{
		WSACleanup();
	}
};

LibMaus2WinSockInit const winsockinit;
#endif

#if defined(LIBMAUS2_HAVE_LIBSECRECY)

#include <libmaus2/aio/SecrecyCache.hpp>

libsecrecy::RawKeyBaseCache libmaus2::aio::SecrecyCache::secrecyCache;
#endif

#include <libmaus2/network/CurlInitObject.hpp>

libmaus2::network::CurlInitObject const curlInitObject;

#include <libmaus2/aio/StreamLock.hpp>

libmaus2::aio::StreamLock::lock_type libmaus2::aio::StreamLock::coutlock;
libmaus2::aio::StreamLock::lock_type libmaus2::aio::StreamLock::cerrlock;
libmaus2::aio::StreamLock::lock_type libmaus2::aio::StreamLock::cinlock;

#include <unistd.h>

#include <libmaus2/types/types.hpp>
#include <libmaus2/autoarray/AutoArray.hpp>
#include <libmaus2/util/stringFunctions.hpp>
#include <libmaus2/aio/PosixFdInput.hpp>
#include <limits>
#include <cstdlib>
#include <sstream>
#include <iostream>
#include <libmaus2/dazzler/align/AlignmentFileConstants.hpp>

#include <libmaus2/rank/CodeBase.hpp>

typedef ::libmaus2::rank::ChooseCache choose_cache_type;
choose_cache_type libmaus2::rank::CodeBase::CC64(64);

#include <libmaus2/rank/ERankBase.hpp>

typedef ::libmaus2::rank::EncodeCache<16,uint16_t> encode_cache_type;
encode_cache_type libmaus2::rank::ERankBase::EC16;

typedef ::libmaus2::rank::DecodeCache<16,uint16_t> decode_cache_type;
decode_cache_type libmaus2::rank::ERankBase::DC16;

#include <libmaus2/rank/RankTable.hpp>

#if defined(RANKTABLES)
typedef ::libmaus2::rank::RankTable rank_table_type;
typedef ::libmaus2::rank::SimpleRankTable simple_rank_table_type;
const rank_table_type libmaus2::rank::ERankBase::R;
const simple_rank_table_type libmaus2::rank::ERankBase::S;
#endif

#include <libmaus2/util/SaturatingCounter.hpp>

unsigned int const ::libmaus2::util::SaturatingCounter::shift[4] = { 6,4,2,0 };
unsigned char const ::libmaus2::util::SaturatingCounter::mask[4] = {
		static_cast<uint8_t>(~(3 << 6)),
		static_cast<uint8_t>(~(3 << 4)),
		static_cast<uint8_t>(~(3 << 2)),
		static_cast<uint8_t>(~(3 << 0))
};

#include <libmaus2/util/AlphaDigitTable.hpp>

libmaus2::util::AlphaDigitTable::AlphaDigitTable()
{
	memset(&A[0],0,sizeof(A));

	A[static_cast<int>('0')] = 1;
	A[static_cast<int>('1')] = 1;
	A[static_cast<int>('2')] = 1;
	A[static_cast<int>('3')] = 1;
	A[static_cast<int>('4')] = 1;
	A[static_cast<int>('5')] = 1;
	A[static_cast<int>('6')] = 1;
	A[static_cast<int>('7')] = 1;
	A[static_cast<int>('8')] = 1;
	A[static_cast<int>('9')] = 1;

	for ( int i = 'a'; i <= 'z'; ++i )
		A[i] = 1;
	for ( int i = 'A'; i <= 'Z'; ++i )
		A[i] = 1;
}

#include <libmaus2/util/AlphaTable.hpp>

libmaus2::util::AlphaTable::AlphaTable()
{
	memset(&A[0],0,sizeof(A));

	for ( int i = 'a'; i <= 'z'; ++i )
		A[i] = 1;
	for ( int i = 'A'; i <= 'Z'; ++i )
		A[i] = 1;
}

#include <libmaus2/util/DigitTable.hpp>

libmaus2::util::DigitTable::DigitTable()
{
	memset(&A[0],0,sizeof(A));
	A[static_cast<int>('0')] = 1;
	A[static_cast<int>('1')] = 1;
	A[static_cast<int>('2')] = 1;
	A[static_cast<int>('3')] = 1;
	A[static_cast<int>('4')] = 1;
	A[static_cast<int>('5')] = 1;
	A[static_cast<int>('6')] = 1;
	A[static_cast<int>('7')] = 1;
	A[static_cast<int>('8')] = 1;
	A[static_cast<int>('9')] = 1;
}

#include <libmaus2/bambam/SamPrintableTable.hpp>

libmaus2::bambam::SamPrintableTable::SamPrintableTable()
{
	memset(&A[0],0,sizeof(A));

	for ( int i = '!'; i <= '~'; ++i )
		A[i] = 1;
}

#include <libmaus2/bambam/SamZPrintableTable.hpp>

libmaus2::bambam::SamZPrintableTable::SamZPrintableTable()
{
	memset(&A[0],0,sizeof(A));
	A[static_cast<int>(' ')] = 1;

	for ( int i = '!'; i <= '~'; ++i )
		A[i] = 1;
}

#include <libmaus2/bambam/SamInfoBase.hpp>

libmaus2::util::DigitTable const libmaus2::bambam::SamInfoBase::DT;
libmaus2::util::AlphaDigitTable const libmaus2::bambam::SamInfoBase::ADT;
libmaus2::util::AlphaTable const libmaus2::bambam::SamInfoBase::AT;
libmaus2::bambam::SamPrintableTable const libmaus2::bambam::SamInfoBase::SPT;
libmaus2::bambam::SamZPrintableTable const libmaus2::bambam::SamInfoBase::SZPT;
libmaus2::math::DecimalNumberParser const libmaus2::bambam::SamInfoBase::DNP;

#include <libmaus2/aio/InputStreamFactoryContainer.hpp>

std::map<std::string,libmaus2::aio::InputStreamFactory::shared_ptr_type> libmaus2::aio::InputStreamFactoryContainer::factories =
	libmaus2::aio::InputStreamFactoryContainer::setupFactories();

#include <libmaus2/aio/OutputStreamFactoryContainer.hpp>

std::map<std::string,libmaus2::aio::OutputStreamFactory::shared_ptr_type> libmaus2::aio::OutputStreamFactoryContainer::factories =
	libmaus2::aio::OutputStreamFactoryContainer::setupFactories();

#include <libmaus2/aio/InputOutputStreamFactoryContainer.hpp>

std::map<std::string,libmaus2::aio::InputOutputStreamFactory::shared_ptr_type> libmaus2::aio::InputOutputStreamFactoryContainer::factories =
	libmaus2::aio::InputOutputStreamFactoryContainer::setupFactories();

#include <libmaus2/bambam/ScramInputContainer.hpp>
#include <libmaus2/aio/InputStreamInstance.hpp>

std::map<void *, std::shared_ptr<scram_cram_io_input_t> > libmaus2::bambam::ScramInputContainer::Mcontrol;
std::map<void *, libmaus2::aio::InputStreamInstance::shared_ptr_type> libmaus2::bambam::ScramInputContainer::Mstream;
std::map<void *, libmaus2::aio::InputStreamInstance::shared_ptr_type> libmaus2::bambam::ScramInputContainer::Mcompstream;
libmaus2::parallel::StdMutex libmaus2::bambam::ScramInputContainer::Mlock;

#include <libmaus2/digest/DigestFactoryContainer.hpp>

std::map< std::string, libmaus2::digest::DigestFactoryInterface::shared_ptr_type > libmaus2::digest::DigestFactoryContainer::factories =
	libmaus2::digest::DigestFactoryContainer::setupFactories();

#include <libmaus2/util/NotDigitOrTermTable.hpp>
char const libmaus2::util::NotDigitOrTermTable::table[256] = {
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
};

#include <libmaus2/aio/MemoryFileContainer.hpp>
#include <libmaus2/util/ArgInfoParseBase.hpp>

libmaus2::parallel::StdMutex libmaus2::aio::MemoryFileContainer::lock;
std::map < std::string, libmaus2::aio::MemoryFile::shared_ptr_type > libmaus2::aio::MemoryFileContainer::M;

static uint64_t getMemoryFileMaxBlockSize()
{
	char const * mem = getenv("MEMORYFILEMAXBLOCKSIZE");

	if ( ! mem )
		// return std::numeric_limits<uint64_t>::max();
		return 16ull*1024ull*1024ull;
	else
	{
		uint64_t const v = libmaus2::util::ArgInfoParseBase::parseValueUnsignedNumeric<uint64_t>("MEMORYFILEMAXBLOCKSIZE",mem);

		std::cerr << "[D] using value " << v << " (parsed from " << mem << ") for MEMORYFILEMAXBLOCKSIZE" << std::endl;

		return v;
	}
}

uint64_t libmaus2::aio::MemoryFile::maxblocksize = getMemoryFileMaxBlockSize();

#include <libmaus2/random/GaussianRandom.hpp>

static uint64_t getPosixFdInputWarnThreshold()
{
	char const * cthres = getenv("LIBMAUS2_AIO_POSIXFDINPUT_WARN_THRESHOLD");

	if ( cthres )
	{
		std::istringstream istr(cthres);
		uint64_t v;
		istr >> v;
		if ( istr )
		{
			// std::cerr << "setting warn threshold to " << v << std::endl;
			return v;
		}
		else
			return 0;
	}
	else
	{
		return 0;
	}
}

std::time_t const libmaus2::aio::PosixFdInput::warnThreshold = getPosixFdInputWarnThreshold();
std::atomic<uint64_t> libmaus2::aio::PosixFdInput::totalin(0);
libmaus2::parallel::StdSpinLock libmaus2::aio::PosixFdInput::totalinlock;

#include <libmaus2/aio/PosixFdOutputStreamBuffer.hpp>

static std::time_t getPosixOutputStreamBufferWarnThreshold()
{
	char const * cthres = getenv("LIBMAUS2_AIO_POSIXFDOUTPUTSTREAMBUFFER_WARN_THRESHOLD");

	if ( cthres )
	{
		std::istringstream istr(cthres);
		uint64_t v;
		istr >> v;
		if ( istr )
		{
			// std::cerr << "setting warn threshold to " << v << std::endl;
			return v;
		}
		else
			return 0;
	}
	else
	{
		return 0;
	}
}

int getPosixOutputStreamBufferCheck()
{
	char const * cthres = getenv("LIBMAUS2_AIO_POSIXFDOUTPUTSTREAMBUFFER_CHECK");

	if ( cthres )
	{
		std::istringstream istr(cthres);
		int v;
		istr >> v;
		if ( istr )
		{
			return v;
		}
		else
			return 0;
	}
	else
	{
		return 0;
	}
}

std::time_t const libmaus2::aio::PosixFdOutputStreamBuffer::warnThreshold = getPosixOutputStreamBufferWarnThreshold();
int const libmaus2::aio::PosixFdOutputStreamBuffer::check = getPosixOutputStreamBufferCheck();
std::atomic<uint64_t> libmaus2::aio::PosixFdOutputStreamBuffer::totalout(0);
libmaus2::parallel::StdSpinLock libmaus2::aio::PosixFdOutputStreamBuffer::totaloutlock;

static std::map<std::string,uint64_t> getPosixFdInputBlockSizeOverride()
{
	std::map<std::string,uint64_t> M;

	char const * envstr = getenv("LIBMAUS2_POSIXFDINPUT_BLOCKSIZE_OVERRIDE");

	if ( envstr )
	{
		std::string const senvstr(envstr);

		std::string::size_type p = 0;

		while ( p < senvstr.size() )
		{
			std::string::size_type h = p;
			while (
				h < senvstr.size() &&
				(
					senvstr[h] != ':'
					||
					(
						senvstr[h] == ':' &&
						h+1 < senvstr.size() &&
						senvstr[h+1] == ':'
					)
				)
			)
			{
				if ( senvstr[h] != ':' )
					++h;
				else
					h += 2;
			}

			assert ( h == senvstr.size() || senvstr[h] == ':' );

			std::string const ppart = senvstr.substr(p,h-p);
			std::vector<char> vpart(ppart.begin(),ppart.end());

			uint64_t o = 0;
			for ( uint64_t i = 0; i < vpart.size(); )
				if ( vpart[i] != ':' )
					vpart[o++] = vpart[i++];
				else
				{
					assert ( i+1 < vpart.size() && vpart[i+1] == ':' );
					vpart[o++] = vpart[i];
					i += 2;
				}
			vpart.resize(o);
			std::string const part(vpart.begin(),vpart.end());

			if ( part.find('=') != std::string::npos )
			{
				std::string::size_type const m = part.find('=');
				std::string const key = part.substr(0,m);
				std::string const val = part.substr(m+1);

				std::istringstream istr(val);
				uint64_t u = 0;

				istr >> u;
				bool ok = true;

				if ( istr )
				{
					uint64_t multiplier = 1;

					if ( istr.peek() != std::istream::traits_type::eof() )
					{
						char const unit = istr.get();

						if ( istr.peek() == std::istream::traits_type::eof() )
						{
							switch ( unit )
							{
								case 'k': multiplier = 1024ull; break;
								case 'K': multiplier = 1000ull; break;
								case 'm': multiplier = 1024ull*1024ull; break;
								case 'M': multiplier = 1000ull*1000ull; break;
								case 'g': multiplier = 1024ull*1024ull*1024ull; break;
								case 'G': multiplier = 1000ull*1000ull*1000ull; break;
								case 't': multiplier = 1024ull*1024ull*1024ull*1024ull; break;
								case 'T': multiplier = 1000ull*1000ull*1000ull*1000ull; break;
								case 'p': multiplier = 1024ull*1024ull*1024ull*1024ull*1024ull; break;
								case 'P': multiplier = 1000ull*1000ull*1000ull*1000ull*1000ull; break;
								case 'e': multiplier = 1024ull*1024ull*1024ull*1024ull*1024ull*1024ull; break;
								case 'E': multiplier = 1000ull*1000ull*1000ull*1000ull*1000ull*1000ull; break;
								default: ok = false; break;
							}

							if ( ok )
								u *= multiplier;
						}
						else
						{
							ok = false;
						}
					}
				}
				else
				{
					ok = false;
				}

				if ( ok && u > 0 )
				{
					M[key] = u;
				}
			}

			p = h+1;
		}
	}

	return M;
}

std::map<std::string,uint64_t> const libmaus2::aio::PosixFdInput::blocksizeoverride = getPosixFdInputBlockSizeOverride();

#include <libmaus2/util/TempFileRemovalContainer.hpp>

bool ::libmaus2::util::SignalHandlerContainer::setupComplete = false;
std::set < std::string > libmaus2::util::TempFileRemovalContainer::tmpfilenames;
std::vector < std::string > libmaus2::util::TempFileRemovalContainer::tmpdirectories;
bool ::libmaus2::util::TempFileRemovalContainer::setupComplete = false;
libmaus2::util::TempFileRemovalContainer::sighandler_t libmaus2::util::TempFileRemovalContainer::siginthandler = 0;
libmaus2::util::TempFileRemovalContainer::sighandler_t libmaus2::util::TempFileRemovalContainer::sigtermhandler = 0;
libmaus2::util::TempFileRemovalContainer::sighandler_t libmaus2::util::TempFileRemovalContainer::sighuphandler = 0;
libmaus2::util::TempFileRemovalContainer::sighandler_t libmaus2::util::TempFileRemovalContainer::sigpipehandler = 0;
::libmaus2::parallel::StdSpinLock libmaus2::util::TempFileRemovalContainer::lock;

#include <libmaus2/sorting/PairFileSorting.hpp>

static bool getPairFileSortingDelayDelete()
{
	char const * envstr = getenv("LIBMAUS2_PAIRFILESORTING_DELAYDELETE");

	if ( envstr )
	{
		std::string const senvstr(envstr);
		std::istringstream istr(senvstr);
		int v;
		istr >> v;
		if ( istr && istr.peek() == std::istream::traits_type::eof() )
		{
			std::cerr << "[V] setting libmaus2::sorting::PairFileSorting::delaydelete to " << v << std::endl;
			return v;
		}
		else
			return 0;
	}
	else
	{
		return false;
	}
}

bool const libmaus2::sorting::PairFileSorting::delaydelete = getPairFileSortingDelayDelete();

#include <libmaus2/lz/BgzfInflateBase.hpp>

static int getDefaultCheckCrc()
{
	return 1;
}

static bool getCheckCrc()
{
	char const * envstr = getenv("LIBMAUS2_LZ_BGZFINFLATEBASE_CHECKCRC");

	if ( envstr )
	{
		std::string const senvstr(envstr);
		std::istringstream istr(senvstr);
		int v;
		istr >> v;
		if ( istr && istr.peek() == std::istream::traits_type::eof() )
		{
			std::cerr << "[V] setting libmaus2::lz::BgzfInflateBase::checkCrc to " << v << std::endl;
			return v;
		}
		else
			return getDefaultCheckCrc();
	}
	else
	{
		return getDefaultCheckCrc();
	}
}

int libmaus2::lz::BgzfInflateBase::checkCrc = getCheckCrc();

#include <libmaus2/bambam/BamAlignmentDecoderFactory.hpp>

libmaus2::parallel::SimpleThreadPool * libmaus2::bambam::BamAlignmentDecoderFactory::STP = 0;

static uint64_t getDefaultTraceXOVR()
{
	return 125;
}

static uint64_t getTraceXOVR()
{
	char const * envstr = getenv("LIBMAUS2_DAZZLER_ALIGN_ALIGNMENTFILECONSTANTS_TRACE_XOVR");

	if ( envstr )
	{
		std::string const senvstr(envstr);
		std::istringstream istr(senvstr);
		uint64_t v;
		istr >> v;
		if ( istr && istr.peek() == std::istream::traits_type::eof() )
		{
			std::cerr << "[V] setting libmaus2::dazzler::align::AlignmentFileConstants::TRACE_XOVR to " << v << std::endl;
			return v;
		}
		else
			return getDefaultTraceXOVR();
	}
	else
	{
		return getDefaultTraceXOVR();
	}
}

uint8_t const libmaus2::dazzler::align::AlignmentFileConstants::TRACE_XOVR = getTraceXOVR();
