/*
    libmaus2
    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/>.
*/
#if ! defined(LIBMAUS2_UTIL_OCTETSTRING_HPP)
#define LIBMAUS2_UTIL_OCTETSTRING_HPP

#include <libmaus2/util/Histogram.hpp>
#include <libmaus2/suffixsort/divsufsort.hpp>
#include <libmaus2/util/StringAllocTypes.hpp>

namespace libmaus2
{
	namespace util
	{
		struct OctetStringBase
		{
			typedef OctetStringBase this_type;
			typedef std::unique_ptr<this_type> unique_ptr_type;
			typedef std::shared_ptr<this_type> shared_ptr_type;
			typedef uint8_t const * const_iterator;

			typedef ::libmaus2::autoarray::AutoArray<uint8_t,libmaus2::autoarray::alloc_type_cxx> A_type;
			A_type A;
			int const verbose;

			const_iterator begin() const
			{
				return A.begin();
			}

			const_iterator end() const
			{
				return A.end();
			}

			template<typename stream_type>
			static uint64_t computeOctetLengthFromFile(std::string const & fn, uint64_t const len)
			{
				stream_type stream(fn);
				return computeOctetLength(stream,len);
			}

			static uint64_t computeOctetLength(std::istream &, uint64_t const len);

			static shared_ptr_type constructRaw(
				std::string const & filename,
				uint64_t const offset = 0,
				uint64_t const blength = std::numeric_limits<uint64_t>::max()
			);

			OctetStringBase(
				std::string const & filename,
				uint64_t offset = 0,
				uint64_t blength = std::numeric_limits<uint64_t>::max(),
				int const verbose = 0
			);
			OctetStringBase(std::istream & CIS, uint64_t blength, int const verbose = 0);
			OctetStringBase(std::istream & CIS, uint64_t const octetlength, uint64_t const symlength, int const verbose = 0);

			uint64_t size() const
			{
				return A.size();
			}

			uint8_t operator[](uint64_t const i) const
			{
				return A[i];
			}
			uint8_t get(uint64_t const i) const
			{
				return (*this)[i];
			}

			::libmaus2::util::Histogram::unique_ptr_type getHistogram() const;
			std::map<int64_t,uint64_t> getHistogramAsMap() const;
		};

		template<unsigned int>
		struct OctectStringTypeBase
		{};

		template<>
		struct OctectStringTypeBase<32>
		{
			typedef int32_t wordtype;
		};

		template<>
		struct OctectStringTypeBase<64>
		{
			typedef int64_t wordtype;
		};

		template<unsigned int _wordsize>
		struct OctetString : public OctetStringBase
		{
			static unsigned int const wordsize = _wordsize;
			typedef typename OctectStringTypeBase<wordsize>::wordtype wordtype;
			typedef OctetString<wordsize> this_type;
			typedef std::unique_ptr<this_type> unique_ptr_type;
			typedef std::shared_ptr<this_type> shared_ptr_type;

			typedef ::libmaus2::suffixsort::DivSufSort<wordsize,uint8_t *,uint8_t const *,wordtype *,wordtype const *,256,true> sort_type_parallel;
			typedef ::libmaus2::suffixsort::DivSufSort<wordsize,uint8_t *,uint8_t const *,wordtype *,wordtype const *,256,false> sort_type_serial;
			typedef typename sort_type_serial::saidx_t saidx_t;

			OctetString(
				std::string const & filename,
				uint64_t offset = 0,
				uint64_t blength = std::numeric_limits<uint64_t>::max(),
				int const verbose = 0
			) : OctetStringBase(filename,offset,blength,verbose) {}
			OctetString(std::istream & CIS, uint64_t blength, int const verbose = 0) : OctetStringBase(CIS,blength,verbose) {}
			OctetString(std::istream & CIS, uint64_t const octetlength, uint64_t const symlength, int const verbose = 0)
			: OctetStringBase(CIS,octetlength,symlength,verbose) {}

			static shared_ptr_type constructRaw(
				std::string const & filename,
				uint64_t const offset = 0,
				uint64_t const blength = std::numeric_limits<uint64_t>::max()
			);


			::libmaus2::autoarray::AutoArray<saidx_t,static_cast<libmaus2::autoarray::alloc_type>(libmaus2::util::StringAllocTypes::sa_atype)>
				computeSuffixArray(bool const parallel = false) const;
		};

		extern template struct OctetString<32>;
		extern template struct OctetString<64>;
	}
}
#endif
