/**
 * @file data.cpp
 * 
 * @copyright Copyright (c) 2022 Joris Baranauskas
 * 
 * Distributed under the GNU GENERAL PUBLIC LICENSE, Version 3.
 * You should have received a copy of the license along with this project.
 * If not, see https://www.gnu.org/licenses/.
 */

#include <data.hpp>

#include <algorithm>

namespace vu
{
	Data::Data()
		: firstName(), lastName(), examGrade(), grades(), printMode() { }

	Data::Data(const String& firstName, const String& lastName, uint16 examGrade, const Vector<uint16>& grades, PrintMode printMode)
		: firstName(firstName), lastName(lastName), examGrade(examGrade), grades(grades), printMode(printMode) { }

	Data::Data(const Data& other)
		: firstName(other.firstName), lastName(other.lastName), examGrade(other.examGrade), grades(other.grades), printMode(other.printMode) { }

#if defined(MOVE_SEMANTICS_ENABLED) && MOVE_SEMANTICS_ENABLED == 1
	Data::Data(Data&& other)
		: firstName(other.firstName), lastName(other.lastName), examGrade(other.examGrade), grades(other.grades), printMode(other.printMode) { }
#endif

	Data::~Data() = default;

	float Data::getAverageGradeOfHomework() const
	{
		if (this->grades.size() <= 0)
			return 0.0f;

		float average = 0.0f;
		
		for (uint16 i = 0; i < this->grades.size(); i++)
			average += this->grades[i];

		return average / (float)this->grades.size();
	}

	float Data::getMedianGradeOfHomework() const
	{
		if (this->grades.size() <= 0)
			return 0.0f;

		::std::sort((uint16*)this->grades.data(), (uint16*)this->grades.data() + this->grades.size());

		if (this->grades.size() % 2 != 0)
			return (float)this->grades[this->grades.size() / 2];

		return (float)(this->grades[(this->grades.size() - 1) / 2] + this->grades[this->grades.size() / 2]) / 2.0f;
	}

	float Data::getFinalGrade(PrintMode printMode) const
	{
		float average = this->getAverageGradeOfHomework();
		float median = this->getMedianGradeOfHomework();
		
		if (printMode == Data::PrintMode::Average)
			return 0.4f * average + 0.6f * this->examGrade;
		else if (printMode == Data::PrintMode::Median)
			return 0.4f * median + 0.6f * this->examGrade;
		else
			return 0.0f;
	}

	Data::PrintMode Data::getPrintMode() const
	{
		return this->printMode;
	}

	void Data::setPrintMode(Data::PrintMode printMode)
	{
		this->printMode = printMode;
	}

	String Data::getFirstName()
	{
		return this->firstName;
	}

	const String& Data::getFirstName() const
	{
		return this->firstName;
	}

	String Data::getLastName()
	{
		return this->lastName;
	}

	const String& Data::getLastName() const
	{
		return this->lastName;
	}

	const Vector<uint16>& Data::getGrades() const
	{
		return this->grades;
	}

	Data& Data::operator =(const Data& other)
	{
		this->firstName = other.firstName;
		this->lastName = other.lastName;
		this->examGrade = other.examGrade;
		this->printMode = other.printMode;
		this->grades = other.grades;
		return *this;
	}

#if defined(MOVE_SEMANTICS_ENABLED) && MOVE_SEMANTICS_ENABLED == 1
	Data& Data::operator =(Data&& other)
	{
		if (this == &other)
			return *this;

		this->firstName = other.firstName;
		this->lastName = other.lastName;
		this->examGrade = other.examGrade;
		this->printMode = other.printMode;
		this->grades = other.grades;
		return *this;
	}
#endif

	Data Data::create(const String& firstName, const String& lastName, uint16 examGrade, const Vector<uint16>& grades, PrintMode printMode)
	{
		return Data(firstName, lastName, examGrade, grades, printMode);
	}
}
