/*
    libmaus2
    Copyright (C) 2020 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_PARALLEL_THREADPOOL_BGZF_BGZFHEADERDECODER_HPP)
#define LIBMAUS2_PARALLEL_THREADPOOL_BGZF_BGZFHEADERDECODER_HPP

#include <libmaus2/parallel/threadpool/bgzf/BgzfHeaderDecoderBase.hpp>
#include <libmaus2/exception/LibMausException.hpp>

namespace libmaus2
{
	namespace parallel
	{
		namespace threadpool
		{
			namespace bgzf
			{
				struct BgzfHeaderDecoder : public BgzfHeaderDecoderBase
				{
					static bool checkId(char const * p, std::size_t const offset)
					{
						return get8(p,offset) == 31 && get8(p,offset+1) == 139;
					}

					static bool checkCM(char const * p, std::size_t const offset)
					{
						return get8(p,offset+2) == 8;
					}

					static bool checkFlag(char const * p, std::size_t const offset)
					{
						return get8(p,offset+3) == 4;
					}

					static bool checkFront(char const * p, std::size_t const offset)
					{
						return
							checkId(p,offset)
							&&
							checkCM(p,offset)
							&&
							checkFlag(p,offset);
					}

					static uint32_t getMTIME(char const * p, std::size_t const offset)
					{
						return get32(p,offset+4);
					}

					static uint32_t getXFL(char const * p, std::size_t const offset)
					{
						return get8(p,offset+8);
					}

					static uint32_t getOS(char const * p, std::size_t const offset)
					{
						return get8(p,offset+9);
					}

					static uint32_t getXLEN(char const * p, std::size_t const offset)
					{
						return get16(p,offset+10);
					}

					static void printFront(char const * p, std::size_t const offset, std::ostream & out)
					{
						out << "[";
						for ( uint64_t i = 0; i < getCheckLength(); ++i )
							out << static_cast<int>(get8(p,offset+i)) << ";";
						out << "]";
					}

					static uint32_t getCheckLength()
					{
						return 12;
					}

					static uint32_t getHeaderLength(char const * p, std::size_t const offset)
					{
						return 12 + getXLEN(p,offset);
					}

					static uint32_t getBlockSize(char const * p, std::size_t const offset)
					{
						std::size_t xlen = getXLEN(p,offset);

						p += offset;
						p += 12;

						while ( xlen )
						{
							if ( xlen <= 4 )
							{
								throw libmaus2::exception::LibMausException("BgzfHeaderDecoder::getBlockSize: malformed gzip header");
							}
							else
							{
								uint8_t const SI1 = get8(p,0);
								uint8_t const SI2 = get8(p,1);
								std::size_t const slen = get16(p,2);

								xlen -= 4;
								p += 4;

								if ( slen > xlen )
								{
									throw libmaus2::exception::LibMausException("BgzfHeaderDecoder::getBlockSize: malformed gzip header");
								}

								if ( SI1 == 66 && SI2 == 67 )
								{
									if ( slen == 2 )
									{
										return get16(p,0)+1;
									}
									else
									{
										throw libmaus2::exception::LibMausException("BgzfHeaderDecoder::getBlockSize: malformed gzip header");
									}
								}
								else
								{
									p += slen;
									xlen -= slen;
								}
							}
						}

						throw libmaus2::exception::LibMausException("BgzfHeaderDecoder::getBlockSize: malformed gzip header");
					}
				};
			}
		}
	}
}
#endif
