// Target: PythonTextTail.cpp
// Search:
// void CPythonTextTail::Render()

// Replace Render - ENABLE_MOB_STACK_NAME caches stack @100ms, SetValue only on change

#ifdef ENABLE_MOB_STACK_NAME
std::string CPythonTextTail::GetBaseNameFromTextTail(TTextTail* pTextTail)
{
	auto it = m_OriginalNameMap.find(pTextTail->dwVirtualID);
	if (it != m_OriginalNameMap.end())
		return it->second;
	if (pTextTail->pTextInstance)
		return std::string(pTextTail->pTextInstance->GetValueStringReference());
	return std::string();
}
#endif

void CPythonTextTail::Render()
{
	TTextTailList::iterator itor;

#ifdef ENABLE_MOB_STACK_NAME
	// O(n^2) stack clustering + SetValue/Update every frame was the FPS killer and
	// made names jump when groups reformed. Recompute slowly; SetValue only on change.
	static DWORD s_dwLastStackMS = 0;
	static std::map<DWORD, int> s_stackCount;       // leader VID -> count
	static std::map<DWORD, DWORD> s_memberLeader;  // member VID -> leader VID
	static std::map<DWORD, std::string> s_lastDisplay;

	const DWORD dwNowMS = ELTimer_GetMSec();
	if (dwNowMS - s_dwLastStackMS >= 100)
	{
		s_dwLastStackMS = dwNowMS;
		s_stackCount.clear();
		s_memberLeader.clear();

		for (itor = m_CharacterTextTailList.begin(); itor != m_CharacterTextTailList.end(); ++itor)
		{
			TTextTail* pTextTail = *itor;
			if (!pTextTail)
				continue;

			if (s_memberLeader.find(pTextTail->dwVirtualID) != s_memberLeader.end())
				continue;

			CInstanceBase* pInst = CPythonCharacterManager::Instance().GetInstancePtr(pTextTail->dwVirtualID);
			if (pInst && pInst->IsPC())
			{
				s_memberLeader[pTextTail->dwVirtualID] = pTextTail->dwVirtualID;
				s_stackCount[pTextTail->dwVirtualID] = 1;
				continue;
			}

			std::string myName = GetBaseNameFromTextTail(pTextTail);
			s_memberLeader[pTextTail->dwVirtualID] = pTextTail->dwVirtualID;
			s_stackCount[pTextTail->dwVirtualID] = 1;
			if (myName.empty())
				continue;

			for (TTextTailList::iterator itorOther = m_CharacterTextTailList.begin();
				itorOther != m_CharacterTextTailList.end(); ++itorOther)
			{
				if (itorOther == itor)
					continue;

				TTextTail* pOther = *itorOther;
				if (!pOther)
					continue;
				if (s_memberLeader.find(pOther->dwVirtualID) != s_memberLeader.end())
					continue;

				CInstanceBase* pOtherInst = CPythonCharacterManager::Instance().GetInstancePtr(pOther->dwVirtualID);
				if (pOtherInst && pOtherInst->IsPC())
					continue;

				if (GetBaseNameFromTextTail(pOther) != myName)
					continue;

				float dx = pTextTail->x - pOther->x;
				float dy = pTextTail->y - pOther->y;
				if ((dx * dx + dy * dy) <= (MOB_STACK_NAME_PIXEL_THRESHOLD * MOB_STACK_NAME_PIXEL_THRESHOLD))
				{
					s_memberLeader[pOther->dwVirtualID] = pTextTail->dwVirtualID;
					s_stackCount[pTextTail->dwVirtualID]++;
				}
			}
		}
	}

	for (itor = m_CharacterTextTailList.begin(); itor != m_CharacterTextTailList.end(); ++itor)
	{
		TTextTail* pTextTail = *itor;
		if (!pTextTail)
			continue;

		std::map<DWORD, DWORD>::iterator itLeader = s_memberLeader.find(pTextTail->dwVirtualID);
		if (itLeader == s_memberLeader.end())
		{
			// Not in cached map yet (first frames) - render normally
		}
		else if (itLeader->second != pTextTail->dwVirtualID)
		{
			continue; // stacked under another leader
		}

		int nCount = 1;
		std::map<DWORD, int>::iterator itCount = s_stackCount.find(pTextTail->dwVirtualID);
		if (itCount != s_stackCount.end())
			nCount = itCount->second;

		std::map<DWORD, std::string>::iterator itOrigName = m_OriginalNameMap.find(pTextTail->dwVirtualID);
		if (itOrigName != m_OriginalNameMap.end() && pTextTail->pTextInstance)
		{
			char szDisplay[CHARACTER_NAME_MAX_LEN + 16];
			if (nCount > 1)
				snprintf(szDisplay, sizeof(szDisplay), "%s (%d)", itOrigName->second.c_str(), nCount);
			else
				snprintf(szDisplay, sizeof(szDisplay), "%s", itOrigName->second.c_str());

			std::map<DWORD, std::string>::iterator itLast = s_lastDisplay.find(pTextTail->dwVirtualID);
			if (itLast == s_lastDisplay.end() || itLast->second != szDisplay)
			{
				s_lastDisplay[pTextTail->dwVirtualID] = szDisplay;
				pTextTail->pTextInstance->SetValue(szDisplay);
				pTextTail->pTextInstance->Update();
			}
		}

		pTextTail->pTextInstance->Render();
		if (pTextTail->pMarkInstance && pTextTail->pGuildNameTextInstance)
		{
			pTextTail->pMarkInstance->Render();
			pTextTail->pGuildNameTextInstance->Render();
		}
		if (pTextTail->pTitleTextInstance)
			pTextTail->pTitleTextInstance->Render();
		if (pTextTail->pLevelTextInstance)
			pTextTail->pLevelTextInstance->Render();
	}
#else
	for (itor = m_CharacterTextTailList.begin(); itor != m_CharacterTextTailList.end(); ++itor)
	{
		TTextTail * pTextTail = *itor;
		pTextTail->pTextInstance->Render();
		if (pTextTail->pMarkInstance && pTextTail->pGuildNameTextInstance)
		{
			pTextTail->pMarkInstance->Render();
			pTextTail->pGuildNameTextInstance->Render();
		}
		if (pTextTail->pTitleTextInstance)
		{
			pTextTail->pTitleTextInstance->Render();
		}
		if (pTextTail->pLevelTextInstance)
		{
			pTextTail->pLevelTextInstance->Render();
		}
	}
#endif

	for (itor = m_ItemTextTailList.begin(); itor != m_ItemTextTailList.end(); ++itor)
	{
		TTextTail * pTextTail = *itor;

		RenderTextTailBox(pTextTail);
		pTextTail->pTextInstance->Render();
		if (pTextTail->pOwnerTextInstance)
			pTextTail->pOwnerTextInstance->Render();
	}

	for (TChatTailMap::iterator itorChat = m_ChatTailMap.begin(); itorChat!=m_ChatTailMap.end(); ++itorChat)
	{
		TTextTail * pTextTail = itorChat->second;
		if (pTextTail->pOwner->isShow())
			RenderTextTailName(pTextTail);
	}
}
