/*
    Copyright (C) 2012 Bartosz Broda, Jacek Bochenek, Łukasz Biłeńkij, 
    Damian Jaworski, Wojciech Mazur, Dominik Piasecki, Maciej Piasecki, 
    Radosław Ramocki
    
    This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser 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 LICENSE and COPYING files for more details.
*/

#include "wbst_headers.h"
#include "singletest.h"
#include "options.h"

using namespace std;



SingleTest::SingleTest( Matrices::SuperMatrixPtr pMatrix, std::vector<WBSTQuestion> questions,
						smartcomparator::compare::CompareFunctionPtr pCmp,
						const std::string &flex_class,string logfile): m_pMatrix(pMatrix),
m_vQuestions(questions),
m_sLogFile(logfile),
m_pCmp(pCmp),
m_sFlexClass(flex_class)
{
}

void SingleTest::runTest()
{
	try
	{
		m_vGoodIndices.clear();

		m_mBadToChoosen.clear();
		m_vSkippedIndices.clear();

		for(size_t q = 0; q < m_vQuestions.size(); q++)
		{
			SingleQuestion( q );

		}// for each question
	}
	catch(std::runtime_error &e)
	{
		std::cerr << "ERROR: " << e.what() << std::endl;
	}
}

void SingleTest::WriteResults()
{
	std::ofstream out(m_sLogFile.c_str() );
	SingleTestResult result = GetAggregatedResults();

	if(!out)
	{
		std::cerr << result;
		throw std::runtime_error("Cannot open file for writing wbst result: " + m_sLogFile);
	}

	out << "<wbst tester_version="<< VERSION <<">\n";
	out << "\t<aggregated_results>\n";
	out << "\t\t<correct>" << m_vGoodIndices.size() << "</correct>\n";
	out << "\t\t<incorrect>" << m_mBadToChoosen.size() << "</incorrect>\n";
	out << "\t\t<skipped>" << m_vSkippedIndices.size() << "</skipped>\n";
	out << "\t\t<accuracy>" << double(m_vGoodIndices.size()) / double(m_vGoodIndices.size()+m_mBadToChoosen.size() ) << "</accuracy>\n";
	out << "\t</aggregated_results>\n";

	out << "\t<skipped_questions>\n";
	for(std::set<size_t>::iterator iter = m_vSkippedIndices.begin(); iter != m_vSkippedIndices.end(); iter++)
		out << "\t\t<skipped_question_line>" << m_vQuestions[*iter].ToString() << "</skipped_question_line>\n";
	out << "\t</skipped_questions>\n";

	out << "\t<wrong_anwsers>\n";
	for(std::map<size_t, std::string>::iterator iter = m_mBadToChoosen.begin(); iter != m_mBadToChoosen.end(); iter++)
		out << "\t\t<wrong_answer choosen=\""<< iter->second <<"\">" << m_vQuestions[iter->first].ToString() << "</wrong_answer>\n";
	out << "\t</wrong_anwsers>\n";

	out << "\t<correct_answered>\n";
	for(std::set<size_t>::iterator iter = m_vGoodIndices.begin(); iter != m_vGoodIndices.end(); iter++)
		out << "\t\t<answer>" << m_vQuestions[*iter].ToString() << "</answer>\n";
	out << "\t</correct_answered>\n";

	out << "</wbst>\n";
	out.close();
}


void SingleTest::SingleQuestion(size_t q)
{
	WBSTQuestion &question = m_vQuestions[q];
	size_t size_rows = m_pMatrix->NumberOfRows();
	size_t q_row = m_pMatrix->GetRowIndex( m_sFlexClass + ":" + question.GetQuestion() );
	if(q_row > size_rows)
		q_row = m_pMatrix->GetRowIndex( question.GetQuestion() );
	if(q_row > size_rows)
	{
		m_vSkippedIndices.insert(q);
		return;
	}
	double max_sim = -100000;
	std::string best= "--blad--";
	const std::set<std::string> &decoys = question.GetDecoys();
	for(std::set<std::string>::const_iterator decoy_iter = decoys.begin();
	decoy_iter != decoys.end() ; decoy_iter++)
	{
		size_t d = m_pMatrix->GetRowIndex( m_sFlexClass + ":" +*decoy_iter );
		if(d > size_rows)
			d = m_pMatrix->GetRowIndex( *decoy_iter ); //if no flex class was given in a matrix

		if(d > size_rows)
		{
			m_vSkippedIndices.insert(q);
			return;
		}
		double sim = m_pCmp->Compare(q_row, d);
		if(sim > max_sim)
		{
			max_sim = sim;
			best = *decoy_iter;
		}

	}//for each question

	if(best == question.GetAnwser() )
		m_vGoodIndices.insert(q);
	else
		m_mBadToChoosen[q] = best;

}

SingleTestResult SingleTest::GetAggregatedResults()
{
	SingleTestResult results;
	results.skipped = m_vSkippedIndices.size();
	results.good = m_vGoodIndices.size();
	results.bad = m_mBadToChoosen.size();
	return results;
}

SingleTestResult::SingleTestResult()
{
	good = bad = skipped = 0;
}

std::ostream & operator << (std::ostream& out, const SingleTestResult &res)
{
	out << "Good: " << res.good << "\nBad: " << res.bad << "\nSkipped: " << res.skipped
			<< "\nAccuracy: " << double(res.good)/double(res.good+res.bad) << std::endl;
	return out;
}
