/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
***************************************************************************/

#pragma once

#include "glmCoreAPI.h"
#include "glmTypes.h"
#include "glmAssert.h"

#include "glmString.h"
#include "glmPODArray.h"

namespace glm
{
	/**
	* \brief PerfLogger permits to store Time results in a .csv file and compare the average value with the average value of the given file
	* \note This class only defines static methods
	* Sample on how to use this class to log performances:
	*	PerfLogger myLogger("perf.csv", "dt=0.1;param1=12", 0.1, true, false);	//instanciate one PerfLogger for test test with the file to log in, a description of the context for the test (e.g param1=2; dt=0.1; ...) and the configuration
	*	for(unsigned int i=0; i<10; ++i)
	*	{
	*		Time testDuration = mytest.run();	//run the test as many times as you needed
	*		myLogger.addResult(testDuration);			//add the results in the logger
	*	}
	*	mytest.testAndFlushResults();	//flush the results in the log file. Given the configuration, this method will asserts if the average of the given values is upper than the average in the file + 10%(the given margin when instanciating the perfLogger)
	*
	* \ingroup time
	* \author Golaem <dev@golaem.com>
	*/
	class GLM_CORE_API PerfLogger
	{
	public:
		
		GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared);

		PerfLogger(const glm::GlmString& filePath="perfResults.csv", const glm::GlmString& resultDescription="", double perfAuthorizedMargin=0.1, bool assertIfLowerThanMean=true, bool writeResultsOnlyIfWithinAthorizedMargin=false);

		//************************************************************
		//! @name Configuration
		//************************************************************
		//@{
		glm::GlmString& filePath()
		{
			return _filePath;
		}

		glm::GlmString& resultDescription()
		{
			return _resultDescription;
		};


		/** Get the authorized margin when comparing the results average with the file average
		*
		* \return a reference on the current authorized margin
		*/
		double& perfAuthorizedMargin()
		{
			return _perfAuthorizedMargin;
		}

		/** Get the current configuration for testing averages values
		*
		* \return a reference on the current configuration
		*/
		bool& assertIfValueLowerThanMean()
		{
			return _assertIfLowerThanMean;
		}

		/** Get the current configuration for testing averages values
		*
		* \return a reference on the current configuration
		*/
		bool& writeResultsOnlyIfWithinAthorizedMargin()
		{
			return _writeResultsOnlyIfWithinAthorizedMargin;
		}

		/** Get the current excel function which will be used in the second column of the output .csv file
		*
		* \return a reference on the current string of formula
		* \note to get the first column with a correct value in the formula, use the FIRST_VALUED_COLUMN token: it will be replaced in the formula by the real column number
		* \note to get the first column with a correct value in the formula, use the LAST_VALUED_COLUMN token: it will be replaced in the formula by the real column number
		* \note the default value of the formula is: =AVERAGE(FIRST_VALUED_COLUMN:LAST_VALUED_COLUMN)
		*/
		glm::GlmString& excelFunction()
		{
			return _excelFunction;
		}
		//@}



		//************************************************************
		//! @name Adding results
		//************************************************************
		//@{
		/**
		*
		* \param result 
		* \return a reference on the perflogger to allow chained calls (e.g	logger.addResult(1).addResult(2).addResult(3).flushResultsInFile();	)
		*/
		PerfLogger& addResult(Time result);

		/**
		*
		* \param results 
		* \return a reference on the perflogger to allow chained calls
		*/
		PerfLogger& addResult(const glm::PODArray<Time>& results);
		//@}

		//************************************************************
		//! @name Results comparison and save methods
		//************************************************************
		//@{
		//////////////////////
		/** Compare average value of the given results with the average value of results in the given file, and store the new results at the end of the file
		*
		* \return false if there are no results to flush
		*/
		bool testAndFlushResults();

		//////////////////////
		/**
		*
		* \return false if there are no results to flush
		*/
		bool flushResultsInFile();

		//////////////////////
		/**
		*
		* \param[out] meanValue 
		* \param[in] filterWithComputerName if true, the mean value will be computed only with results from the same computer
		* \return false if the mean value couldn't be retrieved
		*/
		bool getMeanValueFromFile(Time* meanValue, const bool filterWithComputerName=true) const;

		//////////////////////
		/**
		*
		* \param[out] meanValue 
		* \return false if the mean value couldn't be retrieved
		*/
		bool getMeanValueFromResults(Time* meanValue) const;
		//@}

	protected:
		//************************************************************
		//! @name Helpers
		//************************************************************
		//@{
		/** Get the column name in excel style (column 1=A, column 26=Z, column 27=AA, and so on)
		*
		* \param columnNumber 
		* \param zeroIndexedArray if true, the index 0 will correspond to column name A. If false, then it's 1 wich will be equivalent to A.
		* \return the column name
		*/
		glm::GlmString getColumnName(int columnNumber, bool zeroIndexedArray=false);
		//@}

		//************************************************************
		//! @name Datas
		//************************************************************
		//@{
		glm::GlmString _filePath;
		double _perfAuthorizedMargin;
		bool _assertIfLowerThanMean;
		bool _writeResultsOnlyIfWithinAthorizedMargin;

		glm::GlmString _resultDescription;
		glm::PODArray<Time> _results;
		glm::GlmString	_excelFunction;
		//@}
	};
}

