//-----------------------------------------------------------------------------
//
//	Wait.cpp
//
//	Base class for objects we want to be able to wait for.
//
//	Copyright (c) 2010 Mal Lansell <mal@lansell.org>
//	All rights reserved.
//
//	SOFTWARE NOTICE AND LICENSE
//
//	This file is part of OpenZWave.
//
//	OpenZWave is free software: you can redistribute it and/or modify
//	it under the terms of the GNU Lesser General Public License as published
//	by the Free Software Foundation, either version 3 of the License,
//	or (at your option) any later version.
//
//	OpenZWave 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 Lesser General Public License for more details.
//
//	You should have received a copy of the GNU Lesser General Public License
//	along with OpenZWave.  If not, see <http://www.gnu.org/licenses/>.
//
//-----------------------------------------------------------------------------
#include <stdio.h>
#include "Defs.h"
#include "platform/Wait.h"
#include "platform/Event.h"
#include "platform/Log.h"

#ifdef WIN32
#include "platform/windows/WaitImpl.h"	// Platform-specific implementation of a Wait object
#elif defined WINRT
#include "platform/winRT/WaitImpl.h"	// Platform-specific implementation of a Wait object
#else
#include "platform/unix/WaitImpl.h"	// Platform-specific implementation of a Wait object
#endif

namespace OpenZWave
{
	namespace Internal
	{
		namespace Platform
		{

			void WaitMultipleCallback(void* _context);

//-----------------------------------------------------------------------------
//	<Wait::Wait>
//	Constructor
//-----------------------------------------------------------------------------
			Wait::Wait()
			{
				m_pImpl = new WaitImpl(this);
			}

//-----------------------------------------------------------------------------
//	<Wait::~Wait>
//	Destructor
//-----------------------------------------------------------------------------
			Wait::~Wait()
			{
				delete m_pImpl;
			}

//-----------------------------------------------------------------------------
//	<Wait::AddWatcher>
//	Add a watcher to our object.
//-----------------------------------------------------------------------------
			void Wait::AddWatcher(pfnWaitNotification_t _callback, void* _context)
			{
				if (!_callback)
				{
					assert(0);
					return;
				}

				// Add a ref so our object cannot disappear while being watched
				AddRef();

				// Add the watcher (platform specific code required here for thread safety)
				m_pImpl->AddWatcher(_callback, _context);
			}

//-----------------------------------------------------------------------------
//	<Wait::RemoveWatcher>
//	Remove a watcher from our object.
//-----------------------------------------------------------------------------
			void Wait::RemoveWatcher(pfnWaitNotification_t _callback, void* _context)
			{
				if (m_pImpl->RemoveWatcher(_callback, _context))
				{
					Release();
				}
			}

//-----------------------------------------------------------------------------
//	<Wait::Notify>
//	Notify all the watchers that the object has become signalled
//-----------------------------------------------------------------------------
			void Wait::Notify()
			{
				m_pImpl->Notify();
			}

//-----------------------------------------------------------------------------
//	<Wait::Multiple>
//	Wait for one of multiple objects to become signalled.
//-----------------------------------------------------------------------------
			int32 Wait::Multiple(Wait** _objects, uint32 _numObjects, int32 _timeout // = -1
					)
			{
				uint32 i;

				// Create an event that will be set when any of the objects in the list becomes signalled.
				Event* waitEvent = new Event();

				// Add a watcher to each object in the list, passing in the event as the context.
				for (i = 0; i < _numObjects; ++i)
				{
//		Log::Write( LogLevel_Info, "Wait::Multiple - Object %p %d", _objects[i], _objects[i]->IsSignalled());
					_objects[i]->AddWatcher(WaitMultipleCallback, waitEvent);
				}

				int32 res = -1;	// Default to timeout result
				string str = "";
				if (waitEvent->Wait(_timeout))
				{
					// An object was signalled.  Run through the list
					// and see which one it was.
					for (i = 0; i < _numObjects; ++i)
					{
						if (_objects[i]->IsSignalled())
						{
							if (res == -1)
								res = (int32) i;
							char buf[15];
							snprintf(buf, sizeof(buf), "%d, ", i);
							str += buf;
						}
					}
				}
//	Log::Write( LogLevel_Debug, "Wait::Multiple res=%d num=%d >%s", res, _numObjects, str.c_str() );

				// Remove the watchers
				for (i = 0; i < _numObjects; ++i)
				{
					_objects[i]->RemoveWatcher(WaitMultipleCallback, waitEvent);
				}

				// We're done with the event now
				waitEvent->Release();
				return res;
			}

//-----------------------------------------------------------------------------
//	<WaitMultipleCallback>
//	Callback handler for the watchers added during WaitImpl::Multiple
//-----------------------------------------------------------------------------
			void WaitMultipleCallback(void* _context)
			{
				Event* waitEvent = (Event*) _context;
				waitEvent->Set();
			}
		} // namespace Platform
	} // namespace Internal
} // namespace OpenZWave
