/*
    Copyright (C) 2018 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/>.
*/
#if ! defined(LIBMAUS2_DAZZLER_ALIGN_LASMERGE2_HPP)
#define LIBMAUS2_DAZZLER_ALIGN_LASMERGE2_HPP

#include <libmaus2/util/FiniteSizeHeap.hpp>
#include <libmaus2/dazzler/align/AlignmentWriter.hpp>
#include <libmaus2/dazzler/align/SimpleOverlapParser.hpp>
#include <libmaus2/dazzler/align/SortingOverlapOutputBuffer.hpp>

namespace libmaus2
{
	namespace dazzler
	{
		namespace align
		{
			struct LasMerge2
			{
				static void runmerge(std::string const & O, std::vector<std::string> const & I, bool const index)
				{
					int64_t const tspace = libmaus2::dazzler::align::AlignmentFile::getTSpace(I);
					uint64_t novl = 0;
					for ( uint64_t i = 0; i < I.size(); ++i )
						novl += libmaus2::dazzler::align::AlignmentFile::getNovl(I[i]);
					libmaus2::dazzler::align::AlignmentWriter::unique_ptr_type AW(new libmaus2::dazzler::align::AlignmentWriter(O, tspace, index, novl));

					libmaus2::autoarray::AutoArray < libmaus2::dazzler::align::SimpleOverlapParser::unique_ptr_type > ASOP(I.size());
					libmaus2::util::FiniteSizeHeap < libmaus2::dazzler::align::OverlapData::DataIndex, libmaus2::dazzler::align::OverlapDataInterfaceFullComparator > FSH(I.size());

					for ( uint64_t i = 0; i < I.size(); ++i )
					{
						libmaus2::dazzler::align::SimpleOverlapParser::unique_ptr_type SOP(
							new libmaus2::dazzler::align::SimpleOverlapParser(I[i],256*1024)
						);

						ASOP[i] = std::move(SOP);

						while ( ASOP[i]->parseNextBlock() )
						{
							if ( ASOP[i]->getData().size() )
							{
								FSH.push(libmaus2::dazzler::align::OverlapData::DataIndex(
									i,&(ASOP[i]->getData()),0)
								);

								break;
							}
						}
					}

					while ( ! FSH.empty() )
					{
						libmaus2::dazzler::align::OverlapData::DataIndex D = FSH.pop();

						std::pair<uint8_t const *, uint8_t const *> const P = D.data->getData(D.index);

						AW->put(P.first,P.second);

						if ( D.index + 1 < D.data->size() )
						{
							D.index += 1;
							FSH.push(D);
						}
						else
						{
							while ( ASOP[D.parserid]->parseNextBlock() )
								if ( ASOP[D.parserid]->getData().size() )
								{
									D.index = 0;
									FSH.push(D);
									break;
								}
						}
					}
				}

				static int lasmerge2(
					std::string const & outfilename,
					std::vector<std::string> infilenames,
					std::string const & tmpfilebase,
					uint64_t const mergefanin,
					bool const index
				)
				{
					assert ( infilenames.size() );

					uint64_t nextid = 0;
					if ( infilenames.size() > 1 )
					{
						assert ( mergefanin > 1 );
						bool deleteinput = false;

						while ( infilenames.size() > mergefanin )
						{
							uint64_t const packs = ( infilenames.size() + mergefanin - 1 ) / mergefanin;
							uint64_t const tmergefanin = ( infilenames.size() + packs - 1 ) / packs;

							uint64_t s = 0;
							std::vector<std::string> IV(packs);
							for ( uint64_t z = 0; z < packs; ++z )
							{
								uint64_t const low  = z * tmergefanin;
								uint64_t const high = std::min(low+tmergefanin,static_cast<uint64_t>(infilenames.size()));
								assert ( high > low );
								s += high - low;

								std::vector<std::string> I(infilenames.begin()+low,infilenames.begin()+high);
								std::ostringstream fnostr;
								fnostr << tmpfilebase << "_" << std::setw(6) << std::setfill('0') << (nextid++);
								std::string const O = fnostr.str();
								libmaus2::util::TempFileRemovalContainer::addTempFile(O);

								LasMerge2::runmerge(O,I,index);

								if ( deleteinput )
									for ( uint64_t i = 0; i < I.size(); ++i )
										libmaus2::dazzler::align::SortingOverlapOutputBuffer<>::removeFileAndIndex(I[i]);

								IV[z] = O;
							}
							assert ( s == infilenames.size() );

							infilenames = IV;
							deleteinput = true;
						}

						assert ( infilenames.size() <= mergefanin );

						LasMerge2::runmerge(outfilename,infilenames,index);

						if ( deleteinput )
							for ( uint64_t i = 0; i < infilenames.size(); ++i )
								libmaus2::dazzler::align::SortingOverlapOutputBuffer<>::removeFileAndIndex(infilenames[i]);
					}
					else
					{
						libmaus2::util::GetFileSize::copy(infilenames[0],outfilename);
						if ( index )
							libmaus2::dazzler::align::OverlapIndexer::constructIndexIf(outfilename);
					}

					return EXIT_SUCCESS;

				}
			};
		}
	}
}
#endif
