/**
    libmaus2
    Copyright (C) 2009-2021 German Tischler-Höhle
    Copyright (C) 2011-2014 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/>.
**/
#if ! defined(LIBMAUS2_SUFFIXSORT_BWTB3M_BWTMERGEISABASE_HPP)
#define LIBMAUS2_SUFFIXSORT_BWTB3M_BWTMERGEISABASE_HPP

#include <libmaus2/util/GetCObject.hpp>
#include <libmaus2/suffixsort/GapArrayByteDecoderBuffer.hpp>
#include <libmaus2/gamma/GammaGapDecoder.hpp>
#include <libmaus2/timing/RealTimeClock.hpp>
#include <libmaus2/aio/ConcatInputStream.hpp>
#include <libmaus2/aio/SynchronousGenericInput.hpp>
#include <libmaus2/aio/SynchronousGenericOutput.hpp>

namespace libmaus2
{
	namespace suffixsort
	{
		namespace bwtb3m
		{
			template<typename gap_iterator>
			struct BwtMergeIsaBase
			{
				static uint64_t mergeIsa(
					std::vector<std::string> const & oldmergedisaname, // mergedisaname
					std::vector<std::string> const & newmergedisaname, // blockresults.files.sampledisa
					std::string const & mergedmergedisaname, // newmergedisaname
					uint64_t const blockstart, // blockstart
					gap_iterator & Gc,
					uint64_t const gn,
					std::ostream * logstr
				)
				{
					::libmaus2::timing::RealTimeClock rtc;

					// merge sampled inverse suffix arrays
					if ( logstr )
						(*logstr) << "[V] merging sampled inverse suffix arrays...";
					rtc.start();

					libmaus2::aio::ConcatInputStream ISIold(oldmergedisaname);
					libmaus2::aio::ConcatInputStream ISInew(newmergedisaname);

					::libmaus2::aio::SynchronousGenericInput<uint64_t> SGIISAold(ISIold,16*1024);
					::libmaus2::aio::SynchronousGenericInput<uint64_t> SGIISAnew(ISInew,16*1024);
					::libmaus2::aio::SynchronousGenericOutput<uint64_t> SGOISA(mergedmergedisaname,16*1024);

					// sum over old (RHS block) suffixes
					uint64_t s = 0;
					// rank of position zero in merged block
					uint64_t blockp0rank = 0;

					// scan the gap array
					for ( uint64_t i = 0; i < gn; ++i )
					{
						s += Gc.get(); // *(Gc++);

						// while old suffixes are smaller than next new suffix
						int64_t re;
						while ( (re=SGIISAold.peek()) >= 0 && re < static_cast<int64_t>(s) )
						{
							// rank (add number of new suffixes processed before)
							uint64_t const r = SGIISAold.get() + i;
							// position (copy as is)
							uint64_t const p = SGIISAold.get();

							SGOISA . put ( r );
							SGOISA . put ( p );
						}

						// is next sample in next (LHS) block for rank i?
						if ( SGIISAnew.peek() == static_cast<int64_t>(i) )
						{
							// add number of old suffixes to rank
							uint64_t const r = SGIISAnew.get() + s;
							// keep absolute position as is
							uint64_t const p = SGIISAnew.get();

							SGOISA . put ( r );
							SGOISA . put ( p );

							// check whether this rank is for the leftmost position in the merged block
							if ( p == blockstart )
								blockp0rank = r;
						}
					}

					assert ( SGIISAnew.peek() < 0 );
					assert ( SGIISAold.peek() < 0 );

					SGOISA.flush();
					if ( logstr )
						(*logstr) << "done, time " << rtc.getElapsedSeconds() << std::endl;

					return blockp0rank;
				}

			};

			extern template struct BwtMergeIsaBase< libmaus2::util::GetCObject<uint32_t const *> >;
			extern template struct BwtMergeIsaBase< libmaus2::suffixsort::GapArrayByteDecoderBuffer >;
			extern template struct BwtMergeIsaBase< libmaus2::gamma::GammaGapDecoder >;
		}
	}
}
#endif
