/***************************************************************************
                          cuserlist.cpp  -  description
                             -------------------
    begin                : Sun Sep 30 2001
    copyright            : (C) 2001-2005 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "cuserlist.h"

#include <stdio.h>

#include "core/clist.h"
#include "dcobject.h"

CUserList::CUserList()
{
	m_nShareSize = 0;
}

CUserList::~CUserList()
{
	Clear();
}

/** */
void CUserList::Clear()
{
	m_Mutex.Lock();
	
	for ( MyInfoMap::iterator it = m_MyInfoMap.begin(); it != m_MyInfoMap.end(); ++it )
	{
		delete it->second;
	}
	
	m_MyInfoMap.clear();
	
	m_Mutex.UnLock();
	
	m_nShareSize = 0;
}

/** */
void CUserList::InitUserList( CMessageNickList * nicklist )
{
	CString *nick;

	if (!nicklist)
		return;

	m_nShareSize = 0;

	for(nick=0;(nick=nicklist->m_NickList.Next(nick))!=0;)
	{
		AppendUser(*nick);
	}
}

/** */
void CUserList::InitOperatorList( CMessageOpList * oplist )
{
	CString *nick;

	// set in the nicklist the operator status
	for(nick=0;(nick=oplist->m_NickList.Next(nick))!=0;)
	{
		// add the user, some hubs send the oplist before nicklist or myinfo
		AppendUser(*nick);
		
		m_Mutex.Lock();
		MyInfoMap::iterator it = m_MyInfoMap.find(nick->Data());
		if ( it != m_MyInfoMap.end() )
		{
			it->second->m_bOperator = true;
		}
		m_Mutex.UnLock();
	}
}

/** */
bool CUserList::AppendUser( CString & nick, bool getinfo )
{
	bool res = false;
	
	if ( nick.IsEmpty() )
	{
		return res;
	}
	
	CMessageMyInfo *myinfo=0;

	m_Mutex.Lock();

	MyInfoMap::iterator it = m_MyInfoMap.find(nick);
	if ( it == m_MyInfoMap.end() )
	{
		myinfo = new CMessageMyInfo();
		myinfo->m_sNick     = nick;
		myinfo->m_eAwayMode = euamNORMAL;

		m_MyInfoMap[nick] = myinfo;

		if ( getinfo )
			res = true;
	}

	m_Mutex.UnLock();
	
	return res;
}

/** */
void CUserList::RemoveUser( CString & nick )
{
	if ( nick.IsEmpty() )
	{
		return;
	}
	
	CMessageMyInfo *myinfo=0;

	m_Mutex.Lock();

	MyInfoMap::iterator it = m_MyInfoMap.find(nick);
	if ( it != m_MyInfoMap.end() )
	{
		myinfo = it->second;
		
		if ( myinfo->m_nShared > m_nShareSize )
			m_nShareSize = 0;
		else
			m_nShareSize -= myinfo->m_nShared;

		m_MyInfoMap.erase( it );
		delete myinfo;
	}

	m_Mutex.UnLock();
}

/** */
bool CUserList::UpdateUser( CMessageMyInfo * pMyInfo )
{
	bool res = false;
	CMessageMyInfo *myinfo=0;

	if (!pMyInfo)
		return res;

	if ( pMyInfo->m_sNick.IsEmpty() )
	{
		return res;
	}

	m_Mutex.Lock();

	MyInfoMap::iterator it = m_MyInfoMap.find(pMyInfo->m_sNick);
	if ( it != m_MyInfoMap.end() )
	{
		myinfo = it->second;
		
		if ( myinfo->m_nShared >= m_nShareSize )
			m_nShareSize = 0;
		else
			m_nShareSize -= myinfo->m_nShared;
		
		// don't send old data to the gui
		if ( myinfo->m_sNick != pMyInfo->m_sNick )
			res = true;
		else if ( myinfo->m_sComment != pMyInfo->m_sComment )
			res = true;
		else if ( myinfo->m_sUnknown != pMyInfo->m_sUnknown )
			res = true;
		else if ( myinfo->m_eUserSpeed != pMyInfo->m_eUserSpeed )
			res = true;
		else if ( myinfo->m_sUserSpeed != pMyInfo->m_sUserSpeed )
			res = true;
		else if ( myinfo->m_sEMail != pMyInfo->m_sEMail )
			res = true;
		else if ( myinfo->m_nShared != pMyInfo->m_nShared )
			res = true;
		else if ( myinfo->m_eAwayMode != pMyInfo->m_eAwayMode )
			res = true;
		else if ( pMyInfo->m_bOperator != myinfo->m_bOperator )
			res = true;
		else if ( myinfo->m_sVerComment != pMyInfo->m_sVerComment )
			res = true;
		else if ( myinfo->m_eClientVersion != pMyInfo->m_eClientVersion )
			res = true;
		else if ( myinfo->m_eClientMode != pMyInfo->m_eClientMode )
			res = true;
		else if ( myinfo->m_bServerFlag != pMyInfo->m_bServerFlag )
			res = true;
		else if ( myinfo->m_bFireballFlag != pMyInfo->m_bFireballFlag )
			res = true;
		else if ( myinfo->m_bTLSFlag != pMyInfo->m_bTLSFlag )
			res = true;
		
		/* copy values that are stored but not in $MyINFO protocol message */
		pMyInfo->m_bOperator = myinfo->m_bOperator;
		pMyInfo->m_MessageLock = myinfo->m_MessageLock;
		pMyInfo->m_MessageSupports = myinfo->m_MessageSupports;
		pMyInfo->m_sTransferHost = myinfo->m_sTransferHost;
	}
	else
	{
		myinfo = new CMessageMyInfo();
		
		m_MyInfoMap[pMyInfo->m_sNick] = myinfo;

		res = true;
	}

	if ( res )
	{
		*myinfo = *pMyInfo;
	}

	m_nShareSize += myinfo->m_nShared;

	m_Mutex.UnLock();

	return res;
}

/** */
bool CUserList::IsUserOnline( CString & nick )
{
	bool res = false;

	if ( nick.IsEmpty() )
	{
		return res;
	}

	m_Mutex.Lock();
	
	res = ( m_MyInfoMap.find(nick) != m_MyInfoMap.end() );
	
	m_Mutex.UnLock();

	return res;
}

/** */
bool CUserList::IsAdmin( const CString & nick )
{
	bool res = false;

	if ( nick.IsEmpty() )
	{
		return res;
	}

	m_Mutex.Lock();
	
	MyInfoMap::const_iterator it = m_MyInfoMap.find(nick);
	if ( it != m_MyInfoMap.end() )
	{
		res = it->second->m_bOperator;
	}
	
	m_Mutex.UnLock();

	return res;
}

/** */
eUserClientVersion CUserList::GetUserClientVersion( const CString & nick )
{
	eUserClientVersion res = eucvNONE;

	if ( nick.IsEmpty() )
	{
		return res;
	}

	m_Mutex.Lock();
	
	MyInfoMap::const_iterator it = m_MyInfoMap.find(nick);
	if ( it != m_MyInfoMap.end() )
	{
		res = it->second->m_eClientVersion;
	}
	
	m_Mutex.UnLock();

	return res;
}

/** */
bool CUserList::GetUserMyInfo( CString nick, CMessageMyInfo * p )
{
	bool res = false;

	if ( nick.IsEmpty() )
	{
		return res;
	}

	m_Mutex.Lock();
	
	MyInfoMap::const_iterator it = m_MyInfoMap.find(nick);
	if ( it != m_MyInfoMap.end() )
	{
		*p = *(it->second);
		res = true;
	}
	
	m_Mutex.UnLock();

	return res;
}

/** */
bool CUserList::SetUserTransferInfo( CString nick, CDCMessage * msg )
{
	bool res = false;
	CMessageMyInfo *myinfo=0;
	
	if ( (msg) && (!nick.IsEmpty()) )
	{
		m_Mutex.Lock();

		MyInfoMap::iterator it = m_MyInfoMap.find(nick);
		if ( it != m_MyInfoMap.end() )
		{
			myinfo = it->second;
			if ( msg->m_eType == DC_MESSAGE_CONNECT_CLIENT)
			{
				myinfo->m_sTransferHost = ((DCMessageConnectClient*)msg)->m_sHubHost;
				res = true;			
			}
			else if ( msg->m_eType == DC_MESSAGE_LOCK)
			{
				myinfo->m_MessageLock = *((CMessageLock*)msg);
				res = true;
			}
			else if ( msg->m_eType == DC_MESSAGE_SUPPORTS)
			{
				myinfo->m_MessageSupports = *((CMessageSupports*)msg);
				res = true;
			}
		}

		m_Mutex.UnLock();
	}

	return res;
}

/** */
CString CUserList::GetNextNick( const CString & nick )
{
	CString s;
	
	m_Mutex.Lock();
	
	if ( ! m_MyInfoMap.empty() )
	{
		MyInfoMap::iterator it;
		
		if ( nick.IsEmpty() )
		{
			it = m_MyInfoMap.begin();
			s = it->first;
		}
		else
		{
			it = m_MyInfoMap.find(nick);
			
			if ( it != m_MyInfoMap.end() )
			{
				++it;
				if ( it != m_MyInfoMap.end() )
				{
					s = it->first;
				}
			}
		}
	}
	
	m_Mutex.UnLock();
	
	return s;
}

/** */
bool CUserList::SetUserIP( const CString & nick, const CString & ip )
{
	if ( nick.IsEmpty() )
	{
		return false;
	}
	
	bool res = false;
	
	m_Mutex.Lock();
	
	MyInfoMap::const_iterator it = m_MyInfoMap.find( nick );
	
	if ( it != m_MyInfoMap.end() )
	{
		if ( it->second->m_sTransferHost != ip )
		{
			it->second->m_sTransferHost = ip;
			res = true;
		}
	}
	
	m_Mutex.UnLock();
	
	return res;
}
