#include "FileZilla.h"
#include "pathcache.h"

CPathCache::tCache CPathCache::m_cache;

int CPathCache::m_hits = 0;
int CPathCache::m_misses = 0;

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

void CPathCache::Store(const CServer& server, const CServerPath& target, const CServerPath& source, const wxString subdir/*=_T("")*/)
{
	wxASSERT(!target.IsEmpty() && !source.IsEmpty());

	tServerCache *pServerCache;
	tCacheIterator iter = m_cache.find(server);
	if (iter != m_cache.end())
		pServerCache = iter->second;
	else
	{
		pServerCache = new tServerCache;
		m_cache[server] = pServerCache;
	}
	tServerCache &serverCache = *pServerCache;

	CSourcePath sourcePath;
	
	sourcePath.source = Lookup(serverCache, source, _T(""));
	if (sourcePath.source.IsEmpty())
		sourcePath.source = source;

	sourcePath.subdir = subdir;

	serverCache[sourcePath] = target;

	if (subdir != _T(""))
	{
		if (subdir == _T(".."))
		{
			if (!sourcePath.source.HasParent())
				return;
			sourcePath.source = sourcePath.source.GetParent();
		}
		else
			sourcePath.source.AddSegment(subdir);

		if (sourcePath.source != target)
			return;
		sourcePath.subdir = _T("");

		serverCache[sourcePath] = target;
	}
}

CServerPath CPathCache::Lookup(const CServer& server, const CServerPath& source, const wxString subdir /*=_T("")*/)
{
	const tCacheConstIterator iter = m_cache.find(server);
	if (iter == m_cache.end())
		return CServerPath();

	CServerPath result = Lookup(*iter->second, source, subdir);
	if (result.IsEmpty())
	{
		CServerPath path = source;
		path.AddSegment(subdir);
		result = Lookup(*iter->second, path, _T(""));
	}

	if (result.IsEmpty())
		m_misses++;
	else
		m_hits++;

	return result;
}

CServerPath CPathCache::Lookup(const tServerCache &serverCache, const CServerPath& source, const wxString subdir) const
{
	CSourcePath sourcePath;
	
	if (subdir != _T(""))
	{
		sourcePath.source = Lookup(serverCache, source, _T(""));
		if (sourcePath.source.IsEmpty())
			sourcePath.source = source;
	}
	else
		sourcePath.source = source;

	sourcePath.subdir = subdir;
	tServerCacheConstIterator serverIter = serverCache.find(sourcePath);
	if (serverIter == serverCache.end())
		return CServerPath();

	return serverIter->second;
}

void CPathCache::InvalidateServer(const CServer& server)
{
	tCacheIterator iter = m_cache.find(server);
	if (iter == m_cache.end())
		return;

	delete iter->second;
	m_cache.erase(iter);
}

void CPathCache::InvalidatePath(const CServer& server, const CServerPath& path, const wxString& subdir /*=_T("")*/)
{
	// It should suffice to just remove the path/subdir pair and the constructed path
	// Since directory removal is done recursively, the childs will be deleted prior to this one
	// Even if not (for whatever reason), removing this entry should suffice in case its target changes
	// after MKDing it again. (e.g. RMD a link, MKD a regular directory)

	tCacheIterator iter = m_cache.find(server);
	if (iter == m_cache.end())
		return;

	CSourcePath sourcePath;
	
	sourcePath.source = Lookup(*iter->second, path, _T(""));
	if (sourcePath.source.IsEmpty())
		sourcePath.source = path;

	sourcePath.subdir = subdir;

	tServerCacheIterator serverIter = iter->second->find(sourcePath);
	if (serverIter != iter->second->end())
		iter->second->erase(serverIter);

	sourcePath.source.AddSegment(subdir);
	sourcePath.subdir = _T("");
	serverIter = iter->second->find(sourcePath);
	if (serverIter != iter->second->end())
		iter->second->erase(serverIter);
}

void CPathCache::Clear()
{
	for (tCacheIterator iter = m_cache.begin(); iter != m_cache.end(); iter++)
		delete iter->second;

	m_cache.clear();
}



syntax highlighted by Code2HTML, v. 0.9.1