//-------------------------------------------------------------------------------
// <copyright file="PayloadPortSlave_ThreadSafeMap.cpp" company="LiquidRobotics">
//
// Copyright (c) 2014 Liquid Robotics, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// This software is associated with a release.  Liquid Robotics makes no
// representation that the design, interfaces or component distribution will
// remain unchanged in future releases.
//
// </copyright>
//
// <summary>
// 	Implements the ThreadSafeMap inner class of PayloadPortSlave
// </summary>
//
// <owner>Mike Cookson</owner>
//-------------------------------------------------------------------------------

#include "PayloadPortSlave.h"

namespace LiquidRobotics {
namespace PayloadInterface {

PayloadPortSlave::ThreadSafeMap::ThreadSafeMap()
: pMutex(NULL)
{
	pMutex = new Mutex;
}

PayloadPortSlave::ThreadSafeMap::~ThreadSafeMap()
{
	if (pMutex)
		delete pMutex;
}

int PayloadPortSlave::ThreadSafeMap::Size() const
{
	Lock lock(*pMutex);
	return items.size();
}

bool PayloadPortSlave::ThreadSafeMap::Empty() const
{
	Lock lock(*pMutex);
	return items.empty();
}

int PayloadPortSlave::ThreadSafeMap::Size(Key key) const
{
	Lock lock(*pMutex);
	MapType::const_iterator it = items.find(key);

	if (it == items.end())
		return 0;

	else
		return it->second->Size();
}

bool PayloadPortSlave::ThreadSafeMap::Empty(Key key) const
{
	Lock lock(*pMutex);
	MapType::const_iterator it = items.find(key);
	const bool result = (it == items.end()) || it->second->IsEmpty();

	return result;
}

PayloadPortSlave::ThreadSafeMap::Item PayloadPortSlave::ThreadSafeMap::Dequeue(Key key)
{
	Lock lock(*pMutex);
	MapType::iterator it = items.find(key);
	Item result;

	if (it != items.end())
		result = it->second->Read();

	return result;
}

bool PayloadPortSlave::ThreadSafeMap::TryDequeue(Key key, Item0& item)
{
	Lock lock(*pMutex);
	MapType::iterator it = items.find(key);

	if (it == items.end())
		return false;

	else
	{
		Queue::Lock queueLock(*it->second);

		if (it->second->IsEmpty())
			return false;
		else
		{
			MessageWrapper::Ptr wrapper = it->second->Read();
			wrapper->OnTransmit();
			item = wrapper->message;
			return true;
		}
	}
}

void PayloadPortSlave::ThreadSafeMap::Enqueue(Key key, const Item& item)
{
	Lock lock(*pMutex);
	MapType::iterator it = items.find(key);

	if (it == items.end())
	{
		Queue::Ptr qptr(new Queue());

		items[key] = qptr;
		qptr->WriteExpand(item);
	}
	else
		it->second->WriteExpand(item);
}

void PayloadPortSlave::ThreadSafeMap::Enqueue(Key key, const Item0& item)
{
	MessageWrapper::Ptr i;
	i.reset(new MessageWrapper(item));
	Enqueue(key, i);
}

void PayloadPortSlave::ThreadSafeMap::Flush()
{
	Lock lock(*pMutex);

	items.clear();
}

void PayloadPortSlave::ThreadSafeMap::Flush(Key key)
{
	Lock lock(*pMutex);
	MapType::iterator it = items.find(key);

	if (it != items.end())
	{
		Queue::Lock queueLock(*it->second);
		Queue temp;

		while(!it->second->IsEmpty())
			temp.Write(it->second->Read());

		queueLock.unlock();
		lock.unlock();

		while(temp.IsEmpty())
		{
			Queue::Item item = temp.Read();
			item->OnDiscard();
		}
	}
}

}
}
