// This file is part of ZetaGlest <https://github.com/ZetaGlest>
//
// Copyright (C) 2018  The ZetaGlest team
//
// ZetaGlest is a fork of MegaGlest <https://megaglest.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>

#include "base_thread.h"
#include "platform_common.h"
#include "util.h"
#include "conversion.h"
#include "platform_util.h"
#include <time.h>

using namespace Shared::Util;

namespace Shared {
	namespace PlatformCommon {

		Mutex BaseThread::mutexMasterThreadList;
		std::map<void *, int> BaseThread::masterThreadList;

		BaseThread::BaseThread() : Thread(),
			mutexRunning(new Mutex(CODE_AT_LINE)),
			mutexQuit(new Mutex(CODE_AT_LINE)),
			mutexBeginExecution(new Mutex(CODE_AT_LINE)),
			mutexDeleteSelfOnExecutionDone(new Mutex(CODE_AT_LINE)),
			mutexThreadObjectAccessor(new Mutex(CODE_AT_LINE)),
			mutexThreadOwnerValid(new Mutex(CODE_AT_LINE)),
			mutexExecutingTask(new Mutex(CODE_AT_LINE)),
			ptr(NULL),
			mutexStarted(new Mutex(CODE_AT_LINE)),
			genericData(NULL) {

			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d]\n", __FILE__, __FUNCTION__, __LINE__);
			ptr = this;
			MutexSafeWrapper safeMutexMasterList(&mutexMasterThreadList);
			masterThreadList[ptr]++;
			safeMutexMasterList.ReleaseLock();

			uniqueID = "base_thread";

			setQuitStatus(false);
			setRunningStatus(false);
			setStarted(false);
			setHasBeginExecution(false);
			setExecutingTask(false);
			setDeleteSelfOnExecutionDone(false);
			setThreadOwnerValid(true);

			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d]\n", __FILE__, __FUNCTION__, __LINE__);
		}

		Mutex * BaseThread::getMutexThreadObjectAccessor() {
			return mutexThreadObjectAccessor;
		}

		BaseThread::~BaseThread() {

			//printf("In ~BaseThread Line: %d uniqueID [%s]\n",__LINE__,uniqueID.c_str());
			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s]\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str());

			//BaseThread *base_thread = dynamic_cast<BaseThread *>(this);
			if (this->getStarted() == false) {
				time_t elapsed = time(NULL);
				for (; this->getStarted() == false &&
					difftime((long int) time(NULL), elapsed) <= 3;) {
					sleep(5);
				}
			}

			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s]\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str());
			bool ret = shutdownAndWait();
			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s] ret [%d] END\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str(), ret);

			//printf("In ~BaseThread Line: %d uniqueID [%s]\n",__LINE__,uniqueID.c_str());

			MutexSafeWrapper safeMutexMasterList(&mutexMasterThreadList);

			//printf("In ~BaseThread Line: %d uniqueID [%s]\n",__LINE__,uniqueID.c_str());

			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s] ret [%d] END\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str(), ret);

			//printf("In ~BaseThread Line: %d uniqueID [%s]\n",__LINE__,uniqueID.c_str());

			if (masterThreadList.find(this) == masterThreadList.end()) {
				if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s] ret [%d] END\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str(), ret);

				//printf("invalid thread delete for ptr: %p", this);
				//throw game_runtime_error(szBuf);
			}
			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s] ret [%d] END\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str(), ret);

			//printf("In ~BaseThread Line: %d uniqueID [%s]\n",__LINE__,uniqueID.c_str());

			masterThreadList[this]--;
			if (masterThreadList[this] <= 0) {
				if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s] ret [%d] END\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str(), ret);
				masterThreadList.erase(this);
			}

			//printf("In ~BaseThread Line: %d uniqueID [%s]\n",__LINE__,uniqueID.c_str());

			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s] ret [%d] END\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str(), ret);

			safeMutexMasterList.ReleaseLock();

			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s] ret [%d] END\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str(), ret);


			delete mutexRunning;
			mutexRunning = NULL;
			delete mutexQuit;
			mutexQuit = NULL;
			delete mutexBeginExecution;
			mutexBeginExecution = NULL;
			delete mutexDeleteSelfOnExecutionDone;
			mutexDeleteSelfOnExecutionDone = NULL;
			delete mutexThreadObjectAccessor;
			mutexThreadObjectAccessor = NULL;
			delete mutexThreadOwnerValid;
			mutexThreadOwnerValid = NULL;
			delete mutexExecutingTask;
			mutexExecutingTask = NULL;
			delete mutexStarted;
			mutexStarted = NULL;

			//printf("In ~BaseThread Line: %d uniqueID [%s] [%p]\n",__LINE__,uniqueID.c_str(),this);
		}

		bool BaseThread::getStarted() {
			static string mutexOwnerId = CODE_AT_LINE;
			MutexSafeWrapper safeMutex(mutexStarted, mutexOwnerId);
			mutexStarted->setOwnerId(mutexOwnerId);
			bool retval = started;
			safeMutex.ReleaseLock();

			return retval;
		}

		void BaseThread::setStarted(bool value) {
			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s]\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str());

			static string mutexOwnerId = CODE_AT_LINE;
			MutexSafeWrapper safeMutex(mutexStarted, mutexOwnerId);
			mutexStarted->setOwnerId(mutexOwnerId);
			started = value;
			safeMutex.ReleaseLock();

			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s]\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str());
		}

		bool BaseThread::isThreadDeleted(void *ptr) {
			bool result = false;
			MutexSafeWrapper safeMutexMasterList(&mutexMasterThreadList);
			if (masterThreadList.find(ptr) != masterThreadList.end()) {
				result = (masterThreadList[ptr] <= 0);
			}
			safeMutexMasterList.ReleaseLock();
			return result;
		}

		Mutex * BaseThread::getMutexThreadOwnerValid() {
			if (getThreadOwnerValid() == true) {
				return mutexThreadOwnerValid;
			}
			return NULL;
		}

		void BaseThread::setThreadOwnerValid(bool value) {
			static string mutexOwnerId = CODE_AT_LINE;
			MutexSafeWrapper safeMutex(mutexThreadOwnerValid, mutexOwnerId);
			mutexThreadOwnerValid->setOwnerId(mutexOwnerId);
			threadOwnerValid = value;
			safeMutex.ReleaseLock();
		}

		bool BaseThread::getThreadOwnerValid() {
			//bool ret = false;
			static string mutexOwnerId = CODE_AT_LINE;
			MutexSafeWrapper safeMutex(mutexThreadOwnerValid, mutexOwnerId);
			//mutexThreadOwnerValid.setOwnerId(mutexOwnerId);
			bool ret = threadOwnerValid;
			safeMutex.ReleaseLock();

			return ret;
		}

		void BaseThread::signalQuit() {
			setQuitStatus(true);
		}

		void BaseThread::setQuitStatus(bool value) {
			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s]\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str());

			static string mutexOwnerId = CODE_AT_LINE;
			MutexSafeWrapper safeMutex(mutexQuit, mutexOwnerId);
			mutexQuit->setOwnerId(mutexOwnerId);
			quit = value;
			safeMutex.ReleaseLock();

			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s]\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str());
		}

		bool BaseThread::getQuitStatus() {
			//bool retval = false;
			//static string mutexOwnerId = CODE_AT_LINE;
			MutexSafeWrapper safeMutex(mutexQuit, CODE_AT_LINE);
			//mutexQuit.setOwnerId(mutexOwnerId);
			bool retval = quit;
			safeMutex.ReleaseLock();

			return retval;
		}

		bool BaseThread::getHasBeginExecution() {
			//bool retval = false;
			static string mutexOwnerId = CODE_AT_LINE;
			MutexSafeWrapper safeMutex(mutexBeginExecution, mutexOwnerId);
			//mutexBeginExecution.setOwnerId(mutexOwnerId);
			bool retval = hasBeginExecution;
			safeMutex.ReleaseLock();

			return retval;
		}

		void BaseThread::setHasBeginExecution(bool value) {
			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s]\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str());

			static string mutexOwnerId = CODE_AT_LINE;
			MutexSafeWrapper safeMutex(mutexBeginExecution, mutexOwnerId);
			mutexBeginExecution->setOwnerId(mutexOwnerId);
			hasBeginExecution = value;
			safeMutex.ReleaseLock();

			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s]\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str());
		}

		bool BaseThread::getRunningStatus() {
			//bool retval = false;

			static string mutexOwnerId = CODE_AT_LINE;
			MutexSafeWrapper safeMutex(mutexRunning, mutexOwnerId);
			bool retval = running;
			safeMutex.ReleaseLock();

			if (retval == false) {
				retval = !getHasBeginExecution();
			}

			return retval;
		}

		void BaseThread::setRunningStatus(bool value) {
			static string mutexOwnerId = CODE_AT_LINE;
			MutexSafeWrapper safeMutex(mutexRunning, mutexOwnerId);
			mutexRunning->setOwnerId(mutexOwnerId);
			running = value;
			if (value == true) {
				setHasBeginExecution(true);
			}
			safeMutex.ReleaseLock();
		}

		void BaseThread::setExecutingTask(bool value) {
			static string mutexOwnerId = CODE_AT_LINE;
			MutexSafeWrapper safeMutex(mutexExecutingTask, mutexOwnerId);
			mutexExecutingTask->setOwnerId(mutexOwnerId);
			executingTask = value;
			safeMutex.ReleaseLock();
		}

		bool BaseThread::getExecutingTask() {
			//bool retval = false;
			static string mutexOwnerId = CODE_AT_LINE;
			MutexSafeWrapper safeMutex(mutexExecutingTask, mutexOwnerId);
			bool retval = executingTask;
			safeMutex.ReleaseLock();

			return retval;
		}

		bool BaseThread::getDeleteSelfOnExecutionDone() {
			//bool retval = false;
			static string mutexOwnerId = CODE_AT_LINE;
			MutexSafeWrapper safeMutex(mutexDeleteSelfOnExecutionDone, mutexOwnerId);
			bool retval = deleteSelfOnExecutionDone;
			safeMutex.ReleaseLock();

			return retval;
		}

		void BaseThread::setDeleteSelfOnExecutionDone(bool value) {
			static string mutexOwnerId = CODE_AT_LINE;
			MutexSafeWrapper safeMutex(mutexDeleteSelfOnExecutionDone, mutexOwnerId);
			mutexDeleteSelfOnExecutionDone->setOwnerId(mutexOwnerId);
			deleteSelfOnExecutionDone = value;
		}

		void BaseThread::deleteSelfIfRequired() {
			if (getDeleteSelfOnExecutionDone() == true) {
				if (isThreadDeleted(this->ptr) == false) {
					this->setDeleteAfterExecute(true);
				}
				return;
			}
		}

		bool BaseThread::canShutdown(bool deleteSelfIfShutdownDelayed) {
			return true;
		}

		bool BaseThread::shutdownAndWait(BaseThread *pThread) {
			bool ret = false;
			if (pThread != NULL) {
				ret = pThread->shutdownAndWait();
			}

			return ret;
		}

		bool BaseThread::shutdownAndWait() {
			bool ret = true;
			BaseThread *pThread = this;
			//string uniqueID = (pThread != NULL ? pThread->getUniqueID() : "?");
			string uniqueID = pThread->getUniqueID();
			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s]\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str());

			//if(pThread != NULL) {
			if (pThread->getRunningStatus() == true) {
				if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s]\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str());

				pThread->signalQuit();
				sleep(0);
				ret = false;

				int maxWaitSeconds = 5;
				if (pThread->canShutdown() == false) {
					maxWaitSeconds = 2;
				}

				for (time_t elapsed = time(NULL); difftime(time(NULL), elapsed) <= maxWaitSeconds; ) {
					if (pThread->getRunningStatus() == false) {
						ret = true;
						break;
					}

					sleep(0);
				}
				if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s]\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str());
				sleep(0);
			}
			//}
			if (SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem, "In [%s::%s Line: %d] uniqueID [%s] ret [%d] END\n", __FILE__, __FUNCTION__, __LINE__, uniqueID.c_str(), ret);
			return ret;
		}

	}
} //end namespace
