#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>

#include "GHOST_Event.h"
#include "GHOST_DisplayManagerZETA.h"
#include "GHOST_SystemZETA.h"
#include "GHOST_TimerManager.h"
#include "GHOST_WindowZETA.h"
#include "GHOST_WindowManager.h"

GHOST_SystemZETA::GHOST_SystemZETA()
	: GHOST_System()
{
	GHOST_PRINT( "ZETA: GHOST_SystemZETA::GHOST_SystemZETA()\n" );
	
	fBApplication = NULL;
	
	fStartTime = ( GHOST_TUns64 )( real_time_clock_usecs() / 1000 );
}

/*
 * GHOST_ISystem methods for GHOST_SystemZETA
 */

GHOST_IWindow*
GHOST_SystemZETA::createWindow(
	const STR_String& title,
	GHOST_TInt32 left,
	GHOST_TInt32 top,
	GHOST_TUns32 width,
	GHOST_TUns32 height,
	GHOST_TWindowState state,
	GHOST_TDrawingContextType type,
	const bool stereoVisual)
{
	GHOST_PRINT( "ZETA: GHOST_SystemZETA::createWindow()\n" );
	
	// Create the window
	GHOST_WindowZETA* window = NULL;
	
	// We may not create a BWindow without BApplication object.
	if( fBApplication == NULL )
	{
		GHOST_PRINT( "ZETA: No valid BApplication object instantiated!\n" );
		return NULL;
	}
	
	window = new GHOST_WindowZETA(
		this, title, left, top, width, height, state, type, stereoVisual );
		
	if( window )
	{
		if( window->getValid() )
		{
			GHOST_PRINT( "ZETA: -> Window created successfully!\n" );
			
			// Store the pointer to the window 
            GHOST_ASSERT( m_windowManager, "m_windowManager not initialized" );
            m_windowManager->addWindow( window );
            m_windowManager->setActiveWindow( window );
            pushEvent( new GHOST_Event( getMilliSeconds(), GHOST_kEventWindowSize, window ) );
        }
        else
		{
			GHOST_PRINT( "ZETA: -> Window not valid! Exiting!\n" );
			delete window;
			window = NULL;
		}
	}
	else
		GHOST_PRINT( "ZETA: -> Error creating window! Exiting!\n" );
	
	return window;
}

GHOST_TSuccess
GHOST_SystemZETA::getCursorPosition(
	GHOST_TInt32& x,
	GHOST_TInt32& y ) const
{
	GHOST_PRINT( "ZETA: GHOST_SystemZETA::getCursorPosition()\n" );
	GHOST_PRINT( "ZETA: -> Not Implemented!\n" );

	/*
	 * TODO
	 * I got actually no idea from where to query the cursor position.
	 * Do nothing for now.
	 */
	
	return GHOST_kSuccess;
}

void
GHOST_SystemZETA::getMainDisplayDimensions(
	GHOST_TUns32& width,
	GHOST_TUns32& height) const
{
	GHOST_PRINT( "ZETA: GHOST_SystemZETA::getMainDisplayDimensions()\n" );

	// Double work. Done in GHOST_DisplayManagerZETA, too. So grab it from there.
	
	GHOST_DisplaySetting dsetting;
	
	fDisplayManagerZETA->getDisplaySetting(
		( GHOST_TUns8 )0,
		( GHOST_TInt32 )0,
		dsetting );
	
	width = dsetting.xPixels;
	height = dsetting.yPixels;
}

GHOST_TUns8
GHOST_SystemZETA::getNumDisplays() const
{
	GHOST_PRINT( "ZETA: GHOST_SystemZETA::getNumDisplays()\n" );
	
	// ZETA for now only supports one Display
	return GHOST_TUns8( 1 );
}

bool
GHOST_SystemZETA::processEvents(
	bool waitForEvent )
{
	/*
	 * yes you read right. no idea what I should do here. read some comments below.
	 * processing nothing in here, doesn't seem to hurt. nevertheless, code stays for
	 * revisit purposes. Michael.
	 */
	
	return true;
	
	bool anyProcessed = false;
	
	do
	{
		GHOST_TimerManager* timerMgr = getTimerManager();
		
		if( waitForEvent )
		{
			GHOST_TUns64 next = timerMgr->nextFireTime();
			
			if( next == GHOST_kFireTimeNever )
			{
				/* No idea whatever needs to be done here instead. Sleep 10 milliseconds. */
				snooze( 10000 );
			}
			else
			{
				bigtime_t wakeup_time = ( bigtime_t )( ( next - getMilliSeconds() ) * 1000 );
				snooze_until( wakeup_time, B_SYSTEM_TIMEBASE );
			}
		}
		
		if( timerMgr->fireTimers( getMilliSeconds() ) )
		{
			anyProcessed = true;
		}
		
		/*
		 * As we pushEvent() all the events in GHOSTBDirectWindow::DispatchMessage(),
		 * there is no need to check the windows for events here. Or?
		 * I don't know if GHOST itself puts events into the event queue of a GHOST window.
		 * At least for myself I don't. :) Errrr, is there any at all? Haven't seen sth like that?!
		 *		
		 * For now, we just iterate through the windows, and ask the BDirectWindow if
		 * the view in it needs an update. If yes, we fire a WindowUpdate event.
		 */
		
		anyProcessed = true;
	}
	while( waitForEvent && !anyProcessed );
	
	return anyProcessed;
}

GHOST_TSuccess
GHOST_SystemZETA::setCursorPosition(
	GHOST_TInt32 x,
	GHOST_TInt32 y ) const
{
	GHOST_PRINT( "ZETA: GHOST_SystemZETA::setCursorPosition()\n" );
	GHOST_PRINT( "ZETA: -> Not implemented!\n" );	
	
	// TODO
	
	// No idea how to do that.	
	return GHOST_kSuccess;
}

/*
 * GHOST_System methods for GHOST_SystemZETA
 */

GHOST_TSuccess
GHOST_SystemZETA::exit()
{
	GHOST_PRINT( "ZETA: GHOST_SystemZETA::exit()\n" );
	
	// Hm. Anything else to be done here?	
	return GHOST_System::exit();
}

GHOST_TSuccess
GHOST_SystemZETA::getButtons(
	GHOST_Buttons& buttons ) const
{
	GHOST_PRINT( "ZETA: GHOST_SystemZETA::getButtons()\n" );
	GHOST_PRINT( "ZETA: -> Not implemented!\n" );	
	
	// What do I need to do here exactly?
	return GHOST_kSuccess;
}

GHOST_TUns64
GHOST_SystemZETA::getMilliSeconds() const
{
	return ( ( GHOST_TUns64 )( real_time_clock_usecs() / 1000 ) - fStartTime );
}

GHOST_TSuccess
GHOST_SystemZETA::getModifierKeys(
	GHOST_ModifierKeys& keys ) const
{
	GHOST_PRINT( "ZETA: GHOST_SystemZETA::getModifierKeys()\n" );
	
	/* I can't recall if this is in a working state. Michael */
		
	uint32 mods = modifiers();
	
	/* what? command or option? just went to map command key to alt */
	
	if( mods & B_LEFT_SHIFT_KEY )
	{
		keys.set( GHOST_kModifierKeyLeftShift, true );
	}
	else
		keys.set( GHOST_kModifierKeyLeftShift, false );

	if( mods & B_RIGHT_SHIFT_KEY )
	{
		keys.set( GHOST_kModifierKeyRightShift, true );
	}
	else
		keys.set( GHOST_kModifierKeyRightShift, false );

	if( mods & B_LEFT_CONTROL_KEY )
	{
		keys.set( GHOST_kModifierKeyLeftControl, true );
	}
	else
		keys.set( GHOST_kModifierKeyLeftControl, false );

	if( mods & B_RIGHT_CONTROL_KEY )
	{
		keys.set( GHOST_kModifierKeyRightControl, true );
	}
	else
		keys.set( GHOST_kModifierKeyRightControl, false );

	if( mods & B_LEFT_OPTION_KEY )
	{
		keys.set( GHOST_kModifierKeyLeftAlt, true );
	}
	else
		keys.set( GHOST_kModifierKeyLeftAlt, false );

	if( mods & B_RIGHT_OPTION_KEY )
	{
		keys.set( GHOST_kModifierKeyRightAlt, true );
	}
	else
		keys.set( GHOST_kModifierKeyRightAlt, false );
	
	return GHOST_kSuccess;
}

GHOST_TSuccess
GHOST_SystemZETA::init()
{
	GHOST_PRINT( "ZETA: GHOST_SystemZETA::init()\n" );

	status_t status = InitBApplicationObject();
	if( status != B_OK )
	{
		GHOST_PRINT( "ZETA: -> Error initializing the BApplication object. Exiting!\n" );
	}
	else
		GHOST_PRINT( "ZETA: -> Initialized BApplication object.\n" );

	GHOST_TSuccess success = GHOST_System::init();

	if( success )
	{
		fDisplayManagerZETA = new GHOST_DisplayManagerZETA( this );
		
		if( ( status == B_OK ) && fDisplayManagerZETA )
			return GHOST_kSuccess;
	}
	
	return GHOST_kFailure;
}

/*
 * ZETA methods for GHOST_SystemZETA
 */

static int32
bAppThread(
	void* arg )
{
	be_app->Lock();
	return be_app->Run();
}

status_t
GHOST_SystemZETA::InitBApplicationObject()
{
	GHOST_PRINT( "ZETA: GHOST_SystemZETA::InitBApplicationObject()\n" );
	
	// don't generate another BApplication object, when there is already one.
	if( be_app )
		return B_OK;
		
	// Create the BApplication object.
	fBApplication = new BApplication( "application/x-vnd.GHOST-framework-BApplication" );
	
	thread_id appthread = spawn_thread( bAppThread, "GHOST_BApplication", B_NORMAL_PRIORITY, 0 );
	if( appthread == B_NO_MORE_THREADS )
	{
		GHOST_PRINT( "ZETA: -> All thread_id numbers are currently in use.\n" );
		return B_ERROR;
	}
	if( appthread == B_NO_MEMORY )
	{
		GHOST_PRINT( "ZETA: -> Not enough memory to allocate the resources for another thread.\n" );
		return B_ERROR;
	}
	
	// As the thread is suspended after being spawned, resume it.
	resume_thread( appthread );
	
	return B_OK;
}
