/*
    libmaus2
    Copyright (C) 2021 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/>.
*/
#if ! defined(LIBMAUS2_BED_BED_HPP)
#define LIBMAUS2_BED_BED_HPP

#include <libmaus2/math/IntegerInterval.hpp>
#include <libmaus2/util/stringFunctions.hpp>
#include <libmaus2/aio/InputStreamInstance.hpp>
#include <map>

namespace libmaus2
{
	namespace bed
	{
		struct BED
		{
			template<typename type>
			static type parse(std::string const & s)
			{
				std::istringstream istr(s);
				type v;
				istr >> v;

				if ( istr && istr.peek() == std::istream::traits_type::eof() )
					return v;
				else
				{
					libmaus2::exception::LibMausException lme;
					lme.getStream() << "[E] BED::parse: unable to parse " << s << std::endl;
					lme.finish();
					throw lme;
				}
			}

			static std::map<
				std::string,
				std::vector< libmaus2::math::IntegerInterval<int64_t> >
			>
				loadBED(std::string const & fn)
			{
				libmaus2::aio::InputStreamInstance ISI(fn);
				return loadBED(ISI);
			}

			static std::map<
				std::string,
				std::vector< libmaus2::math::IntegerInterval<int64_t> >
			>
				loadBED(std::istream & ISI)
			{
				std::map< std::string, std::vector< libmaus2::math::IntegerInterval<int64_t> > > M;

				std::string line;
				while ( std::getline(ISI,line) )
				{
					if ( line.size() && line[0] != '\t' )
					{
						auto const V = libmaus2::util::stringFunctions::tokenize(line,std::string("\t"));

						if ( 2 < V.size() )
						{
							std::string const chr = V[0];
							int64_t const start = parse<int64_t>(V[1]);
							int64_t const end = parse<int64_t>(V[2]);
							libmaus2::math::IntegerInterval<int64_t> I(start,end-1);
							M[chr].push_back(I);
						}
					}
				}

				for ( auto P : M )
				{
					P.second = libmaus2::math::IntegerInterval<int64_t>::mergeTouchingOrOverlapping(P.second);
				}

				return M;
			}

			static void printStats(std::ostream & out, std::string const & fn)
			{
				auto const M = loadBED(fn);

				std::size_t intervals = 0;
				std::size_t diam = 0;

				for ( auto P : M )
				{
					auto const chr = P.first;
					auto const & V = P.second;

					intervals += V.size();

					for ( auto I : V )
						diam += I.diameter();
				}

				out << intervals << "\t" << diam << std::endl;
			}

		};
	}
}
#endif
