/*

dbx_tree: tree database driver for Miranda IM

Copyright 2010 Michael "Protogenes" Kunz,

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.

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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#pragma once

#include "lockfree_hashmap.h"

template <typename TAdministrator, typename TData>
class CThreadLocalStorage
{
	private:
		typedef struct TListElem
		{
			TListElem *      Next;
			TAdministrator * Admin;
			TData            Data;
			TListElem(TListElem * ANext, TAdministrator * AAdmin, const TData & AData)
				:	Next(ANext), Admin(AAdmin), Data(AData)
				{	};

		} TListElem, *PListElem;
		static __declspec(thread) PListElem m_Head;
		
		static inline uint32_t DummyHash(const void * Data, uint32_t Length)
		{
			return *reinterpret_cast<const uint32_t *>(Data);
		}
		
		lockfree::hash_map<DWORD, TData, DummyHash> * m_LockfreeList;
		typedef typename lockfree::hash_map<DWORD, TData, DummyHash>::iterator TThreadStorage;

	public:
		CThreadLocalStorage();
		~CThreadLocalStorage();

		TData & Open(TAdministrator * Admin, const TData & Default);
		TData * Find(TAdministrator * Admin);
		void    Remove(TAdministrator * Admin);
};

const bool _CanUseTLS = ((LOBYTE(LOWORD(GetVersion()))) >= 6);

template <typename TAdministrator, typename TData>
typename CThreadLocalStorage<TAdministrator, TData>::PListElem CThreadLocalStorage<TAdministrator, TData>::m_Head = NULL;


template <typename TAdministrator, typename TData>
CThreadLocalStorage<TAdministrator, TData>::CThreadLocalStorage()
{
	m_LockfreeList = NULL;
	if (!_CanUseTLS)
		m_LockfreeList = new lockfree::hash_map<DWORD, TData, DummyHash>();
}
template <typename TAdministrator, typename TData>
CThreadLocalStorage<TAdministrator, TData>::~CThreadLocalStorage()
{
	if (m_LockfreeList)
		delete m_LockfreeList;
}


template <typename TAdministrator, typename TData>
typename TData & CThreadLocalStorage<TAdministrator, TData>::Open(typename TAdministrator * Admin, const TData & Default)
{
	if (_CanUseTLS)
	{
		PListElem * last = &m_Head;
		PListElem i = m_Head;
		while (i && (i->Admin != Admin))
		{
			last = &i->Next;
			i = i->Next;
		}

		if (i)
		{
			*last = i->Next;
			i->Next = m_Head;
			m_Head = i;
		} else {
			m_Head = new TListElem(m_Head, Admin, Default);
		}
		return m_Head->Data;
	} else {
		TThreadStorage & res = m_LockfreeList->insert(std::make_pair(GetCurrentThreadId(), Default)).first;
		return res->second;
	}
}

template <typename TAdministrator, typename TData>
typename TData * CThreadLocalStorage<TAdministrator, TData>::Find(typename TAdministrator * Admin)
{
	if (_CanUseTLS)
	{
		PListElem * last = &m_Head;
		PListElem i = m_Head;
		while (i && (i->Admin != Admin))
		{
			last = &i->Next;
			i = i->Next;
		}

		if (i)
		{
			*last = i->Next;
			i->Next = m_Head;
			m_Head = i;
		} else {
			return NULL;
		}
		return &m_Head->Data;
	} else {
		TThreadStorage & res = m_LockfreeList->find(GetCurrentThreadId());
		if (res != m_LockfreeList->end())
			return &res->second;
		else 
			return NULL;
	}
}

template <typename TAdministrator, typename TData>
void    CThreadLocalStorage<TAdministrator, TData>::Remove(typename TAdministrator * Admin)
{
	if (_CanUseTLS)
	{
		PListElem * last = &m_Head;
		PListElem i = m_Head;
		while (i && (i->Admin != Admin))
		{
			last = &i->Next;
			i = i->Next;
		}

		if (i)
		{
			*last = i->Next;
			delete i;
		}
	} else {
		m_LockfreeList->erase(GetCurrentThreadId());
	}
}