/*
    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_INTERVALLISTSET_HPP)
#define LIBMAUS2_GTF_INTERVALLISTSET_HPP

#include <libmaus2/gtf/GTFData.hpp>
#include <libmaus2/gtf/IntervalList.hpp>

namespace libmaus2
{
	namespace gtf
	{
		struct IntervalListSet
		{
			typedef IntervalListSet this_type;
			typedef std::unique_ptr<this_type> unique_ptr_type;
			typedef std::shared_ptr<this_type> shared_ptr_type;

			// exon array
			typedef libmaus2::autoarray::AutoArray<libmaus2::gtf::Exon> ap64;
			// array of exon arrays
			libmaus2::autoarray::AutoArray<ap64::unique_ptr_type> AP64;
			libmaus2::autoarray::AutoArray<ap64::unique_ptr_type> AP64_forward;
			libmaus2::autoarray::AutoArray<ap64::unique_ptr_type> AP64_reverse;
			// size of exon arrays
			libmaus2::autoarray::AutoArray<uint64_t> OP64;
			libmaus2::autoarray::AutoArray<uint64_t> OP64_forward;
			libmaus2::autoarray::AutoArray<uint64_t> OP64_reverse;
			// array of interval lists
			libmaus2::autoarray::AutoArray<libmaus2::gtf::IntervalList::unique_ptr_type> AIL;
			libmaus2::autoarray::AutoArray<libmaus2::gtf::IntervalList::unique_ptr_type> AIL_forward;
			libmaus2::autoarray::AutoArray<libmaus2::gtf::IntervalList::unique_ptr_type> AIL_reverse;

			libmaus2::gtf::IntervalList const & operator[](uint64_t const i) const
			{
				return * ( AIL.at(i) );
			}

			libmaus2::gtf::Exon const & operator()(uint64_t const i, uint64_t const j) const
			{
				return (*(AP64[i]))[j];
			}

			IntervalListSet(libmaus2::gtf::GTFData const & gtfdata)
			: AP64(gtfdata.Vchr.size()), AP64_forward(gtfdata.Vchr.size()), AP64_reverse(gtfdata.Vchr.size()),
			  OP64(gtfdata.Vchr.size()), OP64_forward(gtfdata.Vchr.size()), OP64_reverse(gtfdata.Vchr.size()),
			  AIL(gtfdata.Vchr.size()),  AIL_forward(gtfdata.Vchr.size()),  AIL_reverse(gtfdata.Vchr.size())
			{
				for ( uint64_t i = 0; i < gtfdata.Vchr.size(); ++i )
				{
					{
						ap64::unique_ptr_type tptr(new ap64);
						AP64[i] = std::move(tptr);
						OP64[i] = 0;
					}
					{
						ap64::unique_ptr_type tptr(new ap64);
						AP64_forward[i] = std::move(tptr);
						OP64_forward[i] = 0;
					}
					{
						ap64::unique_ptr_type tptr(new ap64);
						AP64_reverse[i] = std::move(tptr);
						OP64_reverse[i] = 0;
					}
				}
				// bucket sort exons into arrays
				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;

					AP64[chrid]->push(OP64[chrid],exon);
					if ( exon.strand )
						AP64_forward[chrid]->push(OP64_forward[chrid],exon);
					else
						AP64_reverse[chrid]->push(OP64_reverse[chrid],exon);
				}
				// initialize interval lists
				for ( uint64_t i = 0; i < gtfdata.Vchr.size(); ++i )
				{
					std::sort(AP64[i]->begin(),AP64[i]->begin() + OP64[i]);
					libmaus2::gtf::IntervalList::unique_ptr_type pIL(libmaus2::gtf::IntervalList::construct(AP64[i]->begin(),OP64[i],i));
					AIL[i] = std::move(pIL);

					// AIL[i]->printUnique(std::cerr);
				}
			}

			std::pair<uint64_t,uint64_t> query(std::pair<uint64_t,uint64_t> const & Q, libmaus2::gtf::IntervalList::QueryContext & context, uint64_t const chr) const
			{
				return (*this)[chr].query(Q,context,AP64[chr]->begin(),chr);
			}
		};
	}
}
#endif
