/*
    libmaus2
    Copyright (C) 2019 German Tischler-Höhle

    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_GTF_RANGESETS_HPP)
#define LIBMAUS2_GTF_RANGESETS_HPP

#include <libmaus2/gtf/GTFData.hpp>
#include <libmaus2/gtf/BAMGTFMap.hpp>
#include <libmaus2/geometry/RangeSet.hpp>

namespace libmaus2
{
	namespace gtf
	{
		struct RangeSets
		{
			typedef RangeSets this_type;
			typedef std::unique_ptr<this_type> unique_ptr_type;

			libmaus2::autoarray::AutoArray< libmaus2::geometry::RangeSet<libmaus2::gtf::Exon>::unique_ptr_type > VRS_forward;
			libmaus2::autoarray::AutoArray< libmaus2::geometry::RangeSet<libmaus2::gtf::Exon>::unique_ptr_type > VRS_reverse;
			libmaus2::autoarray::AutoArray< libmaus2::geometry::RangeSet<libmaus2::gtf::Exon>::unique_ptr_type > VRS;

			// maximum size detected for chromosomes mentioned in GTF data
			std::vector<uint64_t> Vmax;

			RangeSets(libmaus2::gtf::GTFData const & gtfdata, libmaus2::gtf::BAMGTFMap const & bamgtfmap, libmaus2::bambam::BamHeader const & bamheader)
			: VRS_forward(gtfdata.Vchr.size()), VRS_reverse(gtfdata.Vchr.size()), VRS(gtfdata.Vchr.size()), Vmax(gtfdata.Vchr.size(),0)
			{
				// check all exon coordinates
				for ( uint64_t i = 0; i < gtfdata.next_exon_id; ++i )
				{
					libmaus2::gtf::Exon const & exon = gtfdata.Aexon[i];
					libmaus2::gtf::Transcript const & transcript = gtfdata.Atranscript[exon.transcript_id];
					libmaus2::gtf::Gene const & gene = gtfdata.Agene[transcript.gene_id];
					uint64_t const high = exon.getTo();
					uint64_t const chrid = gene.chr_id;
					Vmax[chrid] = std::max(Vmax[chrid],high);
				}

				for ( uint64_t i = 0; i < gtfdata.Vchr.size(); ++i )
					if ( bamgtfmap.Vrmap[i] >= 0 )
					{
						std::size_t const ref_id_in_bam = bamgtfmap.Vrmap[i];
						int64_t const length_in_bam = bamheader.getRefIDLength(ref_id_in_bam);
						std::string const ref_name = bamheader.getRefIDName(ref_id_in_bam);
						int64_t const max_exon_end = Vmax[i];

						bool const ok = length_in_bam >= max_exon_end;

						if ( ! ok )
						{
							libmaus2::exception::LibMausException lme;
							lme.getStream() << "libmaus2::gtf::RangeSets: for id=" << i << " ref_id_in_bam=" << ref_id_in_bam << " ref_name=" << ref_name << " we have an exon ending at " << max_exon_end << " but length in bam is only " << length_in_bam << std::endl;
							lme.finish();
							throw lme;
						}

						Vmax[i] = bamheader.getRefIDLength(bamgtfmap.Vrmap[i]);
					}

				for ( uint64_t i = 0; i < gtfdata.Vchr.size(); ++i )
				{
					{
						libmaus2::geometry::RangeSet<libmaus2::gtf::Exon>::unique_ptr_type RS(new libmaus2::geometry::RangeSet<libmaus2::gtf::Exon>(Vmax[i]));
						VRS_forward[i] = std::move(RS);
					}
					{
						libmaus2::geometry::RangeSet<libmaus2::gtf::Exon>::unique_ptr_type RS(new libmaus2::geometry::RangeSet<libmaus2::gtf::Exon>(Vmax[i]));
						VRS_reverse[i] = std::move(RS);
					}
					{
						libmaus2::geometry::RangeSet<libmaus2::gtf::Exon>::unique_ptr_type RS(new libmaus2::geometry::RangeSet<libmaus2::gtf::Exon>(Vmax[i]));
						VRS[i] = std::move(RS);
					}
				}

				for ( uint64_t i = 0; i < gtfdata.next_exon_id; ++i )
				{
					libmaus2::gtf::Exon const & exon = gtfdata.Aexon[i];
					libmaus2::gtf::Transcript const & transcript = gtfdata.Atranscript[exon.transcript_id];
					libmaus2::gtf::Gene const & gene = gtfdata.Agene[transcript.gene_id];
					uint64_t const chrid = gene.chr_id;

					if ( exon.strand )
						VRS_forward[chrid]->insert(exon);
					else
						VRS_reverse[chrid]->insert(exon);

					VRS[chrid]->insert(exon);
				}
			}

			RangeSets(libmaus2::gtf::GTFData const & gtfdata)
			: VRS_forward(gtfdata.Vchr.size()), VRS_reverse(gtfdata.Vchr.size()), VRS(gtfdata.Vchr.size()), Vmax(gtfdata.Vchr.size(),0)
			{
				// check all exon coordinates
				for ( uint64_t i = 0; i < gtfdata.next_exon_id; ++i )
				{
					libmaus2::gtf::Exon const & exon = gtfdata.Aexon[i];
					libmaus2::gtf::Transcript const & transcript = gtfdata.Atranscript[exon.transcript_id];
					libmaus2::gtf::Gene const & gene = gtfdata.Agene[transcript.gene_id];
					uint64_t const high = exon.getTo();
					uint64_t const chrid = gene.chr_id;
					Vmax[chrid] = std::max(Vmax[chrid],high);
				}

				for ( uint64_t i = 0; i < gtfdata.Vchr.size(); ++i )
				{
					{
						libmaus2::geometry::RangeSet<libmaus2::gtf::Exon>::unique_ptr_type RS(new libmaus2::geometry::RangeSet<libmaus2::gtf::Exon>(Vmax[i]));
						VRS_forward[i] = std::move(RS);
					}
					{
						libmaus2::geometry::RangeSet<libmaus2::gtf::Exon>::unique_ptr_type RS(new libmaus2::geometry::RangeSet<libmaus2::gtf::Exon>(Vmax[i]));
						VRS_reverse[i] = std::move(RS);
					}
					{
						libmaus2::geometry::RangeSet<libmaus2::gtf::Exon>::unique_ptr_type RS(new libmaus2::geometry::RangeSet<libmaus2::gtf::Exon>(Vmax[i]));
						VRS[i] = std::move(RS);
					}
				}

				for ( uint64_t i = 0; i < gtfdata.next_exon_id; ++i )
				{
					libmaus2::gtf::Exon const & exon = gtfdata.Aexon[i];
					libmaus2::gtf::Transcript const & transcript = gtfdata.Atranscript[exon.transcript_id];
					libmaus2::gtf::Gene const & gene = gtfdata.Agene[transcript.gene_id];
					uint64_t const chrid = gene.chr_id;

					if ( exon.strand )
						VRS_forward[chrid]->insert(exon);
					else
						VRS_reverse[chrid]->insert(exon);

					VRS[chrid]->insert(exon);
				}
			}
		};
	}
}
#endif
