/*
    libmaus2
    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_AIO_OUTPUTSTREAMINSTANCEARRAY_HPP)
#define LIBMAUS2_AIO_OUTPUTSTREAMINSTANCEARRAY_HPP

#include <libmaus2/sorting/SortingBufferedOutputFile.hpp>
#include <libmaus2/util/Concat.hpp>

namespace libmaus2
{
	namespace aio
	{
		struct OutputStreamInstanceArray
		{
			typedef OutputStreamInstanceArray this_type;
			typedef std::unique_ptr<this_type> unique_ptr_type;
			typedef std::shared_ptr<this_type> shared_ptr_type;

			std::vector<std::string> Vfn;
			libmaus2::autoarray::AutoArray<libmaus2::aio::OutputStreamInstance::unique_ptr_type> AOSI;

			OutputStreamInstanceArray(std::string const & basefn, uint64_t const rnum)
			: Vfn(rnum), AOSI(rnum)
			{
				for ( uint64_t i = 0; i < Vfn.size(); ++i )
				{
					std::ostringstream ostr;
					ostr << basefn << "_" << std::setw(6) << std::setfill('0') << i;
					Vfn[i] = ostr.str();
					libmaus2::util::TempFileRemovalContainer::addTempFile(Vfn[i]);

					libmaus2::aio::OutputStreamInstance::unique_ptr_type tptr(
						new libmaus2::aio::OutputStreamInstance(Vfn[i])
					);
					AOSI[i] = std::move(tptr);
				}
			}

			libmaus2::aio::OutputStreamInstance & operator[](uint64_t const i)
			{
				return *(AOSI[i]);
			}

			void flush()
			{
				for ( uint64_t i = 0; i < Vfn.size(); ++i )
					if ( AOSI[i] )
						AOSI[i]->flush();
			}

			void close()
			{
				for ( uint64_t i = 0; i < Vfn.size(); ++i )
				{
					AOSI[i]->flush();
					AOSI[i].reset();
				}
			}

			uint64_t concat(std::string const & out, bool const rm = false)
			{
				flush();
				close();

				uint64_t u = 0;
				libmaus2::aio::OutputStreamInstance OSI(out);
				for ( uint64_t i = 0; i < Vfn.size(); ++i )
				{
					{
					libmaus2::aio::InputStreamInstance ISI(Vfn[i]);
					u += libmaus2::util::Concat::concat(ISI,OSI);
					}

					if ( rm )
						libmaus2::aio::FileRemoval::removeFile(Vfn[i]);
				}

				return u;
			}

			template<typename element_type>
			void sortSingle(
				uint64_t const blocksize = libmaus2::sorting::SerialisingSortingBufferedOutputFile<element_type>::getDefaultSortBlockSize(),
                                uint64_t const backblocksize = libmaus2::sorting::SerialisingSortingBufferedOutputFile<element_type>::getDefaultBackBlockSize(),
                                uint64_t const maxfan = libmaus2::sorting::SerialisingSortingBufferedOutputFile<element_type>::getDefaultMaxFan(),
                                uint64_t const sortthreads = libmaus2::sorting::SerialisingSortingBufferedOutputFile<element_type>::getDefaultSortThreads()
			)
			{
				flush();
				close();

				for ( uint64_t i = 0; i < Vfn.size(); ++i )
					libmaus2::sorting::SerialisingSortingBufferedOutputFile<element_type>::sort(Vfn[i],blocksize,backblocksize,maxfan,sortthreads);
			}

			template<typename element_type>
			void reduce(
				std::string const & out,
				uint64_t const blocksize = libmaus2::sorting::SerialisingSortingBufferedOutputFile<element_type>::getDefaultSortBlockSize(),
                                uint64_t const backblocksize = libmaus2::sorting::SerialisingSortingBufferedOutputFile<element_type>::getDefaultBackBlockSize(),
                                uint64_t const maxfan = libmaus2::sorting::SerialisingSortingBufferedOutputFile<element_type>::getDefaultMaxFan(),
                                uint64_t const sortthreads = libmaus2::sorting::SerialisingSortingBufferedOutputFile<element_type>::getDefaultSortThreads()
			)
			{
				flush();
				close();
				libmaus2::sorting::SerialisingSortingBufferedOutputFile<element_type>::reduce(Vfn,out,blocksize,backblocksize,maxfan,sortthreads);
				for ( uint64_t i = 0; i < Vfn.size(); ++i )
					libmaus2::aio::FileRemoval::removeFile(Vfn[i]);
			}
		};
	}
}
#endif
