// channels.cpp - originally written and placed in the public domain by Wei Dai
//                CryptoPP::Test namespace added by JW in February 2017

#include "pch.h"

#ifndef CRYPTOPP_IMPORTS

#include "cryptlib.h"
#include "channels.h"

#if CRYPTOPP_MSC_VERSION
# pragma warning(disable: 4355)
#endif

NAMESPACE_BEGIN(CryptoPP)

#if 0
void MessageSwitch::AddDefaultRoute(BufferedTransformation &destination, const std::string &channel)
{
	m_defaultRoutes.push_back(Route(&destination, channel));
}

void MessageSwitch::AddRoute(unsigned int begin, unsigned int end, BufferedTransformation &destination, const std::string &channel)
{
	RangeRoute route(begin, end, Route(&destination, channel));
	RouteList::iterator it = upper_bound(m_routes.begin(), m_routes.end(), route);
	m_routes.insert(it, route);
}

/*
class MessageRouteIterator
{
public:
	typedef MessageSwitch::RouteList::const_iterator RouteIterator;
	typedef MessageSwitch::DefaultRouteList::const_iterator DefaultIterator;

	bool m_useDefault;
	RouteIterator m_itRouteCurrent, m_itRouteEnd;
	DefaultIterator m_itDefaultCurrent, m_itDefaultEnd;

	MessageRouteIterator(MessageSwitch &ms, const std::string &channel)
		: m_channel(channel)
	{
		std::pair<MapIterator, MapIterator> range = cs.m_routeMap.equal_range(channel);
		if (range.first == range.second)
		{
			m_useDefault = true;
			m_itListCurrent = cs.m_defaultRoutes.begin();
			m_itListEnd = cs.m_defaultRoutes.end();
		}
		else
		{
			m_useDefault = false;
			m_itMapCurrent = range.first;
			m_itMapEnd = range.second;
		}
	}

	bool End() const
	{
		return m_useDefault ? m_itListCurrent == m_itListEnd : m_itMapCurrent == m_itMapEnd;
	}

	void Next()
	{
		if (m_useDefault)
			++m_itListCurrent;
		else
			++m_itMapCurrent;
	}

	BufferedTransformation & Destination()
	{
		return m_useDefault ? *m_itListCurrent->first : *m_itMapCurrent->second.first;
	}

	const std::string & Message()
	{
		if (m_useDefault)
			return m_itListCurrent->second.get() ? *m_itListCurrent->second.get() : m_channel;
		else
			return m_itMapCurrent->second.second;
	}
};

void MessageSwitch::Put(byte inByte);
void MessageSwitch::Put(const byte *inString, unsigned int length);

void MessageSwitch::Flush(bool completeFlush, int propagation=-1);
void MessageSwitch::MessageEnd(int propagation=-1);
void MessageSwitch::PutMessageEnd(const byte *inString, unsigned int length, int propagation=-1);
void MessageSwitch::MessageSeriesEnd(int propagation=-1);
*/
#endif


//
// ChannelRouteIterator
//////////////////////////

void ChannelRouteIterator::Reset(const std::string &channel)
{
	m_channel = channel;
	std::pair<MapIterator, MapIterator> range = m_cs.m_routeMap.equal_range(channel);
	if (range.first == range.second)
	{
		m_useDefault = true;
		m_itListCurrent = m_cs.m_defaultRoutes.begin();
		m_itListEnd = m_cs.m_defaultRoutes.end();
	}
	else
	{
		m_useDefault = false;
		m_itMapCurrent = range.first;
		m_itMapEnd = range.second;
	}
}

bool ChannelRouteIterator::End() const
{
	return m_useDefault ? m_itListCurrent == m_itListEnd : m_itMapCurrent == m_itMapEnd;
}

void ChannelRouteIterator::Next()
{
	if (m_useDefault)
		++m_itListCurrent;
	else
		++m_itMapCurrent;
}

BufferedTransformation & ChannelRouteIterator::Destination()
{
	return m_useDefault ? *m_itListCurrent->first : *m_itMapCurrent->second.first;
}

const std::string & ChannelRouteIterator::Channel()
{
	if (m_useDefault)
		return m_itListCurrent->second.get() ? *m_itListCurrent->second.get() : m_channel;
	else
		return m_itMapCurrent->second.second;
}


//
// ChannelSwitch
///////////////////

size_t ChannelSwitch::ChannelPut2(const std::string &channel, const byte *begin, size_t length, int messageEnd, bool blocking)
{
	if (m_blocked)
	{
		m_blocked = false;
		goto WasBlocked;
	}

	m_it.Reset(channel);

	while (!m_it.End())
	{
WasBlocked:
		if (m_it.Destination().ChannelPut2(m_it.Channel(), begin, length, messageEnd, blocking))
		{
			m_blocked = true;
			return 1;
		}

		m_it.Next();
	}

	return 0;
}

void ChannelSwitch::IsolatedInitialize(const NameValuePairs& parameters)
{
	CRYPTOPP_UNUSED(parameters);
	m_routeMap.clear();
	m_defaultRoutes.clear();
	m_blocked = false;
}

bool ChannelSwitch::ChannelFlush(const std::string &channel, bool completeFlush, int propagation, bool blocking)
{
	if (m_blocked)
	{
		m_blocked = false;
		goto WasBlocked;
	}

	m_it.Reset(channel);

	while (!m_it.End())
	{
	  WasBlocked:
		if (m_it.Destination().ChannelFlush(m_it.Channel(), completeFlush, propagation, blocking))
		{
			m_blocked = true;
			return true;
		}

		m_it.Next();
	}

	return false;
}

bool ChannelSwitch::ChannelMessageSeriesEnd(const std::string &channel, int propagation, bool blocking)
{
	CRYPTOPP_UNUSED(blocking);
	if (m_blocked)
	{
		m_blocked = false;
		goto WasBlocked;
	}

	m_it.Reset(channel);

	while (!m_it.End())
	{
	  WasBlocked:
		if (m_it.Destination().ChannelMessageSeriesEnd(m_it.Channel(), propagation))
		{
			m_blocked = true;
			return true;
		}

		m_it.Next();
	}

	return false;
}

byte * ChannelSwitch::ChannelCreatePutSpace(const std::string &channel, size_t &size)
{
	m_it.Reset(channel);
	if (!m_it.End())
	{
		BufferedTransformation &target = m_it.Destination();
		const std::string &ch = m_it.Channel();
		m_it.Next();
		if (m_it.End())	// there is only one target channel
			return target.ChannelCreatePutSpace(ch, size);
	}
	size = 0;
	return NULLPTR;
}

size_t ChannelSwitch::ChannelPutModifiable2(const std::string &channel, byte *inString, size_t length, int messageEnd, bool blocking)
{
	ChannelRouteIterator it(*this);
	it.Reset(channel);

	if (!it.End())
	{
		BufferedTransformation &target = it.Destination();
		const std::string &targetChannel = it.Channel();
		it.Next();
		if (it.End())	// there is only one target channel
			return target.ChannelPutModifiable2(targetChannel, inString, length, messageEnd, blocking);
	}

	return ChannelPut2(channel, inString, length, messageEnd, blocking);
}

void ChannelSwitch::AddDefaultRoute(BufferedTransformation &destination)
{
	m_defaultRoutes.push_back(DefaultRoute(&destination, value_ptr<std::string>(NULLPTR)));
}

void ChannelSwitch::RemoveDefaultRoute(BufferedTransformation &destination)
{
	for (DefaultRouteList::iterator it = m_defaultRoutes.begin(); it != m_defaultRoutes.end(); ++it)
		if (it->first == &destination && !it->second.get())
		{
			m_defaultRoutes.erase(it);
			break;
		}
}

void ChannelSwitch::AddDefaultRoute(BufferedTransformation &destination, const std::string &outChannel)
{
	m_defaultRoutes.push_back(DefaultRoute(&destination, outChannel));
}

void ChannelSwitch::RemoveDefaultRoute(BufferedTransformation &destination, const std::string &outChannel)
{
	for (DefaultRouteList::iterator it = m_defaultRoutes.begin(); it != m_defaultRoutes.end(); ++it)
		if (it->first == &destination && (it->second.get() && *it->second == outChannel))
		{
			m_defaultRoutes.erase(it);
			break;
		}
}

void ChannelSwitch::AddRoute(const std::string &inChannel, BufferedTransformation &destination, const std::string &outChannel)
{
	m_routeMap.insert(RouteMap::value_type(inChannel, Route(&destination, outChannel)));
}

void ChannelSwitch::RemoveRoute(const std::string &inChannel, BufferedTransformation &destination, const std::string &outChannel)
{
	typedef ChannelSwitch::RouteMap::iterator MapIterator;
	std::pair<MapIterator, MapIterator> range = m_routeMap.equal_range(inChannel);

	for (MapIterator it = range.first; it != range.second; ++it)
		if (it->second.first == &destination && it->second.second == outChannel)
		{
			m_routeMap.erase(it);
			break;
		}
}

NAMESPACE_END

#endif
