/**
    bambam
    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 <config.h>
#include <libmaus2/bambam/BamBlockWriterBaseFactory.hpp>
#include <libmaus2/bambam/BamWriter.hpp>
#include <libmaus2/bambam/BamHeaderUpdate.hpp>
#include <libmaus2/util/ArgInfo.hpp>
#include <biobambam2/Licensing.hpp>
#include <libmaus2/bambam/BamMultiAlignmentDecoderFactory.hpp>
#include <libmaus2/bambam/BamPeeker.hpp>

#include <libmaus2/lz/BgzfDeflateOutputCallbackMD5.hpp>
#include <libmaus2/bambam/BgzfDeflateOutputCallbackBamIndex.hpp>

static int getDefaultMD5() { return 0; }
static int getDefaultVerbose() { return 1; }
static int getDefaultLevel() {return libmaus2::lz::DeflateDefaults::getDefaultLevel();}

int bamfilter(libmaus2::util::ArgInfo const & arginfo)
{
	libmaus2::util::ArgInfo arginfo_0 = arginfo;
	libmaus2::util::ArgInfo arginfo_1 = arginfo;
	std::string md5filename;

	if ( arginfo.getNumRestArgs() < 2 )
	{
		std::cerr << "usage: " << arginfo.progname << " <data.bam> <names_to_keep.bam>" << std::endl;
		return EXIT_FAILURE;
	}

	arginfo_0.replaceKey("I",arginfo.getUnparsedRestArg(0));
	arginfo_1.replaceKey("I",arginfo.getUnparsedRestArg(1));

	int const level = libmaus2::bambam::BamBlockWriterBaseFactory::checkCompressionLevel(arginfo.getValue<int>("level",getDefaultLevel()));

	libmaus2::bambam::BamAlignmentDecoderWrapper::unique_ptr_type decwrapper_0(
		libmaus2::bambam::BamMultiAlignmentDecoderFactory::construct(
			arginfo_0
		)
	);
	libmaus2::bambam::BamAlignmentDecoder & BD_0 = decwrapper_0->getDecoder();
	::libmaus2::bambam::BamHeader const & bamheader_0 = BD_0.getHeader();
	::libmaus2::bambam::BamAlignment alignment_0;
	::libmaus2::bambam::BamPeeker peeker_0(BD_0);

	libmaus2::bambam::BamAlignmentDecoderWrapper::unique_ptr_type decwrapper_1(
		libmaus2::bambam::BamMultiAlignmentDecoderFactory::construct(
			arginfo_1
		)
	);
	libmaus2::bambam::BamAlignmentDecoder & BD_1 = decwrapper_1->getDecoder();
	// ::libmaus2::bambam::BamHeader const & bamheader_1 = BD_1.getHeader();
	::libmaus2::bambam::BamAlignment alignment_1;
	::libmaus2::bambam::BamPeeker peeker_1(BD_1);

	std::vector< ::libmaus2::lz::BgzfDeflateOutputCallback * > cbs;
	::libmaus2::lz::BgzfDeflateOutputCallbackMD5::unique_ptr_type Pmd5cb;
	if ( arginfo.getValue<unsigned int>("md5",getDefaultMD5()) )
	{
		if ( libmaus2::bambam::BamBlockWriterBaseFactory::getMD5FileName(arginfo) != std::string() )
			md5filename = libmaus2::bambam::BamBlockWriterBaseFactory::getMD5FileName(arginfo);
		else
			std::cerr << "[V] no filename for md5 given, not creating hash" << std::endl;

		if ( md5filename.size() )
		{
			::libmaus2::lz::BgzfDeflateOutputCallbackMD5::unique_ptr_type Tmd5cb(new ::libmaus2::lz::BgzfDeflateOutputCallbackMD5);
			Pmd5cb = std::move(Tmd5cb);
			cbs.push_back(Pmd5cb.get());
		}
	}


	std::vector< ::libmaus2::lz::BgzfDeflateOutputCallback * > * Pcbs = 0;
	if ( cbs.size() )
		Pcbs = &cbs;

	::libmaus2::bambam::BamHeader::unique_ptr_type uphead(libmaus2::bambam::BamHeaderUpdate::updateHeader(arginfo,bamheader_0,"bamfilterbyname",std::string(PACKAGE_VERSION)));
	::libmaus2::bambam::BamWriter::unique_ptr_type writer(new ::libmaus2::bambam::BamWriter(std::cout,*uphead,level,Pcbs));

	while (
		peeker_0.peekNext(alignment_0)
		&&
		peeker_1.peekNext(alignment_1)
	)
	{
		char const * name_0 = alignment_0.getName();
		char const * name_1 = alignment_1.getName();

		int const r = libmaus2::bambam::StrCmpNum::strcmpnum(name_0,name_1);

		if ( r < 0 )
		{
			peeker_0.getNext(alignment_0);
		}
		else if ( 0 < r )
		{
			peeker_1.getNext(alignment_1);
		}
		else
		{
			peeker_0.getNext(alignment_0);
			peeker_1.getNext(alignment_1);

			alignment_0.serialise(writer->getStream());
		}
	}

	writer.reset();

	if ( Pmd5cb )
	{
		Pmd5cb->saveDigestAsFile(md5filename);
	}

	return EXIT_SUCCESS;
}

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::cerr << 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> ( "level=<["+::biobambam2::Licensing::formatNumber(getDefaultLevel())+"]>", libmaus2::bambam::BamBlockWriterBaseFactory::getBamOutputLevelHelpText() ) );
				V.push_back ( std::pair<std::string,std::string> ( "verbose=<["+::biobambam2::Licensing::formatNumber(getDefaultVerbose())+"]>", "print progress report (default: 1)" ) );
				V.push_back ( std::pair<std::string,std::string> ( "md5=<["+::biobambam2::Licensing::formatNumber(getDefaultMD5())+"]>", "create md5 check sum (default: 0)" ) );
				V.push_back ( std::pair<std::string,std::string> ( "md5filename=<filename>", "file name for md5 check sum (default: extend output file name)" ) );

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

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

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