/*
 *  ppui/osinterface/sdl/SDL_ModalLoop.cpp
 *
 *  Copyright 2009 Peter Barth
 *
 *  This file is part of Milkytracker.
 *
 *  Milkytracker 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  Milkytracker 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 Milkytracker.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/*
 *  SDL_ModalLoop.cpp
 *  MilkyTracker
 *
 *  Created by Peter Barth on 29.03.06.
 *
 */

#include <SDL.h>
#include "SDL_ModalLoop.h"
#include "Event.h"
#include "Screen.h"
#include "DialogBase.h"
#include "PPMutex.h"

void processSDLEvents(const SDL_Event& event);
void processSDLUserEvents(const SDL_UserEvent& event);

extern PPMutex* globalMutex;

/////////////////////////////////////////////////////////////////
//
// Okay here goes a quick description of how a modal dialog
// using the SDL is realized:
// So when this modal dialog is invoked, we ARE actually between
// two globalLoop mutex calls, because we were invoked
// by some mouse click or other event
// So now we first attach a new event listener to our screen 
// object (so that all events sent end up in our own listener)
// and install our own event handler loop... But this can only
// be done when the globalMutex is unlocked first and locked 
// after...
//
/////////////////////////////////////////////////////////////////
PPModalDialog::ReturnCodes SDL_runModalLoop(PPScreen* screen, PPDialogBase* dialog)
{
	bool exitModalLoop = false;
	SDL_Event event;

	// screen might be disabled in a stackable fashion
	pp_uint32 screenEnableStackCount = 0;
	while (!screen->isDisplayEnabled())
	{
		screen->enableDisplay(true);
		screenEnableStackCount++;
	}

	// This is the responder for buttons invoked by the modal dialog
	ModalLoopResponder modalLoopResponder(exitModalLoop);

	dialog->setResponder(&modalLoopResponder);

	// Detach the event listener from the screen, this will actually detach the entire tracker from the screen
	EventListenerInterface* eventListener = screen->detachEventListener();

	// Instantinate our own event listener
	CustomEventListener customEventListener(eventListener);
	
	// Attach it
	screen->attachEventListener(&customEventListener);

	// Show it
	dialog->show();

	// Now to the tricky part, since a modal dialog has been invoked through a OS event, globalMutex is in locked state
	// so to allow further event processing we must unlock the mutex first
	// -- really messy and critical --
	if (globalMutex)
		globalMutex->unlock();

	// Create our own event loop
	while (!exitModalLoop && SDL_WaitEvent(&event)) 
	{
		switch (event.type) 
		{
			case SDL_MOUSEMOTION:
			{
				// ignore old mouse motion events in the event queue
				SDL_Event new_event;
				
				if (SDL_PeepEvents(&new_event, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_MOUSEMOTION)) > 0) 
				{
					while (SDL_PeepEvents(&new_event, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_MOUSEMOTION)) > 0);
					processSDLEvents(new_event);
				} 
				else 
				{
					processSDLEvents(event);
				}
				break;
			}
			
			case SDL_USEREVENT:
				processSDLUserEvents((const SDL_UserEvent&)event);
				break;
			
			default:
				processSDLEvents(event);
				break;
		}
	}	

	// pretend nothing happened at all, continue with main event loop after we're finished here
	if (globalMutex)
		globalMutex->lock();

	// re-attach tracker
	screen->attachEventListener(eventListener);	

	// if screen was disabled we enable it again
	while (screenEnableStackCount > 0)
	{
		screen->enableDisplay(false);
		screenEnableStackCount--;
	}
	
	return modalLoopResponder.getReturnCode();
}
