namespace ThreadUtils {
	static void WaitAbortable(HANDLE ev, abort_callback & abort, DWORD timeout = INFINITE) {
		const HANDLE handles[2] = {ev, abort.get_abort_event()};
		SetLastError(0);
		const DWORD status = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
		switch(status) {
			case WAIT_OBJECT_0:
				break;
			case WAIT_OBJECT_0 + 1:
				throw exception_aborted();
			default:
				throw exception_win32(GetLastError());
		}
	}

	template<typename TWhat>
	class CObjectQueue {
	public:
		CObjectQueue() { m_event.create(true,false); }

		template<typename TSource> void Add(const TSource & source) {
			insync(m_sync);
			m_content.add_item(source);
			if (m_content.get_count() == 1) m_event.set_state(true);
		}
		template<typename TDestination> void Get(TDestination & out, abort_callback & abort) {
			WaitAbortable(m_event.get(), abort);
			insync(m_sync);
			pfc::const_iterator<TWhat> iter = m_content.first();
			pfc::dynamic_assert( iter.is_valid() );
			out = *iter;
			m_content.remove(iter);
			if (m_content.get_count() == 0) m_event.set_state(false);
		}

	private:
		win32_event m_event;
		critical_section m_sync;
		pfc::chain_list_v2_t<TWhat> m_content;
	};


	template<typename TBase>
	class CSingleThreadWrapper : protected pfc::thread {
	private:
		enum status {
			success,
			fail,
			fail_io,
			fail_io_data,
			fail_abort,
		};
	protected:
		class command {
		protected:
			command() : m_status(success), m_abort(), m_completionEvent() {}
			virtual void executeImpl(TBase &) {}
			virtual ~command() {}
		public:
			void execute(TBase & obj) {
				try {
					executeImpl(obj);
					m_status = success;
				} catch(exception_aborted const & e) {
					m_status = fail_abort; m_statusMsg = e.what();
				} catch(exception_io_data const & e) {
					m_status = fail_io_data; m_statusMsg = e.what();
				} catch(exception_io const & e) {
					m_status = fail_io; m_statusMsg = e.what();
				} catch(std::exception const & e) {
					m_status = fail; m_statusMsg = e.what();
				}
				SetEvent(m_completionEvent);
			}
			void rethrow() const {
				switch(m_status) {
					case fail:
						throw pfc::exception(m_statusMsg);
					case fail_io:
						throw exception_io(m_statusMsg);
					case fail_io_data:
						throw exception_io_data(m_statusMsg);
					case fail_abort:
						throw exception_aborted();
					case success:
						break;
					default:
						throw pfc::exception_bug_check_v2();
				}
			}
			status m_status;
			pfc::string8 m_statusMsg;
			HANDLE m_completionEvent;
			abort_callback * m_abort;
		};
		
		typedef pfc::rcptr_t<command> command_ptr;

		CSingleThreadWrapper() {		
			m_completionEvent.create(true,false);
			start();
		}

		~CSingleThreadWrapper() {
			m_threadAbort.abort();
			waitTillDone();
		}

		void invokeCommand(command_ptr cmd, abort_callback & abort) {
			abort.check();
			m_completionEvent.set_state(false);
			pfc::vartoggle_t<abort_callback*> abortToggle(cmd->m_abort, &abort);
			pfc::vartoggle_t<HANDLE> eventToggle(cmd->m_completionEvent, m_completionEvent.get() );
			m_commands.Add(cmd);
			m_completionEvent.wait_for(-1);
			//WaitAbortable(m_completionEvent.get(), abort);
			cmd->rethrow();
		}

	private:
		void threadProc() {
			try {
				TBase instance;
				for(;;) {
					command_ptr cmd;
					m_commands.Get(cmd, m_threadAbort);
					cmd->execute(instance);
				}
			} catch(...) {}
		}
		win32_event m_completionEvent;
		CObjectQueue<command_ptr> m_commands;
		abort_callback_impl m_threadAbort;
	};
}
