/*******************************************************************************
*                                                                              *
* Société de Transports de Montréal.                                    	   *
* 2012 - 2013                                                                        *
*                                                                              *
* Projet Zones Tests                                                           *
*                                                                              *
*                                                                              *
*                                                                              *
*******************************************************************************/
/*
	Description:
	Cette classe est responsable de la création d'une liste d'objets CLogElement
	à partir des données de passage des trains contenues dans les fichiers log.
	Cette classe est principalement utilisée par l'interface graphique pour la
	visualisation des passages.
	
*/

/* ************************************************************************** */
/* Revision:
### 20121024 JFM

	
### YYYYMMDD Description du besoin ou du bug
	Description du changement.
 */

/* ************************************************************************** */

#include "LogMgr.h"

#include "TrainLogFileMgr.h"
#include "ZTLog.h"

CLogMgr::CLogMgr()
{
    mMaxFileCount = MAX_LOG_FILE_COUNT;
    mKeepMPM10Logs = false;
}

CLogMgr::~CLogMgr()
{
	DestroyLogList();
}

unsigned int CLogMgr::DestroyLogList()
{
	for(int i = 0; i < mPassagesList.size(); i++)
	{
        delete mPassagesList.at(i);
	}

    mPassagesList.clear();

	return RET_OK;
}

int CLogMgr::KeepAllMPM10Logs(bool keep)
{
    mKeepMPM10Logs = keep;
    return RET_OK;
}

int CLogMgr::KeepAllZT1Logs(bool keep)
{
    mKeepZT1Logs = keep;
    return RET_OK;
}

int CLogMgr::KeepAllZT2Logs(bool keep)
{
    mKeepZT2Logs = keep;
    return RET_OK;
}

int CLogMgr::SetMaxLogFilesCount(int count)
{
    mMaxFileCount = count;
    return RET_OK;
}

//Parse all the logs at once. This may freeze the program and the GUI depending
//on the number of log files.
//The KeepData parameter determines if the raw data should be kept in RAM (Not recommended).
//It is recommended to load it when needed via LoadLogData().
unsigned int CLogMgr::ParseLogs(bool KeepData)
{
	DestroyLogList();

//	QStringList LogFilters;
//	LogFilters << "*.bin";
//    QDir LogDir("./Trains/");

//	LogDir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
//	LogDir.setNameFilters(LogFilters);
//	LogDir.setSorting(QDir::Name);

    QFileInfoList list = GetLogsFilenameList();//LogDir.entryInfoList();

	if(list.size() == 0)
		return RET_OK;

    //Extract data for each passage
	for(int i = 0; i < list.size(); i++)
	{
		ParseNewLog(list.at(i).filePath(),KeepData,false);
	}

	return RET_OK;
}

QFileInfoList CLogMgr::GetLogsFilenameList()
{
    QStringList LogFilters;
    LogFilters << "*.bin";
    QDir LogDir("./Trains/");

    LogDir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
    LogDir.setNameFilters(LogFilters);
    LogDir.setSorting(QDir::Name);

    QFileInfoList list = LogDir.entryInfoList();

    return list;
}

////This function checks the number of log files on the disk and
////makes shure there are not too many.  The number of files can be
////limited by MaxNbLog for the max file count or MaxSize for the maximum
////number of bytes on the disk.
//unsigned int CLogMgr::CleanLogsOnDisk(qint32 MaxNbLogs, qint64 MaxSize)
//{
//	int RemovedFiles = 0;
//	QStringList LogFilters;
//	LogFilters << "*.bin";
//	QDir *LogDir = new QDir("./Trains/");

//	LogDir->setFilter(QDir::Files | QDir::NoDotAndDotDot);
//	LogDir->setNameFilters(LogFilters);
//	LogDir->setSorting(QDir::Name);

//	QFileInfoList list = LogDir->entryInfoList();

//	if(list.size() == 0)
//	{
//		delete LogDir;
//		return RET_OK;
//	}

//	//First, delete the older files if there are too many.
//	if(list.size() > MaxNbLogs)
//	{
//		for(qint32 i = 0; i < (list.size() - MaxNbLogs); i++)
//		{
//			if(LogDir->remove(list.at(i).absoluteFilePath()) == false)
//			{
//				CZTLog::instance()->AddLogString(QString().sprintf("Impossible d'effacer le log %s",list.at(i).absoluteFilePath().toUtf8().data()),true);
//			}
//			else
//			{
//				RemovedFiles++;
//			}
//		}

//		//refresh list
//		delete LogDir;
//		QDir *LogDir = new QDir("./Trains/");

//		LogDir->setFilter(QDir::Files | QDir::NoDotAndDotDot);
//		LogDir->setNameFilters(LogFilters);
//		LogDir->setSorting(QDir::Name);

//		list = LogDir->entryInfoList();
//	}

//	delete LogDir;

//	qint64 DirSize = 0;
//	for(qint32 i = 0; i < list.size(); i++)
//	{
//		DirSize += list.at(i).size();
//	}

//	if(DirSize > MaxSize)
//	{
//		int i = 0;
//		while(DirSize > MaxSize)
//		{
//			if(LogDir->remove(list.at(i).absoluteFilePath()) == true)
//			{
//				DirSize -= list.at(i).size();
//				RemovedFiles++;
//			}
//			else
//			{
//				CZTLog::instance()->AddLogString(QString().sprintf("Impossible d'effacer le log %s",list.at(i).absoluteFilePath().toUtf8().data()),true);
//			}
//			i++;
//		}
//	}

//	if(RemovedFiles > 0)
//	{
//		CZTLog::instance()->AddLogString(QString().sprintf("%d fichiers de passage on été effacés",RemovedFiles),true);
//	}

//	return RET_OK;
//}

//This function loads the raw data from a log element for which
//the data has not yet been loaded.  Data will be loaded directly in the
//passed element pointer.
//Returns a pointer to the new log Element
CLogElement* CLogMgr::LoadLogData(CLogElement *Element)
{
	if(Element->mZTLogType == ZT1_LOG_TYPE)
	{
		CZT1LogElement *TargetElement = (CZT1LogElement*)Element;
		QString FilePath = TargetElement->mLogFileName;
		unsigned int ret;

		//Make shure that the lists are empty in the target element
		for(int i = 0; i < TargetElement->mZTLogData.size(); i++)
		{
			delete TargetElement->mZTLogData.at(i);
		}
		for(int i = 0; i < TargetElement->mZTDetections.size(); i++)
		{
			delete TargetElement->mZTDetections.at(i);
		}

		TargetElement->mZTLogData.clear();
		TargetElement->mZTDetections.clear();

		//Passing the TargetElement to the OpenTrainLog function will fill it with data
		CTrainLogFileMgr::instance()->OpenTrainLog(FilePath,ret,TargetElement,true);

		return (CLogElement*)TargetElement;
	}
	else if(Element->mZTLogType == ZT2_LOG_TYPE)
	{
		CZT2LogElement *TargetElement = (CZT2LogElement*)Element;
		QString FilePath = TargetElement->mLogFileName;
		unsigned int ret;

		//Make shure that the lists are empty in the target element
		for(int i = 0; i < TargetElement->mZTLogData.size(); i++)
		{
			delete TargetElement->mZTLogData.at(i);
		}
		for(int i = 0; i < TargetElement->mZTDetections.size(); i++)
		{
			delete TargetElement->mZTDetections.at(i);
		}

		TargetElement->mZTLogData.clear();
		TargetElement->mZTDetections.clear();

		//Passing TargetElement to the OpenTrainLog function will fill it with data
		CTrainLogFileMgr::instance()->OpenTrainLog(FilePath,ret,TargetElement,true);

		return (CLogElement*)TargetElement;
	}

	return 0;

}

unsigned int CLogMgr::FreeLogData(CLogElement *Element)
{
	if(Element->mZTLogType == ZT1_LOG_TYPE)
	{
		CZT1LogElement *temp = (CZT1LogElement*)Element;
		for(int i = 0; i < temp->mZTLogData.size(); i++)
			delete temp->mZTLogData.at(i);
		temp->mZTLogData.clear();
	}
	if(Element->mZTLogType == ZT2_LOG_TYPE)
	{
		CZT2LogElement *temp = (CZT2LogElement*)Element;
		for(int i = 0; i < temp->mZTLogData.size(); i++)
			delete temp->mZTLogData.at(i);
		temp->mZTLogData.clear();
	}

	return RET_OK;

}

//Resets the logs list and checks for log files.
//Thereafter, ParseNextLog() shall be called periodically for all of files in the list
//returns the number of detected files
unsigned int CLogMgr::InitSteppedLogParsing()
{
    DestroyLogList();
    mLogsFileIndex = 0;

    QStringList LogFilters;
    LogFilters << "*.bin";
    QDir LogDir("./Trains/");

    LogDir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
    LogDir.setNameFilters(LogFilters);
    LogDir.setSorting(QDir::Name);

    mLogsFilelist.clear();

    mLogsFilelist = LogDir.entryInfoList();

    return mLogsFilelist.size();

}

//returns true if the parsed log is valid.
//otherwise returns false
bool CLogMgr::ParseNextLog()
{
    if((int)mLogsFileIndex >= mLogsFilelist.size())
        return false;

    //Extract data

    if(ParseNewLog(mLogsFilelist.at(mLogsFileIndex).filePath()) == RET_ERROR)
        return false;

    mLogsFileIndex++;

    return true;
}

unsigned int CLogMgr::ParseNewLog(QString FileName, bool KeepData,bool CleanupLogs)
{
	Q_UNUSED(KeepData)

    unsigned int ret;
    CLogElement *NewLog = CTrainLogFileMgr::instance()->OpenTrainLog(FileName,ret);

    if(ret == RET_ERROR)
	{
        return RET_ERROR;
	}

	mPassagesList.append(NewLog);

	if(CleanupLogs == true)
	{
		CleanUpLogs();
	}

    return RET_OK;
}


//This function makes shure that we keep only a certain cout of standard passage files
//but we keep all the passage files that contains detections.
unsigned int CLogMgr::CleanUpLogs()
{
	int NbZT1Logs = 0, NbZT2Logs = 0;
	//First, check if we have more logs than supposed...
	for(int i = 0; i < mPassagesList.size(); i++)
	{
		CLogElement *Log = mPassagesList.at(i);

		if(Log->mZTLogType == ZT1_LOG_TYPE)
		{
			CZT1LogElement *ZT1Log = (CZT1LogElement*)Log;
            if(ZT1Log->mZTDetections.size() == 0) /* && ZT1Log->mFlags.mIsProblematicPassage == 0)*/ //JFM 2021-07-20 - Enlevé la conservation des logs problématiques car ça cause des problèmes
			{
				NbZT1Logs++;
			}
		}
		else
		{
			CZT2LogElement *ZT2Log = (CZT2LogElement*)Log;
            if(ZT2Log->mZTDetections.size() == 0) /* && ZT2Log->mFlags.mIsProblematicPassage == 0) */ //JFM 2021-07-20 - Enlevé la conservation des logs problématiques car ça cause des problèmes
			{
				NbZT2Logs++;
			}
		}
	}

	//Now check if we need to delete some logs
    bool CleanZT1Logs = false;
    if(mKeepZT1Logs == false)
    {
        CleanZT1Logs = (NbZT1Logs > mMaxFileCount);
    }

    bool CleanZT2Logs = false;
    if(mKeepZT2Logs == false)
    {
        CleanZT2Logs = (NbZT2Logs > mMaxFileCount);
    }

	//Logs are sorted in ascending order, we want to delete the older
	//logs so start from the top of the list.
	int i = 0;
	while(CleanZT1Logs == true || CleanZT2Logs == true)
	{
		CLogElement *Log = mPassagesList.at(i);

		if(Log->mZTLogType == ZT1_LOG_TYPE)
		{
			if(CleanZT1Logs == true)
			{
				CZT1LogElement *ZT1Log = (CZT1LogElement*)Log;
                if(ZT1Log->mZTDetections.size() == 0 &&
                   !(ZT1Log->mTrainType == TRAIN_TYPE_MPM10 && mKeepMPM10Logs == true)) /* &&       //JFM 2021-07-20 - Enlevé la conservation des logs problématiques car ça cause des problèmes
                   ZT1Log->mFlags.mIsProblematicPassage == 0)*/
				{
					//Remove this log.
					QDir().remove(ZT1Log->mLogFileName);
					delete mPassagesList.at(i);
					mPassagesList.removeAt(i);

					NbZT1Logs--;
                    if(NbZT1Logs <= mMaxFileCount)
					{
						CleanZT1Logs = false;
					}

				}
				else
				{
					i++;
				}
			}
			else
			{
				i++;
			}
		}
		else
		{
			if(CleanZT2Logs == true)
			{
				CZT2LogElement *ZT2Log = (CZT2LogElement*)Log;
                if(ZT2Log->mZTDetections.size() == 0)/* &&          //JFM 2021-07-20 - Enlevé la conservation des logs problématiques car ça cause des problèmes
                   ZT2Log->mFlags.mIsProblematicPassage == 0)*/
				{
					QDir().remove(ZT2Log->mLogFileName);
					delete mPassagesList.at(i);
					mPassagesList.removeAt(i);

					NbZT2Logs--;

                    if(NbZT2Logs <= mMaxFileCount)
					{
						CleanZT2Logs = false;
					}

				}
				else
				{
					i++;
				}
			}
			else
			{
				i++;
			}
		}
	}


	return RET_OK;
}


unsigned int CLogMgr::GetLogsCount()
{
	return mPassagesList.size();
}

QList<CLogElement*> * CLogMgr::GetLogsList()
{
	return &mPassagesList;
}

int CLogMgr::DeleteErrorLogs(bool ZT1, bool ZT2)
{
	int count = 0;
	int i=0;

	while(i < mPassagesList.size())
	{
		CLogElement *Log = mPassagesList.at(i);

		if(Log->mZTLogType == ZT1_LOG_TYPE)
		{
			if(ZT1 == true)
			{
				CZT1LogElement *ZT1Log = (CZT1LogElement*)Log;
                if(ZT1Log->mZTDetections.size() != 0) /* ||
                   ZT1Log->mFlags.mIsProblematicPassage == 1) */  //JFM 2021-07-20 - Enlevé la conservation des logs problématiques car ça cause des problèmes
				{
					QDir().remove(ZT1Log->mLogFileName);
					count++;
					delete mPassagesList.at(i);
					mPassagesList.removeAt(i);
				}
				else
				{
					i++;
				}
			}
		}
		else
		{
			if(ZT2 == true)
			{
				CZT2LogElement *ZT2Log = (CZT2LogElement*)Log;
                if(ZT2Log->mZTDetections.size() != 0) /* ||
                   ZT2Log->mFlags.mIsProblematicPassage == 1) */  //JFM 2021-07-20 - Enlevé la conservation des logs problématiques car ça cause des problèmes
				{
					QDir().remove(ZT2Log->mLogFileName);
					count++;
					delete mPassagesList.at(i);
					mPassagesList.removeAt(i);
				}
				else
				{
					i++;
				}
			}
		}
	}

	CEngLog::instance()->AddLogString(QString().sprintf("%d fichiers d'erreurs effacés",count));
	return count;
}

//QDataStream &operator<<(QDataStream &out, const CLogElement &source)
//{
//    return out;
//}

//QDataStream &operator>>(QDataStream &in, CLogElement &dest)
//{
//    return in;
//}
CLogElement::~CLogElement()
{
}

CZT1LogElement::~CZT1LogElement()
{
//	for(int i = 0; i < mZTLogData.size(); i++)
//		delete mZTLogData.at(i);
	qDeleteAll(mZTLogData);
	mZTLogData.clear();

//    for(int i = 0; i < mZTDetections.size(); i++)
//        delete mZTDetections.at(i);
	qDeleteAll(mZTDetections);
	mZTDetections.clear();
}

//QDataStream &operator<<(QDataStream &out, const CZT1LogElement &source)
//{
//    quint32 NbLogEntry = source.mZTLogData.size();
//    quint32 NbDetections = source.mZTDetections.size();
//    quint32 LogType = source.mZTLogType;

//    out << LogType << NbLogEntry << NbDetections;

//    for(unsigned int i = 0; i < NbLogEntry; i++)
//    {
//        out << *source.mZTLogData.at(i);
//    }

//    //write detections
//    for(unsigned int i =0; i < NbDetections; i++)
//    {
//        out << *source.mZTDetections.at(i);
//    }

//    return out;
//}

//QDataStream &operator>>(QDataStream &in, CZT1LogElement &dest)
//{
//    return in;
//}

CZT2LogElement::~CZT2LogElement()
{
	for(int i = 0; i < mZTLogData.size(); i++)
		delete mZTLogData.at(i);
	mZTLogData.clear();

    for(int i = 0; i < mZTDetections.size(); i++)
        delete mZTDetections.at(i);
    mZTDetections.clear();

}





//QDataStream &operator<<(QDataStream &out, const CZT2LogElement &source)
//{
//    quint32 NbLogEntry = source.mZTLogData.size();
//    quint32 NbDetections = source.mZTDetections.size();
//    quint32 LogType = source.mZTLogType;

//    out << LogType << NbLogEntry << NbDetections;

//    for(unsigned int i = 0; i < NbLogEntry; i++)
//    {
//        out << *source.mZTLogData.at(i);
//    }

//    //write detections
//    for(unsigned int i =0; i < NbDetections; i++)
//    {
//        out << *source.mZTDetections.at(i);
//    }

//    return out;
//}

////QDataStream &operator>>(QDataStream &in, CZT2LogElement &dest)
////{
//////    quint32 NbLogEntry = source.mZTLogData.size();
//////    quint32 NbDetections = source.mZTDetections.size();
//////    quint32 LogType = source.mZTLogType;

//////    dest >> MagicNbr >> LogType >> NbLogEntry >> NbDetections;

//////    for(unsigned int i = 0; i < NbLogEntry; i++)
//////    {
//////        out << *source.mZTLogData->at(i);
//////    }

//////    //write detections
//////    for(unsigned int i =0; i < NbDetections; i++)
//////    {
//////        out << *source.mZTDetections->at(i);
//////    }
////    return in;
////}
