/****
DIAMOND protein sequence aligner
Copyright (C) 2012-2026 Benjamin J. Buchfink

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/>.
****/
// SPDX-License-Identifier: GPL-3.0-or-later

#include <sstream>
#include "output_format.h"
#include "util/escape_sequences.h"
#include "util/sequence/sequence.h"
#include "basic/const.h"
#include "stats/score_matrix.h"

using std::endl;
using std::string;
using std::stringstream;

void XMLFormat::print_match(const HspContext& r, Output::Info& info)
{
	auto& out = info.out;
	if (r.hsp_num == 0) {
		if (r.hit_num > 0)
			out << "  </Hit_hsps>" << '\n' << "</Hit>" << '\n';

		out << "<Hit>" << '\n'
			<< "  <Hit_num>" << r.hit_num + 1 << "</Hit_num>" << '\n';
		string id, def;
		const string target_seqid = r.target_title;
		Util::Seq::get_title_def(target_seqid, id, def);
		if (config.xml_blord_format) {
			out << "  <Hit_id>gnl|BL_ORD_ID|" << r.subject_oid << "</Hit_id>" << '\n'
				<< "  <Hit_def>";
			OutputFormat::print_title(out, target_seqid.c_str(), true, true, " &gt;", &EscapeSequences::XML);
			out << "</Hit_def>" << '\n';
		}
		else {
			out << "  <Hit_id>";
			print_escaped(out, id, &EscapeSequences::XML);
			out << "</Hit_id>" << '\n'
				<< "  <Hit_def>";
			OutputFormat::print_title(out, def.c_str(), true, true, " &gt;", &EscapeSequences::XML);
			out << "</Hit_def>" << '\n';
		}
		out << "  <Hit_accession>";
		print_escaped(out, config.no_parse_seqids ? id : Util::Seq::get_accession(id, info.acc_stats), &EscapeSequences::XML);
		out << "</Hit_accession>" << '\n'
			<< "  <Hit_len>" << r.subject_len << "</Hit_len>" << '\n'
			<< "  <Hit_hsps>" << '\n';
	}

	out << "    <Hsp>" << '\n'
		<< "      <Hsp_num>" << r.hsp_num + 1 << "</Hsp_num>" << '\n'
		<< "      <Hsp_bit-score>" << r.bit_score() << "</Hsp_bit-score>" << '\n'
		<< "      <Hsp_score>" << r.score() << "</Hsp_score>" << '\n'
		<< "      <Hsp_evalue>";
	out.print_e(r.evalue());
	out << "</Hsp_evalue>" << '\n'
		<< "      <Hsp_query-from>" << r.query_source_range().begin_ + 1 << "</Hsp_query-from>" << '\n'
		<< "      <Hsp_query-to>" << r.query_source_range().end_ << "</Hsp_query-to>" << '\n'
		<< "      <Hsp_hit-from>" << r.subject_range().begin_ + 1 << "</Hsp_hit-from>" << '\n'
		<< "      <Hsp_hit-to>" << r.subject_range().end_ << "</Hsp_hit-to>" << '\n'
		<< "      <Hsp_query-frame>" << r.blast_query_frame() << "</Hsp_query-frame>" << '\n'
		<< "      <Hsp_hit-frame>0</Hsp_hit-frame>" << '\n'
		<< "      <Hsp_identity>" << r.identities() << "</Hsp_identity>" << '\n'
		<< "      <Hsp_positive>" << r.positives() << "</Hsp_positive>" << '\n'
		<< "      <Hsp_gaps>" << r.gaps() << "</Hsp_gaps>" << '\n'
		<< "      <Hsp_align-len>" << r.length() << "</Hsp_align-len>" << '\n'
		<< "         <Hsp_qseq>";

	for (HspContext::Iterator i = r.begin(); i.good(); ++i)
		out << i.query_char();

	out << "</Hsp_qseq>" << '\n'
		<< "         <Hsp_hseq>";

	for (HspContext::Iterator i = r.begin(); i.good(); ++i)
		out << i.subject_char();

	out << "</Hsp_hseq>" << '\n'
		<< "      <Hsp_midline>";

	for (HspContext::Iterator i = r.begin(); i.good(); ++i)
		out << i.midline_char(score_matrix(i.query(), i.subject()));

	out << "</Hsp_midline>" << '\n'
		<< "    </Hsp>" << '\n';
}

void XMLFormat::print_header(File& f, int mode, const char* matrix, int gap_open, int gap_extend, double evalue, const char* first_query_name, unsigned first_query_len) const
{
	stringstream ss;
	ss << "<?xml version=\"1.0\"?>" << endl
		<< "<!DOCTYPE BlastOutput PUBLIC \"-//NCBI//NCBI BlastOutput/EN\" \"http://www.ncbi.nlm.nih.gov/dtd/NCBI_BlastOutput.dtd\">" << endl
		<< "<BlastOutput>" << endl
		<< "  <BlastOutput_program>" << AlignMode(mode).to_string() << "</BlastOutput_program>" << endl
		<< "  <BlastOutput_version>" << Const::program_name << ' ' << Const::version_string << "</BlastOutput_version>" << endl
		<< "  <BlastOutput_reference>Benjamin Buchfink, Xie Chao, and Daniel Huson (2015), &quot;Fast and sensitive protein alignment using DIAMOND&quot;, Nature Methods 12:59-60.</BlastOutput_reference>" << endl
		<< "  <BlastOutput_db>" << config.database << "</BlastOutput_db>" << endl
		<< "  <BlastOutput_query-ID>Query_1</BlastOutput_query-ID>" << endl
		<< "  <BlastOutput_query-def>";
	const string fqn = string(first_query_name);
	string escaped;
	EscapeSequences::XML.escape(fqn, escaped);
	ss << escaped.substr(0, fqn.find('\1'));
	ss << "</BlastOutput_query-def>" << endl << "  <BlastOutput_query-len>" << first_query_len << "</BlastOutput_query-len>" << endl
		<< "  <BlastOutput_param>" << endl
		<< "    <Parameters>" << endl
		<< "      <Parameters_matrix>" << matrix << "</Parameters_matrix>" << endl
		<< "      <Parameters_expect>" << evalue << "</Parameters_expect>" << endl
		<< "      <Parameters_gap-open>" << gap_open << "</Parameters_gap-open>" << endl
		<< "      <Parameters_gap-extend>" << gap_extend << "</Parameters_gap-extend>" << endl
		<< "      <Parameters_filter>F</Parameters_filter>" << endl
		<< "    </Parameters>" << endl
		<< "  </BlastOutput_param>" << endl
		<< "<BlastOutput_iterations>" << endl;
	f.write(ss.str().c_str(), ss.str().length());
}

void XMLFormat::print_query_intro(Output::Info& info) const
{
	info.out << "<Iteration>" << '\n'
		<< "  <Iteration_iter-num>" << info.query.oid + 1 << "</Iteration_iter-num>" << '\n'
		<< "  <Iteration_query-ID>Query_" << info.query.oid + 1 << "</Iteration_query-ID>" << '\n'
		<< "  <Iteration_query-def>";
	print_title(info.out, info.query.title, true, false, "", &EscapeSequences::XML);
	info.out << "</Iteration_query-def>" << '\n'
		<< "  <Iteration_query-len>" << info.query.len << "</Iteration_query-len>" << '\n'
		<< "<Iteration_hits>" << '\n';
}

void XMLFormat::print_query_epilog(Output::Info& info) const
{
	if (!info.unaligned) {
		info.out << "  </Hit_hsps>" << '\n'
			<< "</Hit>" << '\n';
	}
	info.out << "</Iteration_hits>" << '\n'
		<< "  <Iteration_stat>" << '\n'
		<< "    <Statistics>" << '\n';
	if (info.db_seqs.has_value())
		info.out << "      <Statistics_db-num>" << info.db_seqs.value() << "</Statistics_db-num>" << '\n';
	if (info.db_letters.has_value())
		info.out << "      <Statistics_db-len>" << info.db_letters.value() << "</Statistics_db-len>" << '\n';
	info.out << "      <Statistics_hsp-len>0</Statistics_hsp-len>" << '\n'
		<< "      <Statistics_eff-space>0</Statistics_eff-space>" << '\n'
		<< "      <Statistics_kappa>";
	info.out.print_d(score_matrix.k());
	info.out << "</Statistics_kappa>" << '\n';
	info.out << "      <Statistics_lambda>";
	info.out.print_d(score_matrix.lambda());
	info.out << "</Statistics_lambda>" << '\n'
		<< "      <Statistics_entropy>0</Statistics_entropy>" << '\n'
		<< "    </Statistics>" << '\n'
		<< "  </Iteration_stat>" << '\n'
		<< "</Iteration>" << '\n';
}

void XMLFormat::print_footer(File& f) const
{
	std::stringstream ss;
	ss << "</BlastOutput_iterations>" << endl
		<< "</BlastOutput>";
	f.write(ss.str().c_str(), ss.str().length());
}