/**
    biobambam
    Copyright (C) 2009-2013 German Tischler
    Copyright (C) 2011-2013 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/>.
**/

#include <biobambam2/BamBamConfig.hpp>
#include <biobambam2/Licensing.hpp>
#include <biobambam2/DepthInterval.hpp>

#include <config.h>

#include <libmaus2/util/TempFileRemovalContainer.hpp>
#include <libmaus2/bambam/BamMultiAlignmentDecoderFactory.hpp>
#include <libmaus2/sorting/SortingBufferedOutputFile.hpp>

void bamdepthintersect(libmaus2::util::ArgInfo const & arginfo)
{
	libmaus2::bambam::BamAlignmentDecoderWrapper::unique_ptr_type decwrapper(
		libmaus2::bambam::BamMultiAlignmentDecoderFactory::construct(arginfo,true /* put rank */));
	::libmaus2::bambam::BamAlignmentDecoder * ppdec = &(decwrapper->getDecoder());
	::libmaus2::bambam::BamAlignmentDecoder & dec = *ppdec;

	std::string const tmpfilenamebase = arginfo.getValue<std::string>("tmpfile",arginfo.getDefaultTmpFileName());
	std::string const tmpfilenameout = tmpfilenamebase + "_bamdepthintersect";
	libmaus2::util::TempFileRemovalContainer::addTempFile(tmpfilenameout);
	bool const binary = arginfo.getValue<int>("binary",0);
	uint64_t const mindepth = arginfo.getValueUnsignedNumeric<uint64_t>("mindepth",0);
	uint64_t const maxdepth = arginfo.getValueUnsignedNumeric<uint64_t>("maxdepth",std::numeric_limits<uint64_t>::max());

	libmaus2::sorting::SerialisingSortingBufferedOutputFile<biobambam2::DepthInterval>::unique_ptr_type Psorter(
		new libmaus2::sorting::SerialisingSortingBufferedOutputFile<biobambam2::DepthInterval>(tmpfilenameout,32*1024*1024 / sizeof(biobambam2::DepthInterval) /* buf size */)
	);

	for ( uint64_t i = 0; i < arginfo.getNumRestArgs(); ++i )
	{
		std::string const fn = arginfo.getUnparsedRestArg(i);
		libmaus2::aio::InputStreamInstance ISI(fn);
		biobambam2::DepthInterval D;
		while ( ISI.peek() != std::istream::traits_type::eof() )
		{
			D.deserialise(ISI);
			Psorter->put(D);
		}
	}

	libmaus2::sorting::SerialisingSortingBufferedOutputFile<biobambam2::DepthInterval>::merger_ptr_type Pmerger(Psorter->getMerger());

	libmaus2::util::FiniteSizeHeap<uint64_t> FSH(0);

	uint64_t prevstart = 0;
	uint64_t depth = 0;
	int64_t prevrefid = -1;

	biobambam2::DepthInterval ID;
	while ( Pmerger->getNext(ID) )
	{
		libmaus2::math::IntegerInterval<int64_t> const I = ID.getInterval();

		while (
			(!FSH.empty())
			&&
			(
				static_cast<int64_t>(ID.refid) != prevrefid
				||
				static_cast<int64_t>(FSH.top()) <= I.from
			)
		)
		{
			uint64_t const end = FSH.pop();

			if ( end > prevstart )
				if ( depth >= mindepth && depth <= maxdepth )
				{
					biobambam2::DepthInterval const D(prevrefid,prevstart,end,depth);
					if ( binary )
						D.serialise(std::cout);
					else
						std::cout << D << "\n";
				}

			depth -= 1;
			prevstart = end;
		}

		if ( static_cast<int64_t>(ID.refid) != prevrefid && prevrefid >= 0 )
		{
			assert ( depth == 0 );

			int64_t const len = dec.getHeader().getRefIDLength(prevrefid);

			if ( len > static_cast<int64_t>(prevstart) )
				if ( depth >= mindepth && depth <= maxdepth )
				{
					biobambam2::DepthInterval const D(prevrefid,prevstart,len,depth);
					if ( binary )
						D.serialise(std::cout);
					else
						std::cout << D << "\n";

				}

			prevstart = 0;
		}

		if ( I.from > static_cast<int64_t>(prevstart) )
		{
			if ( depth >= mindepth && depth <= maxdepth )
			{
				biobambam2::DepthInterval const D(ID.refid,prevstart,I.from,depth);
				if ( binary )
					D.serialise(std::cout);
				else
					std::cout << D << "\n";
			}
		}

		depth += 1;
		prevstart = I.from;

		FSH.pushBump(I.from + I.diameter());

		prevrefid = ID.refid;
	}


	Pmerger.reset();
	Psorter.reset();

	libmaus2::aio::FileRemoval::removeFile(tmpfilenameout);
}

int main(int argc, char * argv[])
{
	try
	{
		::libmaus2::util::ArgInfo const arginfo(argc,argv);

		for ( uint64_t i = 0; i < arginfo.restargs.size(); ++i )
			if (
				arginfo.restargs[i] == "-v"
				||
				arginfo.restargs[i] == "--version"
			)
			{
				std::cerr << ::biobambam2::Licensing::license();
				return EXIT_SUCCESS;
			}
			else if (
				arginfo.restargs[i] == "-h"
				||
				arginfo.restargs[i] == "--help"
			)
			{
				std::cerr << ::biobambam2::Licensing::license() << std::endl;
				std::cerr << "Key=Value pairs:" << std::endl;
				std::cerr << std::endl;

				std::vector< std::pair<std::string,std::string> > V;

				V.push_back ( std::pair<std::string,std::string> ( "exclude=<[SECONDARY,SUPPLEMENTARY]>", "exclude alignments matching any of the given flags" ) );
				V.push_back ( std::pair<std::string,std::string> ( std::string("T=<[") + arginfo.getDefaultTmpFileName() + "]>" , "temporary file name" ) );
				V.push_back ( std::pair<std::string,std::string> ( std::string("prefix=<[]>"), "output file name prefix" ) );

				::biobambam2::Licensing::printMap(std::cerr,V);

				std::cerr << std::endl;
				std::cerr << "Alignment flags: PAIRED,PROPER_PAIR,UNMAP,MUNMAP,REVERSE,MREVERSE,READ1,READ2,SECONDARY,QCFAIL,DUP,SUPPLEMENTARY" << std::endl;

				std::cerr << std::endl;
				return EXIT_SUCCESS;
			}

		bamdepthintersect(arginfo);
	}
	catch(std::exception const & ex)
	{
		std::cerr << ex.what() << std::endl;
		return EXIT_FAILURE;
	}
}
