/**
    bambam
    Copyright (C) 2019 German Tischler

    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 <biobambam2/UpdateNumericalIndex.hpp>

#include <libmaus2/bambam/BamNumericalIndexGenerator.hpp>
#include <libmaus2/bambam/BamNumericalIndexDecoder.hpp>

namespace biobambam2
{
	/**
	 * update numerical index
	 *
	 * @param bamfn: file name of BAM file
	 * @param compdata: (input header size, output data)
	 **/
	void updateNumericalIndex(
		std::string const & bamfn,
		std::pair<uint64_t,std::string> const & compdata,
		std::ostream * verbstr
	)
	{
		std::string const indexfn = libmaus2::bambam::BamNumericalIndexBase::getIndexName(bamfn);

		if ( libmaus2::util::GetFileSize::fileExists(indexfn) )
		{
			std::string const replfn = indexfn + ".repl";
			libmaus2::aio::OutputStreamInstance::unique_ptr_type prepl(
				new libmaus2::aio::OutputStreamInstance(
					replfn
				)
			);

			// get index stats
			uint64_t alcnt, mod, numblocks;

			try
			{
				libmaus2::bambam::BamNumericalIndexDecoder indexdec(indexfn);
				mod = indexdec.getBlockSize();
				alcnt = indexdec.getAlignmentCount();
				numblocks = indexdec.size();
			}
			catch(std::exception const & ex)
			{
				libmaus2::exception::LibMausException lme;
				lme.getStream() << "[E] updateNumericalIndex: unable to get index stats: " << ex.what() << std::endl;
				lme.finish();
				throw lme;
			}

			// replace values for recompressed part
			std::istringstream compistr(compdata.second + libmaus2::lz::BgzfDeflateBase::getEOFBlock());
			libmaus2::bambam::BamRawDecoderNoThrowEOF BRD(compistr);

			typedef libmaus2::bambam::BamRawDecoderBase::RawInterval raw_interval;

			std::pair<
				std::pair<uint8_t const *,uint64_t>,
				raw_interval
			> P;

			int64_t highestSet = -1;
			for ( uint64_t i = 0; (P = BRD.getPos()).first.first; ++i )
				if ( (i % mod) == 0 )
				{
					assert ( i < alcnt );

					uint64_t const blockid = i / mod;
					std::pair<uint64_t,uint64_t> const & start = P.second.start;

					if ( verbstr )
						(*verbstr) << "replacing index position for " << i << " by " << start.first << "," << start.second << std::endl;

					libmaus2::bambam::BamNumericalIndexGenerator::ReplaceObject(
						blockid,start.first,start.second
					).serialise(*prepl);

					#if 0
					libmaus2::bambam::BamNumericalIndexGenerator::replaceValue(
						indexfn,
						blockid,
						start.first,
						start.second
					);
					#endif

					highestSet = blockid;
				}

			prepl->flush();
			prepl.reset();

			libmaus2::bambam::BamNumericalIndexGenerator::replaceValues(indexfn,replfn);

			libmaus2::aio::FileRemoval::removeFile(replfn);

			if ( verbstr )
				(*verbstr) << "shifting index positions for [" << highestSet+1 << "," << numblocks << ")" << " by " << static_cast<int64_t>(compdata.second.size()) - static_cast<int64_t>(compdata.first) << std::endl;

			try
			{
				// shift values in part we moved as is
				libmaus2::bambam::BamNumericalIndexGenerator::shiftValuesStreaming(
					indexfn,
					highestSet + 1,numblocks,
					static_cast<int64_t>(compdata.second.size()) - static_cast<int64_t>(compdata.first)
				);
			}
			catch(std::exception const & ex)
			{
				libmaus2::exception::LibMausException lme;
				lme.getStream() << "[E] updateNumericalIndex: failed in shiftValuesStreaming: " << ex.what() << std::endl;
				lme.finish();
				throw lme;
			}
		}
	}
}
