/**
    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/ReadHeader.hpp>
#include <biobambam2/RunEOFFilter.hpp>

#include <libmaus2/lz/BgzfInflate.hpp>
#include <libmaus2/bambam/BamHeader.hpp>
#include <libmaus2/lz/BgzfDeflate.hpp>
#include <libmaus2/util/GetCObject.hpp>

std::pair<uint64_t,std::string> readHeader(std::istream & in, biobambam2::HeaderUpdate const & HU, int const level, bool const dropheader)
{
	libmaus2::lz::BgzfInflateBase base;
	uint64_t const maxblocksize = libmaus2::lz::BgzfConstants::getBgzfMaxBlockSize();
	libmaus2::autoarray::AutoArray<char> decompBuffer(maxblocksize,false);
	libmaus2::bambam::BamHeaderParserState BHPS;

	std::ostringstream frontHeaderOstr;
	std::ostringstream frontRestOstr;

	std::ostringstream frontHeaderCompOstr;
	std::ostringstream frontRestCompOstr;

	uint64_t frontcomp = 0;

	bool done = false;

	while ( ! done )
	{
		libmaus2::lz::BgzfInflateBase::BaseBlockInfo const BBI = base.readBlock(in);
		uint64_t const decompsize = base.decompressBlock(decompBuffer.begin(),BBI);
		unsigned char const * udecomp = reinterpret_cast<unsigned char const *>(decompBuffer.begin());
		libmaus2::util::GetCObject<unsigned char const *> GO(udecomp);

		std::pair<bool,uint64_t> const P = BHPS.parseHeader(GO,decompsize);

		// header complete?
		if ( P.first )
		{
			// uncompressed part not used by header
			char const * unused_start = decompBuffer.begin()+P.second;
			char const * unused_end = decompBuffer.begin()+decompsize;
			uint64_t const unused_size = unused_end - unused_start;

			// compute output header by invoking callback
			std::string const outheader = HU(std::string(BHPS.text.begin(),BHPS.text.begin()+BHPS.l_text));

			// construct header structure
			libmaus2::bambam::BamHeader header(outheader);
			// write header if we are not asked to drop it
			if ( ! dropheader )
				header.serialise(frontHeaderOstr);

			// write non header data
			frontRestOstr.write(unused_start,unused_size);
		}

		frontcomp += BBI.compdatasize;

		done = P.first;
	}

	{
		std::string const frontHeader = frontHeaderOstr.str();
		libmaus2::lz::BgzfDeflate<std::ostream> BOS(frontHeaderCompOstr,level,true);
		BOS.write(frontHeader.c_str(),frontHeader.size());
		BOS.flush();
	}

	{
		std::string const frontRest = frontRestOstr.str();
		libmaus2::lz::BgzfDeflate<std::ostream> BOS(frontRestCompOstr,level,true);
		BOS.write(frontRest.c_str(),frontRest.size());
		BOS.flush();
	}

	std::string const frontCompHeader = biobambam2::runEOFFilter(frontHeaderCompOstr.str());
	std::string const frontCompRest   = biobambam2::runEOFFilter(frontRestCompOstr.str()  );

	std::string const frontComp = frontCompHeader + frontCompRest;

	return std::pair<uint64_t,std::string>(frontcomp,frontComp);
}
