// NeLNS - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#ifndef NL_LOG_REPORT_H
#define NL_LOG_REPORT_H

#include "nel/misc/types_nl.h"
#include "nel/misc/log.h"
#include "nel/misc/thread.h"
#include <string>
#include <vector>
#include <map>


class CLogReport;

/*
 *
 */
class CMakeLogTask : public NLMISC::IRunnable
{
public:

	/// Constructor
	CMakeLogTask() : _Stopping(false), _Complete(false), _Thread(NULL), _OutputLogReport(NULL) {}

	/// Destructor
	~CMakeLogTask();

	/// Start (called in main thread)
	void	start();

	/// Ask for stop and wait until terminated (called in main thread)
	void	terminateTask();

	///
	bool	isRunning() const { return (_Thread != NULL) && (!_Complete); }

	///
	bool	isStopping() const { return _Stopping; }

	///
	bool	isComplete() const { return _Complete; }

	virtual void run();

	/// Set the path of logfile directory. 
	void setLogPath(const std::string & logPath);

	/// Set one or more paths of logfile directory. They will be processed when running the background thread.
	void setLogPaths(const std::vector<std::string>& logPaths);

	/// Set the path of logfile directory to default value
	void setLogPathToDefault();

	/// Return the log paths
	const std::vector<std::string> getLogPaths() const { return _LogPaths; }

	/** Set which files are to be browsed:
	 * v       --> log*.log (default if setLogTarget() not called or called with an empty string)
	 * v*      --> log*.log + v*_log*.log
	 * v<n>    --> v<n>_log*.log
	 * v<n>+   --> v<n>_log*.log + all matching greater <n> + log*.log
	 * v<n>-   --> v<n>_log*.log + all matching lower <n>
	 * *       --> *.log
	 */
	void setLogTarget(const std::string & logTarget) { _LogTarget = logTarget; }

	/// Return the log report (NULL if task not started yet)
	CLogReport*		getLogReport() { return _OutputLogReport; }

private:

	void	pleaseStop() { _Stopping = true; }
	void	clear();

	volatile bool	_Stopping;
	volatile bool	_Complete;
	std::string		_LogTarget;
	std::vector<std::string>	_LogPaths;

	NLMISC::IThread	*_Thread;

	CLogReport		*_OutputLogReport;
};


const uint NB_LINES_PER_PAGE = 100;


/**
 *
 */
class CLogLineInfo
{
public:
	
	CLogLineInfo() : NbOccurences(0) {}

	void				addAnOccurence( const std::vector<std::string>& lineTokens );

	uint				NbOccurences;

	std::string			SampleLogText;
};


/**
 *
 */
class ILogReport
{
public:

	virtual void storeLine( const std::vector<std::string>& lineTokens, bool mainCountOnly ) = 0;

	virtual void report( NLMISC::CLog *targetLog, bool detailed ) = 0;

	virtual uint getNbDistinctLines() const = 0;

	virtual uint getNbTotalLines( NLMISC::CLog::TLogType logType ) = 0;

	virtual ~ILogReport() {}
};


/**
 * For one service (service name, not service instance), store info about log lines
 */
class CLogReportLeaf : public ILogReport
{
public:

	/// Constructor
	CLogReportLeaf( const std::string& service ) : _Service(service), _TotalLines(0) {}

	std::string		service() { return _Service; }

	virtual uint	getNbDistinctLines() const { return (uint)_LogLineInfo.size(); }

	virtual uint	getNbTotalLines( NLMISC::CLog::TLogType logType );

	virtual void	storeLine( const std::vector<std::string>& lineTokens, bool mainCountOnly );

	virtual void	report( NLMISC::CLog *targetLog, bool detailed );

	uint			reportPart( uint beginIndex, uint maxNbLines, NLMISC::CLog *targetLog );

protected:

	typedef std::map< std::string, CLogLineInfo > CLogLineInfoMap;

	std::string						_Service;

	CLogLineInfoMap					_LogLineInfo;

	std::map< std::string, uint >	_Counts; // indexed by log type string
	
	uint							_TotalLines;
};


/**
 * Store info about log lines for several services
 */
class CLogReportNode : public ILogReport
{
public:

	/// Constructor
	CLogReportNode() {}

	/// Destructor
	~CLogReportNode() { reset(); }

	/// Clear all
	void	reset()
	{
		for ( std::vector<CLogReportLeaf*>::iterator it=_Children.begin(); it!=_Children.end(); ++it )
			delete (*it);
		_Children.clear();
	}

protected:

	virtual void				storeLine( const std::vector<std::string>& lineTokens, bool mainCountOnly=false );

	CLogReportLeaf*				getChild( const std::string& service )
	{
		for ( std::vector<CLogReportLeaf*>::iterator it=_Children.begin(); it!=_Children.end(); ++it )
		{
			if ( (*it)->service() == service )
				return (*it);
		}
		return NULL;
	}

	CLogReportLeaf*				addChild( const std::string& service )
	{
		CLogReportLeaf *child = new CLogReportLeaf( service );
		_Children.push_back( child );
		return child;
	}

	virtual void	report( NLMISC::CLog *targetLog, bool displayDetailsPerService );

	virtual uint	getNbDistinctLines() const
	{
		uint n = 0;
		for ( std::vector<CLogReportLeaf*>::const_iterator it=_Children.begin(); it!=_Children.end(); ++it )
			n += (*it)->getNbDistinctLines();
		return n;
	}

	virtual uint	getNbTotalLines( NLMISC::CLog::TLogType logType=NLMISC::CLog::LOG_UNKNOWN )
	{
		uint n = 0;
		for ( std::vector<CLogReportLeaf*>::const_iterator it=_Children.begin(); it!=_Children.end(); ++it )
			n += (*it)->getNbTotalLines( logType );
		return n;
	}

	void			reportPage( uint pageNum, NLMISC::CLog *targetLog );

private:

	std::vector<CLogReportLeaf*>	_Children;
};


/*
 * <Class description>
 * \author Olivier Cado
 * \author Nevrax France
 * \date 2004
 */
class CLogReport : public CLogReportNode
{
public:

	/// Constructor
	CLogReport() : _CurrentFile(~0), _TotalFiles(~0) {}

	/// Clear all
	void	reset() { CLogReportNode::reset(); }

	/**
	 * Add a log line to the report tree.
	 * \param onlyType Type of log to study. If LOG_UNKNOWN, study all.
	 * \param countOtherTypes If true and onlyType not LOG_UNKNOWN, count the number of lines of each other type found.
	 */
	void	pushLine( const std::string& line, NLMISC::CLog::TLogType onlyType=NLMISC::CLog::LOG_WARNING, bool countOtherTypes=true );

	/// Set the current progress
	void	setProgress( uint currentFile, uint totalFiles ) { _CurrentFile = currentFile; _TotalFiles = totalFiles; }

	/// Get the current progress
	void	getProgress( uint& currentFile, uint& totalFiles ) { currentFile = _CurrentFile; totalFiles = _TotalFiles; }

	/// Get results for a service
	void	reportByService( const std::string& service, NLMISC::CLog *targetLog );

	/// Get partial results (pageNum>=1)
	void	reportPage( uint pageNum, NLMISC::CLog *targetLog ) { CLogReportNode::reportPage( pageNum, targetLog ); }

	/// Get summary of results
	virtual void report( NLMISC::CLog *targetLog, bool displayDetailsPerService=false ) { CLogReportNode::report( targetLog, displayDetailsPerService ); }

private:

	uint	_CurrentFile;
	uint	_TotalFiles;
};


#endif // NL_LOG_REPORT_H

/* End of log_report.h */