/*
    libmaus2
    Copyright (C) 2021 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_PARALLEL_THREADPOOL_BAM_BAMHEADERSEQDATA_HPP)
#define LIBMAUS2_PARALLEL_THREADPOOL_BAM_BAMHEADERSEQDATA_HPP

#include <cassert>
#include <regex>
#include <filesystem>
#include <libmaus2/exception/LibMausException.hpp>
#include <libmaus2/aio/InputStreamInstance.hpp>

namespace libmaus2
{
	namespace parallel
	{
		namespace threadpool
		{
			namespace bam
			{
				struct BamHeaderSeqData
				{
					std::map < std::string, std::map<std::string,std::string> > M;
					std::map < std::string, std::size_t> MO;
					std::vector < std::string > NV;

					void init(std::istream & ISI)
					{
						std::string line;

						std::regex reg("^@SQ");
						std::regex tab("\t");

						std::size_t id = 0;

						while ( std::getline(ISI,line) )
							if ( std::regex_search(line,reg) )
							{
								std::vector<std::string> V(
									std::regex_token_iterator<std::string::const_iterator>(line.begin(), line.end(), tab, -1),
									std::regex_token_iterator<std::string::const_iterator>()
								);

								std::map<std::string,std::string> LM;

								assert ( V.size() && V[0] == "@SQ" );

								for ( std::size_t i = 1; i < V.size(); ++i )
								{
									std::string const skv = V[i];

									if ( skv.size() >= 3 || skv[2] == ':' )
									{
										std::string const key = skv.substr(0,2);
										std::string const val = skv.substr(3);

										// std::cerr << key << "\t" << val << std::endl;

										LM[key] = val;
									}
								}

								auto const it = LM.find("SN");

								if ( it != LM.end() )
								{
									std::string const name = it->second;

									M[name] = LM;

									if ( MO.find(name) == MO.end() )
									{
										MO[name] = id++;
										NV.push_back(name);
									}
									else
									{
										libmaus2::exception::LibMausException lme;
										lme.getStream() << "[E] sequence name " << name << " is not unique" << std::endl;
										lme.finish();
										throw lme;
									}
								}
							}
					}


					bool sequencesMissing(std::filesystem::path const & ref_cache_path) const
					{
						bool needfascan = false;

						for ( auto P : M )
						{
							auto const id = P.first;
							auto const LM = P.second;

							auto const it_M5 = LM.find("M5");

							if ( it_M5 == LM.end() )
							{
								libmaus2::exception::LibMausException lme;
								lme.getStream() << "[E] no M5 attribute found for " << id << std::endl;
								lme.finish();
								throw lme;
							}

							std::string const m5 = it_M5->second;

							std::filesystem::path dictpath = ref_cache_path;
							dictpath /= m5;

							if ( ! std::filesystem::exists(dictpath) )
								needfascan = true;
						}

						return needfascan;
					}

					BamHeaderSeqData(std::istream & ISI)
					{
						init(ISI);
					}
					BamHeaderSeqData(std::string const & fn)
					{
						libmaus2::aio::InputStreamInstance ISI(fn);
						init(ISI);
					}
				};
			}
		}
	}
}
#endif
