/*
    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_VCF_VCFSORTENTRY_HPP)
#define LIBMAUS2_VCF_VCFSORTENTRY_HPP

#include <libmaus2/util/AutoArrayCharBaseAllocator.hpp>
#include <libmaus2/util/AutoArrayCharBaseTypeInfo.hpp>
#include <libmaus2/util/GrowingFreeList.hpp>
#include <libmaus2/util/NumberSerialisation.hpp>
#include <libmaus2/util/TabEntry.hpp>

namespace libmaus2
{
	namespace vcf
	{
		struct VCFSortEntry
		{
			public:
			static libmaus2::util::AutoArrayCharBaseAllocator AFLA;
			static libmaus2::util::GrowingFreeList<
				libmaus2::autoarray::AutoArray<char>,
				libmaus2::util::AutoArrayCharBaseAllocator,
				libmaus2::util::AutoArrayCharBaseTypeInfo
			> AFL;

			libmaus2::autoarray::AutoArray<char>::shared_ptr_type A;
			uint64_t n;

			uint64_t s_a;
			uint64_t s_b;

			uint64_t c_3_a;
			uint64_t c_3_e;
			uint64_t c_4_a;
			uint64_t c_4_e;

			private:

			public:
			VCFSortEntry() : A(AFL.get()), n(0), s_a(0), s_b(0), c_3_a(0), c_3_e(0), c_4_a(0), c_4_e(0) {}
			~VCFSortEntry()
			{
				AFL.put(A);
			}
			VCFSortEntry(VCFSortEntry const & O) : A(AFL.get()), n(0), s_a(0), s_b(0), c_3_a(0), c_3_e(0), c_4_a(0), c_4_e(0)
			{
				*this = O;
			}

			void swap(VCFSortEntry & O)
			{
				std::swap(A    ,O.A);
				std::swap(n    ,O.n);
				std::swap(s_a  ,O.s_a);
				std::swap(s_b  ,O.s_b);
				std::swap(c_3_a,O.c_3_a);
				std::swap(c_3_e,O.c_3_e);
				std::swap(c_4_a,O.c_4_a);
				std::swap(c_4_e,O.c_4_e);
			}

			std::ostream & print(std::ostream & out) const
			{
				out.write(A->begin(),n);
				out.put('\n');
				return out;
			}

			char const * parse(libmaus2::util::TabEntry<> & TE) const
			{
				TE.parse(A->begin(),A->begin(),A->begin()+n);
				return A->begin();
			}

			void set(char const * a, char const * e, uint64_t const r_s_a, uint64_t const r_s_b,
				uint64_t r_c_3_a,
				uint64_t r_c_3_e,
				uint64_t r_c_4_a,
				uint64_t r_c_4_e
			)
			{
				n = e-a;
				A->ensureSize(e-a);
				std::copy(a,e,A->begin());
				s_a = r_s_a;
				s_b = r_s_b;
				c_3_a = r_c_3_a;
				c_3_e = r_c_3_e;
				c_4_a = r_c_4_a;
				c_4_e = r_c_4_e;
			}

			VCFSortEntry & operator=(VCFSortEntry const & O)
			{
				if ( this != &O )
				{
					A->ensureSize(O.A->size());
					std::copy(
						O.A->begin(),
						O.A->begin() + O.n,
						A->begin()
					);

					n = O.n;
					s_a = O.s_a;
					s_b = O.s_b;
					c_3_a = O.c_3_a;
					c_3_e = O.c_3_e;
					c_4_a = O.c_4_a;
					c_4_e = O.c_4_e;
				}
				return *this;
			}

			std::ostream & serialise(std::ostream & out) const
			{
				libmaus2::util::NumberSerialisation::serialiseNumber(out,n);
				out.write(A->begin(),n);
				libmaus2::util::NumberSerialisation::serialiseNumber(out,s_a);
				libmaus2::util::NumberSerialisation::serialiseNumber(out,s_b);
				libmaus2::util::NumberSerialisation::serialiseNumber(out,c_3_a);
				libmaus2::util::NumberSerialisation::serialiseNumber(out,c_3_e);
				libmaus2::util::NumberSerialisation::serialiseNumber(out,c_4_a);
				libmaus2::util::NumberSerialisation::serialiseNumber(out,c_4_e);
				return out;
			}

			std::istream & deserialise(std::istream & in)
			{
				n = libmaus2::util::NumberSerialisation::deserialiseNumber(in);
				A->ensureSize(n);
				in.read(A->begin(),n);
				if ( !in || in.gcount() != static_cast<std::streamsize>(n) )
				{
					libmaus2::exception::LibMausException lme;
					lme.getStream() << "[E] VCFSortEntry::deserialise: failed to read data" << std::endl;
					lme.finish();
					throw lme;
				}
				s_a = libmaus2::util::NumberSerialisation::deserialiseNumber(in);
				s_b = libmaus2::util::NumberSerialisation::deserialiseNumber(in);
				c_3_a = libmaus2::util::NumberSerialisation::deserialiseNumber(in);
				c_3_e = libmaus2::util::NumberSerialisation::deserialiseNumber(in);
				c_4_a = libmaus2::util::NumberSerialisation::deserialiseNumber(in);
				c_4_e = libmaus2::util::NumberSerialisation::deserialiseNumber(in);
				return in;
			}

			bool operator<(VCFSortEntry const & O) const
			{
				if ( s_a != O.s_a )
				{
					return s_a < O.s_a;
				}
				else if ( s_b != O.s_b )
				{
					return s_b < O.s_b;
				}
				else
				{
					uint64_t const l_3 = c_3_e-c_3_a;
					uint64_t const O_l_3 = O.c_3_e-O.c_3_a;

					int const c3 =
						strncmp(
							A->begin()+c_3_a,
							O.A->begin()+O.c_3_a,
							std::min(l_3,O_l_3)
						);

					if ( c3 != 0 )
						return c3 < 0;
					else if ( l_3 != O_l_3 )
						return l_3 < O_l_3;

					uint64_t const l_4 = c_4_e-c_4_a;
					uint64_t const O_l_4 = O.c_4_e-O.c_4_a;

					int const c4 =
						strncmp(
							A->begin()+c_4_a,
							O.A->begin()+O.c_4_a,
							std::min(l_4,O_l_4)
						);

					if ( c4 != 0 )
						return c4 < 0;
					else if ( l_4 != O_l_4 )
						return l_4 < O_l_4;

					return false;
				}
			}
		};
	}
}
#endif
