////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include <Carbon/Carbon.h>
#include <Cocoa/Cocoa.h>

#include "osxGraphicsWindow.h"
#include "config_osxdisplay.h"
#include "osxGraphicsPipe.h"
#include "pStatTimer.h"
#include "glgsg.h"
#include "keyboardButton.h"
#include "mouseButton.h"
#include "osxGraphicsStateGuardian.h"
#include "osxGraphicsPipe.h"
#include "throw_event.h"
#include "pnmImage.h"
#include "virtualFileSystem.h"
#include "config_util.h"

#include <OpenGL/gl.h>
#include <AGL/agl.h>
#include <ApplicationServices/ApplicationServices.h>

////////////////////////// Global Objects .....
TypeHandle osxGraphicsWindow::_type_handle;
osxGraphicsWindow  * osxGraphicsWindow::FullScreenWindow = NULL;


////////////////////////////////////////////////////////////////////
//     Function: GetCurrentOSxWindow
//       Access: Static,
//  Description: How to find the active window for events  on osx..
//
////////////////////////////////////////////////////////////////////
osxGraphicsWindow * osxGraphicsWindow::GetCurrentOSxWindow (WindowRef window)
{
	if(FullScreenWindow != NULL)
	{
//	 cerr << " Full Screen \n";
	   return FullScreenWindow;
   }
//	 cerr << "Not Full Screen \n";

	if (NULL == window)  // HID use this path
	{
		//	Assume first we are a child window. If we cant find a window of that class, then we
		//	are standalone and can jsut grab the front window.
		window	=	GetFrontWindowOfClass(kSimpleWindowClass, TRUE);
		if (NULL == window)
			window	=	FrontNonFloatingWindow();
	}
		
	if (window)
		return (osxGraphicsWindow *) GetWRefCon (window);
	else
		return NULL;
}

////////////////////////////////////////////////////////////////////
//     Function: aglReportError
//       Access: public
//  Description: Helper function for AGL error message and Grabing error code if any
//
////////////////////////////////////////////////////////////////////
OSStatus aglReportError ( const std::string  &comment)
{
	GLenum err = aglGetError();
	if (err != AGL_NO_ERROR ) 
		 osxdisplay_cat.error()<<"AGL Error " <<  aglErrorString(err) << "[" <<comment <<"]\n";

	if (err == AGL_NO_ERROR)
		return noErr;
	else
		return (OSStatus) err;
}
////////////////////////////////////////////////////////////////////
//     Function: InvertGLImage
//       Access: file scopre, static
//  Description: Helper function invertiung a gl image
//
////////////////////////////////////////////////////////////////////
static void InvertGLImage( char *imageData, size_t imageSize, size_t rowBytes )
{
	size_t i, j;
	char *tBuffer = (char*) malloc (rowBytes);
	if (NULL == tBuffer) return;
		
	// Copy by rows through temp buffer
	for (i = 0, j = imageSize - rowBytes; i < imageSize >> 1; i += rowBytes, j -= rowBytes) {
		memcpy( tBuffer, &imageData[i], rowBytes );
		memcpy( &imageData[i], &imageData[j], rowBytes );
		memcpy( &imageData[j], tBuffer, rowBytes );
	}
	free(tBuffer);
}

////////////////////////////////////////////////////////////////////
//     Function: CompositeGLBufferIntoWindow
//       Access: file scopre, static
//  Description: Drop a Gl overlay onto a carbon window.. 
//
////////////////////////////////////////////////////////////////////
static void CompositeGLBufferIntoWindow (AGLContext ctx, Rect *bufferRect, GrafPtr out_port)
{
	GWorldPtr pGWorld;
	QDErr err;
	// blit OpenGL content into window backing store
	// allocate buffer to hold pane image
	long width  = (bufferRect->right - bufferRect->left);
	long height  = (bufferRect->bottom - bufferRect->top);
	
	
	Rect src_rect = {0, 0, height, width};
	Rect ddrc_rect = {0, 0, height, width};
	long rowBytes = width * 4;
	long imageSize = rowBytes * height;
	char *image = (char *) NewPtr (imageSize);
	if (!image) {
		osxdisplay_cat.error() << "Out of memory in CompositeGLBufferIntoWindow()!\n";
		return;		// no harm in continuing
	}
	// pull GL content down to our image buffer
	aglSetCurrentContext( ctx );
	glReadPixels (0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, image);

	// GL buffers are upside-down relative to QD buffers, so we need to flip it
	InvertGLImage( image, imageSize, rowBytes );

	// create a GWorld containing our image
	err = NewGWorldFromPtr (&pGWorld, k32ARGBPixelFormat, &src_rect, 0, 0, 0, image, rowBytes);
	if (err != noErr) {
		osxdisplay_cat.error() << " error in NewGWorldFromPtr, called from CompositeGLBufferIntoWindow()\n";
		free( image );
		return;
	}

	GrafPtr portSave 	= NULL;
	Boolean	portChanged	=	QDSwapPort(out_port, &portSave);

	CopyBits( GetPortBitMapForCopyBits (pGWorld), GetPortBitMapForCopyBits (out_port), &src_rect, &ddrc_rect, srcCopy, 0 );
	
	if (portChanged)
		QDSwapPort(portSave, NULL);

	DisposeGWorld( pGWorld );
	DisposePtr ( image );
}

////////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::event_handler
//       Access: Public
//  Description: The standard window event handler for non-fullscreen
//               windows.
////////////////////////////////////////////////////////////////////
OSStatus osxGraphicsWindow::event_handler(EventHandlerCallRef myHandler, EventRef event)
{
  OSStatus result = eventNotHandledErr;
  UInt32 the_class = GetEventClass(event);
  UInt32 kind = GetEventKind(event);

  WindowRef window = NULL;		
  GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &window);

  if (osxdisplay_cat.is_spam()) {
    osxdisplay_cat.spam()
      << ClockObject::get_global_clock()->get_real_time() 
      << " event_handler: " << (void *)this << ", " << window << ", "
      << the_class << ", " << kind << "\n";
  }

  switch (the_class) {
  case kEventClassMouse:
    result  = handleWindowMouseEvents (myHandler, event);
    break;
	
  case kEventClassWindow:	
    switch (kind) {
    case kEventWindowCollapsing:
      /*
      Rect r;
      GetWindowPortBounds (window, &r);					
      CompositeGLBufferIntoWindow( get_context(), &r, GetWindowPort (window));
      UpdateCollapsedWindowDockTile (window);
      */
      SystemSetWindowForground(false);
      break;
    case kEventWindowActivated: // called on click activation and initially
      SystemSetWindowForground(true);
      DoResize();
      break;
    case kEventWindowDeactivated:
      SystemSetWindowForground(false);
      break;
    case kEventWindowClose: // called when window is being closed (close box)
      // This is a message from the window manager indicating that
      // the user has requested to close the window.
      user_close_request();
      result = noErr;
      break;
    case kEventWindowShown: // called on initial show (not on un-minimize)
      if (window == FrontWindow ())
        SetUserFocusWindow (window);
      break;
    case kEventWindowBoundsChanged: // called for resize and moves (drag)
      DoResize();
      break;
    case kEventWindowZoomed:
      break;
    case kEventWindowCollapsed:
      {
        WindowProperties properties;
        properties.set_minimized(true);
        system_changed_properties(properties);
      }
      break;
    case kEventWindowExpanded:
      {
        WindowProperties properties;
        properties.set_minimized(false);
        system_changed_properties(properties);
      }
      break;
    }
    break;
  }

  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::user_close_request
//       Access: Private
//  Description: The user has requested to close the window, for
//               instance with Cmd-W, or by clicking on the close
//               button.
////////////////////////////////////////////////////////////////////
void osxGraphicsWindow::user_close_request()
{
  string close_request_event = get_close_request_event();
  if (!close_request_event.empty()) {
    // In this case, the app has indicated a desire to intercept
    // the request and process it directly.
    throw_event(close_request_event);
    
  } else {
    // In this case, the default case, the app does not intend
    // to service the request, so we do by closing the window.
    close_window();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::SystemCloseWindow
//       Access: private
//  Description: The Windows is closed by a OS resource not by a internal request 
//
////////////////////////////////////////////////////////////////////
void osxGraphicsWindow::SystemCloseWindow()
{	
 if (osxdisplay_cat.is_debug())	
	osxdisplay_cat.debug() << "System Closing Window \n";
	ReleaseSystemResources();	
};

////////////////////////////////////////////////////////////////////
//     Function: windowEvtHndlr
//       Access: file scope static
//  Description: The C callback for Window Events ..
//
//  We only hook this up for non fullscreen window... so we only
//  handle system window events..
//        
////////////////////////////////////////////////////////////////////
static pascal OSStatus 
windowEvtHndlr(EventHandlerCallRef myHandler, EventRef event, void *userData) {
#pragma unused (userData)
 
  WindowRef window = NULL;		
  GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &window);

  if (window != NULL) {
    osxGraphicsWindow *osx_win = osxGraphicsWindow::GetCurrentOSxWindow(window);
    if (osx_win != (osxGraphicsWindow *)NULL) {
      return osx_win->event_handler(myHandler, event);
    }
  }

  return eventNotHandledErr;
}

///////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::DoResize
//       Access: 
//  Description: The C callback for Window Events ..
//
//   We only hook this up for none fullscreen window... so we only handle system window events..
//        
////////////////////////////////////////////////////////////////////
void     osxGraphicsWindow::DoResize(void)
{
	osxdisplay_cat.debug() << "In Resize Out....." << _properties << "\n";
    // only in window mode .. not full screen
    if(_osx_window != NULL && _is_fullscreen == false && _properties.has_size())
    {
        Rect				rectPort = {0,0,0,0};
        CGRect 				viewRect = {{0.0f, 0.0f}, {0.0f, 0.0f}};

        GetWindowPortBounds (_osx_window, &rectPort); 
        viewRect.size.width = (float) (rectPort.right - rectPort.left);
        viewRect.size.height = (float) (rectPort.bottom - rectPort.top);
        // tell panda
        WindowProperties properties;
        properties.set_size((int)viewRect.size.width,(int)viewRect.size.height);
        properties.set_origin((int) rectPort.left,(int)rectPort.top);
        system_changed_properties(properties);
		
		if (osxdisplay_cat.is_debug())	
			osxdisplay_cat.debug() << " Resizing Window " << viewRect.size.width << " " << viewRect.size.height << "\n";

		//cerr << " Resizing Window " << viewRect.size.width << " " << viewRect.size.height << "\n";

        // ping gl
        aglUpdateContext (aglGetCurrentContext());
        aglReportError("aglUpdateContext .. This is a Resize..");
    }						
};

///////////////////////////////////////////////////////////////////
//     Function: appEvtHndlr
//       Access: 
//  Description: The C callback for APlication Events..
//
//    Hooked  once for application
//        
////////////////////////////////////////////////////////////////////
static pascal OSStatus appEvtHndlr (EventHandlerCallRef myHandler, EventRef event, void* userData)
{
#pragma unused (myHandler)

//	cerr << " In appEvtHandler \n";

    OSStatus result = eventNotHandledErr;
    osxGraphicsWindow *osx_win = NULL;
    WindowRef window = NULL;	
    UInt32 the_class = GetEventClass (event);
    UInt32 kind = GetEventKind (event);

	GetEventParameter(event, kEventParamWindowRef,
                        typeWindowRef, NULL, sizeof(WindowRef),
                        NULL, (void*) &window);

     osx_win = osxGraphicsWindow::GetCurrentOSxWindow(window);

    if(osx_win == NULL)
	  return eventNotHandledErr;
	  
	switch (the_class) {
        case kEventClassTextInput:
			  if(kind == kEventTextInputUnicodeForKeyEvent)
				osx_win->handleTextInput(myHandler, event);
			  //result = noErr; 
				//
				// can not report handled .. the os will not sent the raw key strokes then 
				//	if(osx_win->handleTextInput(myHandler, event) == noErr)
				//	      result = noErr;
			  break;
		case kEventClassKeyboard:
			{
			switch (kind) {
                                case kEventRawKeyRepeat:
				case kEventRawKeyDown:
					result = osx_win->handleKeyInput  (myHandler, event, true);
					break;
				case kEventRawKeyUp:
					result = osx_win->handleKeyInput  (myHandler, event, false);
					break;
				case kEventRawKeyModifiersChanged:
					{
						UInt32 newModifiers;
						OSStatus error = GetEventParameter(event, kEventParamKeyModifiers,typeUInt32, NULL,sizeof(UInt32), NULL, &newModifiers);
						if(error == noErr)			
						{			   
							osx_win->HandleModifireDeleta(newModifiers);
							result = noErr; 	
						  }
					}
					break;
			   }
			}
			break;

		case kEventClassMouse:
			//if(osxGraphicsWindow::FullScreenWindow != NULL)	
				result = osx_win->handleWindowMouseEvents (myHandler, event);
 		    //result = noErr; 
			break;
	}
    return result;
}

///////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::handleTextInput
//       Access: 
//  Description:  Trap Unicode  Input.
//
//        
////////////////////////////////////////////////////////////////////
OSStatus osxGraphicsWindow::handleTextInput (EventHandlerCallRef myHandler, EventRef theTextEvent)
{
	UniChar      *text = NULL;
	UInt32       actualSize = 0; 
 
	OSStatus ret = GetEventParameter (theTextEvent, kEventParamTextInputSendText, typeUnicodeText, NULL, 0, &actualSize, NULL);
	if(ret != noErr)
	   return ret;
 
	text = (UniChar*) NewPtr(actualSize);
	if(text!= NULL)
	{
 
		ret = GetEventParameter (theTextEvent, kEventParamTextInputSendText,typeUnicodeText, NULL, actualSize, NULL, text);
		if(ret != noErr)
			return ret;

		for(unsigned int x = 0; x < actualSize/sizeof(UniChar); ++x)
			_input_devices[0].keystroke(text[x]);	
		DisposePtr((char *)text);
	}
	
	return ret;
}
///////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::ReleaseSystemResources
//       Access: private..
//  Description: Clean up the OS level messes..
////////////////////////////////////////////////////////////////////
void osxGraphicsWindow::ReleaseSystemResources()
{
  if (_is_fullscreen)
  {
    _is_fullscreen = false;
    FullScreenWindow = NULL;
    
    if (_originalMode != NULL)
      CGDisplaySwitchToMode( kCGDirectMainDisplay, _originalMode );

    CGDisplayRelease( kCGDirectMainDisplay );
    aglSetDrawable (get_ggs_context(), NULL);
    
    _originalMode = NULL;
  }
	
  // if the ggs context is assigned to this window
  // clear it..
  if (_osx_window != NULL && GetWindowPort (_osx_window) == (GrafPtr)aglGetDrawable(get_ggs_context()))
    aglSetDrawable (get_ggs_context(),NULL);
  
  // if we are the active gl context clear it..		
  if(aglGetCurrentContext() == get_ggs_context())
    aglSetCurrentContext (NULL);

  if(_osx_window != NULL)
  {
    SetWRefCon (_osx_window, (long int) NULL);
    HideWindow (_osx_window);
    DisposeWindow(_osx_window);
    _osx_window = NULL;
  }
						
  if (_holder_aglcontext) {
    aglDestroyContext (_holder_aglcontext);		
    _holder_aglcontext = NULL;
  }
  
  if (_pending_icon != NULL) {
    CGImageRelease(_pending_icon);
    _pending_icon = NULL;
  }
  if (_current_icon != NULL) {
    cerr << "release current icon\n";
    CGImageRelease(_current_icon);
    _current_icon = NULL;
  }

  WindowProperties properties;
  properties.set_foreground(false);
  properties.set_open(false);
  properties.set_cursor_filename(Filename());
  system_changed_properties(properties);
				
  _is_fullscreen = false;	
  _osx_window = NULL;
}


static int id_seed = 100;

////////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
osxGraphicsWindow::osxGraphicsWindow(GraphicsPipe *pipe, 
                  						const string &name,
                  						const FrameBufferProperties &fb_prop,
                  						const WindowProperties &win_prop,
                  						int flags,
                  						GraphicsStateGuardian *gsg,
                  						GraphicsOutput *host) :
  GraphicsWindow(pipe, name, fb_prop, win_prop, flags, gsg, host),
  _osx_window(NULL),
  _is_fullscreen(false),
  _pending_icon(NULL),
  _current_icon(NULL),
#ifdef HACK_SCREEN_HASH_CONTEXT  
  _holder_aglcontext(NULL),
#endif  
  _originalMode(NULL),
  _ID(id_seed++)
{
	GraphicsWindowInputDevice device =
	GraphicsWindowInputDevice::pointer_and_keyboard("keyboard/mouse");
	_input_devices.push_back(device);
	_input_devices[0].set_pointer_in_window(0, 0);
	_last_key_modifiers = 0;
	
	if (osxdisplay_cat.is_debug())	
		osxdisplay_cat.debug() << "osxGraphicsWindow::osxGraphicsWindow() -" <<_ID << "\n";
  }

////////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::Destructor
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
osxGraphicsWindow::~osxGraphicsWindow()
{
  if (osxdisplay_cat.is_debug())	
    osxdisplay_cat.debug() << "osxGraphicsWindow::~osxGraphicsWindow() -" <<_ID << "\n";

  // Make sure the window callback won't come back to this
  // (destructed) object any more.
  if (_osx_window) {
    SetWRefCon (_osx_window, (long) NULL);
  }

  ReleaseSystemResources();	
}
 

///////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::get_context
//       Access: private..
//  Description:  Helper to Decide whitch context to use if any
////////////////////////////////////////////////////////////////////
AGLContext  osxGraphicsWindow::get_context(void)
{
	if(_holder_aglcontext != NULL)
	  return _holder_aglcontext;

    return get_ggs_context();
 }
///////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::get_ggs_context
//       Access: private..
//  Description:  
////////////////////////////////////////////////////////////////////

AGLContext  osxGraphicsWindow::get_ggs_context(void)
{
	if(_gsg != NULL)
	{
		osxGraphicsStateGuardian *osxgsg = NULL;
		osxgsg = DCAST(osxGraphicsStateGuardian, _gsg);	
		return osxgsg->get_context();
	}
	return NULL;
 }

///////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::buildGL
//       Access: private..
//  Description:  Code of the class.. used to control the GL context Allocation .. 
////////////////////////////////////////////////////////////////////
OSStatus osxGraphicsWindow::buildGL (bool full_screen)
{
	// make sure the ggs is up and runnig..
		osxGraphicsStateGuardian *osxgsg = NULL;
		osxgsg = DCAST(osxGraphicsStateGuardian, _gsg);
		OSStatus stat = osxgsg->buildGL(*this);
		if(stat != noErr)
		   return stat;

	OSStatus err = noErr;
	
//	if(!full_screen)
	{
		if (osxgsg->getAGlPixelFormat())
		 {
			_holder_aglcontext = aglCreateContext(osxgsg->getAGlPixelFormat(),NULL);
			
			err  = aglReportError ("aglCreateContext");
			if(_holder_aglcontext == NULL)
			{
				osxdisplay_cat.error() << "osxGraphicsWindow::buildG Error aglCreateContext \n";
				if(err ==noErr)
				  err = -1;				
			}
			else
			{			
				aglSetInteger (_holder_aglcontext, AGL_BUFFER_NAME, &osxgsg->SharedBuffer); 	
				err = aglReportError ("aglSetInteger AGL_BUFFER_NAME");
			
			}
		}
		else
		{
			osxdisplay_cat.error() << "osxGraphicsWindow::buildG Error Getting PixelFormat \n"; 	
			if(err ==noErr)
				err = -1;				
		}
	}
    return err;
}

////////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::set_icon_filename
//       Access: Private
//  Description: Called internally to load up an icon file that should
//               be applied to the window.  Returns true on success,
//               false on failure.
////////////////////////////////////////////////////////////////////
bool osxGraphicsWindow::set_icon_filename(const Filename &icon_filename)
{
  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();

  Filename icon_pathname = icon_filename;
  if (!vfs->resolve_filename(icon_pathname, model_path)) {
    // The filename doesn't exist.
    osxdisplay_cat.warning()
      << "Could not find icon filename " << icon_filename << "\n";
    return false;
  }


  PNMImage pnmimage;
  if (!pnmimage.read(icon_pathname))
  {
    osxdisplay_cat.warning()
      << "Could not read icon filename " << icon_pathname << "\n";
    return false;
  }

  CGImageRef icon_image = osxGraphicsPipe::create_cg_image(pnmimage);
  if (icon_image == NULL)
  {
    return false;
  }

  if (_pending_icon != NULL)
  {
    CGImageRelease(_pending_icon);
    _pending_icon = NULL;
  }
  _pending_icon = icon_image;
  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::begin_frame
//       Access: Public, Virtual
//  Description: This function will be called within the draw thread
//               before beginning rendering for a given frame.  It
//               should do whatever setup is required, and return true
//               if the frame should be rendered, or false if it
//               should be skipped.
////////////////////////////////////////////////////////////////////
bool osxGraphicsWindow::begin_frame(FrameMode mode, Thread *current_thread)
{
  PStatTimer timer(_make_current_pcollector);
  
  begin_frame_spam(mode);
  if (_gsg == (GraphicsStateGuardian *)NULL || 
      (_osx_window == NULL && _is_fullscreen != true))
  {
    // not powered up .. just abort..
    return false;
  }
 
  // Now is a good time to apply the icon change that may have
  // recently been requested.  By this point, we should be able to get
  // a handle to the dock context.
  if (_pending_icon != NULL)
  {
    CGContextRef context = BeginCGContextForApplicationDockTile();
    if (context != NULL)
    {
      SetApplicationDockTileImage(_pending_icon);
      EndCGContextForApplicationDockTile(context); 

      if (_current_icon != NULL)
      {
        CGImageRelease(_current_icon);
        _current_icon = NULL;
      }
      _current_icon = _pending_icon;
      _pending_icon = NULL;
    }
  }
  
  if (_is_fullscreen)
  {
    aglSetFullScreen(get_ggs_context(),0,0,0,0);
    aglReportError ("aglSetFullScreen");	
    
    if (!aglSetCurrentContext(get_ggs_context()))
      aglReportError ("aglSetCurrentContext");	

  }else
  {
    if (FullScreenWindow != NULL)
    {
      return false;
    }
	
    if (!aglSetDrawable (get_ggs_context(),GetWindowPort (_osx_window)))
    {
      osxdisplay_cat.error() << "Error  aglSetDrawable \n";		   
      aglReportError("aglSetDrawable");
    }

    if (!aglSetCurrentContext(get_ggs_context()))
    {
      osxdisplay_cat.error() << "Error in aglSetCurrentContext \n";
      aglReportError ("aglSetCurrentContext");
    }	
  }
	
  _gsg->reset_if_new();
  _gsg->set_current_properties(&get_fb_properties());
  return _gsg->begin_frame(current_thread);
}

////////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::end_frame
//       Access: Public, Virtual
//  Description: This function will be called within the draw thread
//               after rendering is completed for a given frame.  It
//               should do whatever finalization is required.
////////////////////////////////////////////////////////////////////
void osxGraphicsWindow::end_frame(FrameMode mode, Thread *current_thread) 
{
  end_frame_spam(mode);
  
  if(mode == FM_render )
  {
    nassertv(_gsg != (GraphicsStateGuardian *)NULL);

    if (!_properties.get_fixed_size() && 
        !_properties.get_undecorated() && 
        !_properties.get_fullscreen() &&
        show_resize_box) {
      // Draw a kludgey little resize box in the corner of the window,
      // so the user knows he's supposed to be able to drag the window
      // if he wants.
      DisplayRegionPipelineReader dr_reader(_default_display_region, current_thread);
      _gsg->prepare_display_region(&dr_reader, Lens::SC_mono);
      DCAST(osxGraphicsStateGuardian, _gsg)->draw_resize_box();
    }
      
	aglSwapBuffers (get_ggs_context());
    _gsg->end_frame(current_thread);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::begin_flip
//       Access: Public, Virtual
//  Description: This function will be called within the draw thread
//               after end_frame() has been called on all windows, to
//               initiate the exchange of the front and back buffers.
//
//               This should instruct the window to prepare for the
//               flip at the next video sync, but it should not wait.
//
//               We have the two separate functions, begin_flip() and
//               end_flip(), to make it easier to flip all of the
//               windows at the same time.
////////////////////////////////////////////////////////////////////
void osxGraphicsWindow::end_flip()
{
//  cerr << " end_flip [" << _ID << "]\n";
}
void osxGraphicsWindow::begin_flip() 
{
 // this forces a rip to proper context
 //  cerr << " begin_flip [" << _ID << "]\n";
	return;
 
	if(_is_fullscreen)
	{
		aglSetFullScreen(get_ggs_context(),0,0,0,0);
		aglReportError ("aglSetFullScreen");	

		if (!aglSetCurrentContext(get_ggs_context()))
			aglReportError ("aglSetCurrentContext");
					
		aglSwapBuffers (get_ggs_context());
	}
	else
	{
		if(!aglSetDrawable (get_ggs_context(),GetWindowPort (_osx_window)))
		{
			osxdisplay_cat.error() << "Error  aglSetDrawable \n";		   
			aglReportError("aglSetDrawable");
		}

		if (!aglSetCurrentContext(get_ggs_context()))
		{
			osxdisplay_cat.error() << "Error in aglSetCurrentContext \n";		   
			aglReportError ("aglSetCurrentContext");
		}	

		aglSwapBuffers (get_ggs_context());
	}
	
/*	
  if(_is_fullscreen)
	aglSwapBuffers (get_ggs_context());
  else	
	aglSwapBuffers (get_context());
	
	
	aglReportError("aglSwapBuffers");
	*/
}

////////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::close_window
//       Access: Protected, Virtual
//  Description: Closes the window right now.  Called from the window
//               thread.
////////////////////////////////////////////////////////////////////
void osxGraphicsWindow::close_window()
{
  SystemCloseWindow();

  WindowProperties properties;
  properties.set_open(false);
  system_changed_properties(properties);

  ReleaseSystemResources();
  _gsg.clear();
  _active = false;
  GraphicsWindow::close_window();
}

//////////////////////////////////////////////////////////
// HACK ALLERT ************ Undocumented OSX calls...
// I can not find any other way to get the mouse focus to a window in OSX..
//
extern "C" {
	struct  CPSProcessSerNum
	{
		UInt32 lo;
		UInt32 hi;
	};

extern OSErr CPSGetCurrentProcess(CPSProcessSerNum *psn);
extern OSErr CPSEnableForegroundOperation( struct CPSProcessSerNum *psn);
extern OSErr CPSSetProcessName ( struct CPSProcessSerNum *psn, char *processname);
extern OSErr CPSSetFrontProcess( struct CPSProcessSerNum *psn);
};

////////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::open_window
//       Access: Protected, Virtual
//  Description: Opens the window right now.  Called from the window
//               thread.  Returns true if the window is successfully
//               opened, or false if there was a problem.
////////////////////////////////////////////////////////////////////
bool osxGraphicsWindow::open_window()
{
	WindowProperties req_properties  = _properties;
  
  if (_gsg == 0)
  {
    _gsg = new osxGraphicsStateGuardian(_pipe, NULL);
  }
  
  return OSOpenWindow(req_properties);
}


bool osxGraphicsWindow::OSOpenWindow(WindowProperties &req_properties)
{
	OSErr err = noErr;
	
	if (_current_icon != NULL && _pending_icon == NULL)
	{
		// If we already have an icon specified, we'll need to reapply it
		// when the window is succesfully created.
		_pending_icon = _current_icon;
		_current_icon = NULL;
	}
  
	static bool GlobalInits = false;
	if (GlobalInits != true)
	{
		//
		// one time aplication inits.. to get a window open from a standalone aplication..

		EventHandlerRef	ref1;
		EventTypeSpec	list1[] =
		{ 
			//{ kEventClassCommand,  kEventProcessCommand },
			//{ kEventClassCommand,  kEventCommandUpdateStatus },
			{ kEventClassMouse, kEventMouseDown },// handle trackball functionality globaly because there is only a single user
			{ kEventClassMouse, kEventMouseUp }, 
			{ kEventClassMouse, kEventMouseMoved },
			{ kEventClassMouse, kEventMouseDragged },
			{ kEventClassMouse, kEventMouseWheelMoved } ,
			{ kEventClassKeyboard, kEventRawKeyDown },
			{ kEventClassKeyboard, kEventRawKeyUp } ,
			{ kEventClassKeyboard, kEventRawKeyRepeat },
			{ kEventClassKeyboard, kEventRawKeyModifiersChanged }	,
			{kEventClassTextInput,	kEventTextInputUnicodeForKeyEvent},				   
		};

		EventHandlerUPP	gEvtHandler = NewEventHandlerUPP(appEvtHndlr);
		err = InstallApplicationEventHandler (gEvtHandler, GetEventTypeCount (list1) , list1, this, &ref1 );
		GlobalInits = true;

		struct CPSProcessSerNum PSN;
		GetCurrentProcess((ProcessSerialNumber *)&PSN);
		err = CPSGetCurrentProcess(&PSN);

		if (req_properties.has_title())
			err = CPSSetProcessName(&PSN,(char *)req_properties.get_title().c_str());
		else
			err = CPSSetProcessName(&PSN,"");
				
		err = CPSEnableForegroundOperation(&PSN);
		err = CPSSetFrontProcess(&PSN);
	}

	if (req_properties.has_fullscreen() && req_properties.get_fullscreen())
	{
		// capture the main display
		CGDisplayCapture( kCGDirectMainDisplay );
		// if sized try and switch it..
		if (req_properties.has_size())
		{
			_originalMode = CGDisplayCurrentMode( kCGDirectMainDisplay );	
			CFDictionaryRef newMode = CGDisplayBestModeForParameters( kCGDirectMainDisplay, 32,  req_properties.get_x_size(), req_properties.get_y_size(), 0 );
			if (newMode == NULL)
			{
				osxdisplay_cat.error()
					<< "Invalid fullscreen size: " << req_properties.get_x_size()
					<< ", " << req_properties.get_y_size()
					<< "\n";
			}
			else
			{
				CGDisplaySwitchToMode( kCGDirectMainDisplay, newMode);

				// Set our new window size according to the size we actually got.

				SInt32 width, height;
				CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(newMode, kCGDisplayWidth), kCFNumberSInt32Type, &width);
				CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(newMode, kCGDisplayHeight), kCFNumberSInt32Type, &height);

				_properties.set_size(width, height);
			}
		}
		
		if (buildGL(true) != noErr)
		{
			if(_originalMode != NULL)
				CGDisplaySwitchToMode( kCGDirectMainDisplay, _originalMode );
			_originalMode = NULL;
		  
			CGDisplayRelease( kCGDirectMainDisplay );
			return false;		     
		}	

		_properties.set_fullscreen(true);

		_is_fullscreen	=true;	
		FullScreenWindow = this;
		req_properties.clear_fullscreen();
	}
	else
	{
		// lets use this as a crome based window..
		Rect r;
		if (req_properties.has_origin())
		{	
			r.top = req_properties.get_y_origin();
			r.left =req_properties.get_x_origin();
		}
		else
		{
			r.top = 50;
			r.left = 10;
		}
      
		if (req_properties.has_size())
		{
		  r.right = r.left + req_properties.get_x_size();
		  r.bottom = r.top + req_properties.get_y_size();
		}
		else
		{			
		  r.right = r.left + 512;
		  r.bottom = r.top + 512;
		}
    
		if (req_properties.has_parent_window())
		{
			NSWindow*	parentWindow		=	(NSWindow *)req_properties.get_parent_window();
			NSView*		aView				=	[[parentWindow contentView] viewWithTag:378];
			NSRect		aRect				=	[aView frame];
			NSPoint		origin				=	[parentWindow convertBaseToScreen:aRect.origin];
			
			CreateNewWindow(kSimpleWindowClass, kWindowNoAttributes, &r, &_osx_window);
			NSWindow*	childWindow			=	[[NSWindow alloc] initWithWindowRef:_osx_window];
			
			[childWindow setFrameOrigin:origin];
			[childWindow setAcceptsMouseMovedEvents:YES];
			[childWindow setBackgroundColor:[NSColor blackColor]];
			[parentWindow addChildWindow:childWindow ordered:NSWindowAbove];
			[childWindow orderFront:nil];
			
			_properties.set_fixed_size(true);
		}
		else
		{
			if (req_properties.has_undecorated() && req_properties.get_undecorated())
			{ // create a unmovable .. no edge window..
				
				CreateNewWindow(kDocumentWindowClass, kWindowStandardDocumentAttributes |  kWindowNoTitleBarAttribute, &r, &_osx_window);
			}
			else
			{ // create a window with crome and sizing and sucj
			  // In this case, we want to constrain the window to the
			  // available size.
				
				Rect bounds;
				GetAvailableWindowPositioningBounds(GetMainDevice(), &bounds);
				
				r.left = max(r.left, bounds.left);
				r.right = min(r.right, bounds.right);
				r.top = max(r.top, bounds.top);
				r.bottom = min(r.bottom, bounds.bottom);
				
				CreateNewWindow(kDocumentWindowClass, kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute, &r, &_osx_window);
			}
		}

    
		if (_osx_window)
		{
			EventHandlerUPP gWinEvtHandler;			// window event handler
			EventTypeSpec list[] =
			{ 
				{ kEventClassWindow, kEventWindowCollapsing },
				{ kEventClassWindow, kEventWindowShown },
				{ kEventClassWindow, kEventWindowActivated },
				{ kEventClassWindow, kEventWindowDeactivated },
				{ kEventClassWindow, kEventWindowClose },
				{ kEventClassWindow, kEventWindowBoundsChanged },

				{ kEventClassWindow, kEventWindowCollapsed },
				{ kEventClassWindow, kEventWindowExpanded },
				{ kEventClassWindow, kEventWindowZoomed },
				{ kEventClassWindow, kEventWindowClosed },
			};

			SetWRefCon (_osx_window, (long) this); // point to the window record in the ref con of the window
			gWinEvtHandler = NewEventHandlerUPP(windowEvtHndlr); 
			InstallWindowEventHandler (_osx_window, gWinEvtHandler, GetEventTypeCount (list), list, (void*)this, NULL); // add event handler
			ShowWindow (_osx_window);
      
			if(buildGL(false) != noErr)
			{
			osxdisplay_cat.error() << " Error In Generate GL \n";
			  
			HideWindow (_osx_window);
			SetWRefCon (_osx_window, (long int) NULL);
			DisposeWindow(_osx_window);
			_osx_window = NULL;
			return false;
			}

			//
			// atach the holder context to the window..
			//

			if (!aglSetDrawable(_holder_aglcontext, GetWindowPort (_osx_window)))
			  err = aglReportError ("aglSetDrawable");

			if (req_properties.has_fullscreen())
			{
			  _properties.set_fullscreen(false);		
			  req_properties.clear_fullscreen();			
			}

			if (req_properties.has_undecorated())
			{
			  _properties.set_undecorated(req_properties.get_undecorated());
			  req_properties.clear_undecorated();
			}
		}

		// Now measure the size and placement of the window we
		// actually ended up with.
		Rect rectPort = {0,0,0,0};
		GetWindowPortBounds (_osx_window, &rectPort); 	
		_properties.set_size((int)(rectPort.right - rectPort.left),(int) (rectPort.bottom - rectPort.top));
		req_properties.clear_size();
		req_properties.clear_origin();
  }

	if (req_properties.has_icon_filename())
		set_icon_filename(req_properties.get_icon_filename());

	_properties.set_foreground(true);
	_properties.set_minimized(false);
	_properties.set_open(true);

	if (_properties.has_size())
		set_size_and_recalc(_properties.get_x_size(), _properties.get_y_size());
	
  return (err == noErr);
}

////////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::process_events()
//       Access: virtual, protected
//  Description: Required Event upcall . Used to dispatch Window and Aplication Events 
//               back into panda
//               
////////////////////////////////////////////////////////////////////
void osxGraphicsWindow::process_events()
{
    GraphicsWindow::process_events();
    EventRef theEvent;
    EventTargetRef theTarget;
    theTarget = GetEventDispatcherTarget();

    //    while  (ReceiveNextEvent(0, NULL,kEventDurationForever,true, &theEvent)== noErr)
    while  (ReceiveNextEvent(0, NULL,kEventDurationNoWait,true, &theEvent)== noErr)
    {
//	   static int pass = 0;
//	    cerr << "--------------------------------------------Dispatch Event " << pass++ << "\n";
        SendEventToEventTarget (theEvent, theTarget);
        ReleaseEvent(theEvent);
//		cerr << "------------------------------------Done Dispatch \n";
    }

};

// handle display config changes meaing we need to update the GL context via the resize function and check for windwo dimension changes
// also note we redraw the content here as it could be lost in a display config change
//void handleWindowDMEvent (void *userData, short theMessage, void *notifyData)
//{
//	if (kDMNotifyEvent == theMessage)
//	{ // post change notifications only
//		osxGraphicsWindow * osxwin = NULL;
//		WindowRef window = (WindowRef) userData;
//		if (window)
//			osxwin = osxGraphicsWindow::GetCurrentOSxWindow (window);
//		if (osxwin) { // have a valid OpenGl window
//			Rect rectPort;
//			CGRect viewRect = {{0.0f, 0.0f}, {0.0f, 0.0f}};
//			GetWindowPortBounds (window, &rectPort);
//			viewRect.size.width = (float) (rectPort.right - rectPort.left);
//			viewRect.size.height = (float) (rectPort.bottom - rectPort.top);
//			//resizeGL (pContextInfo->aglContext, &pContextInfo->camera, pContextInfo->shapeSize, viewRect);
//			InvalWindowRect (window,  &rectPort); // force redrow
//		}
//	}
//}

////////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::handleKeyInput()
//       Access: virtual, protected
//  Description: Required Event upcall . Used to dispatch Window and Aplication Events 
//               back into panda
//               
////////////////////////////////////////////////////////////////////
// key input handler
OSStatus osxGraphicsWindow::handleKeyInput (EventHandlerCallRef myHandler, EventRef event, Boolean keyDown) {
  if (osxdisplay_cat.is_debug()) {
    UInt32 keyCode;
    GetEventParameter (event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);

    osxdisplay_cat.debug()
      << ClockObject::get_global_clock()->get_real_time() 
      << " handleKeyInput: " << (void *)this << ", " << keyCode 
      << ", " << (int)keyDown << "\n";
  }
  
  CallNextEventHandler(myHandler, event);

  // We don't check the result of the above function.  In principle,
  // this should return eventNotHandledErr if the key event is not
  // handled by the OS, but in practice, testing this just seems to
  // eat the Escape keypress meaninglessly.  Keypresses like F11 that
  // are already mapped in the desktop seem to not even come into this
  // function in the first place.
  UInt32 newModifiers = 0;
  OSStatus error = GetEventParameter(event, kEventParamKeyModifiers, 
                                     typeUInt32, NULL, sizeof(UInt32), 
                                     NULL, &newModifiers);
  if(error == noErr) {						   
    HandleModifireDeleta(newModifiers);
  }
    
  UInt32 keyCode;
  GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL,
                    sizeof(UInt32), NULL, &keyCode);
  ButtonHandle button = OSX_TranslateKey(keyCode, event);

  if (keyDown) {
    if ((newModifiers & cmdKey) != 0) {
      if (button == KeyboardButton::ascii_key("q") ||
          button == KeyboardButton::ascii_key("w")) {
        // Command-Q or Command-W: quit the application or close the
        // window, respectively.  For now, we treat them both the
        // same: close the window.
        user_close_request();
      }
    }
    SendKeyEvent(button, true);
  } else {
    SendKeyEvent(button, false);
  }
  
  return noErr;
}
 ////////////////////////////////////////////////////////////////////
 //     Function: 
 //       Access: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
void osxGraphicsWindow::SystemSetWindowForground(bool forground)
{
	  WindowProperties properties;
      properties.set_foreground(forground);
	  system_changed_properties(properties);
};		
 ////////////////////////////////////////////////////////////////////
 //     Function: 
 //       Access: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
	
 void osxGraphicsWindow::SystemPointToLocalPoint(Point &qdGlobalPoint)
 {
	if(_osx_window != NULL)
	{
		GrafPtr savePort;
		Boolean	portChanged	=	QDSwapPort(GetWindowPort(_osx_window), &savePort);

		GlobalToLocal( &qdGlobalPoint );

		if (portChanged)
			QDSwapPort(savePort, NULL);
	}
					
 };
	
 ////////////////////////////////////////////////////////////////////
 //     Function: 
 //       Access: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////

 OSStatus osxGraphicsWindow::handleWindowMouseEvents (EventHandlerCallRef myHandler, EventRef event)
 {
     WindowRef			window = NULL;
     OSStatus			result = eventNotHandledErr;
     UInt32 				kind = GetEventKind (event);
     EventMouseButton	button = 0;
     Point qdGlobalPoint = {0, 0};
     UInt32				modifiers = 0;	
     Rect 				rectPort;

//	 cerr <<" Start Mouse Event " << _ID << "\n";

     // Mac OS X v10.1 and later
     // should this be front window???
     GetEventParameter(event, kEventParamWindowRef, typeWindowRef, NULL, sizeof(WindowRef), NULL, &window);
	 if(!_is_fullscreen && (window == NULL || window != _osx_window ))
	     return eventNotHandledErr;
	 
	 
     GetWindowPortBounds (window, &rectPort);

     result = CallNextEventHandler(myHandler, event);	
     if (eventNotHandledErr == result) 
     { // only handle events not already handled (prevents wierd resize interaction)
         switch (kind) {
             // start trackball, pan, or dolly
            case kEventMouseDown:
                {
                    GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(EventMouseButton), NULL, &button);
                    GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
                    if(_properties.get_mouse_mode()==WindowProperties::M_relative)
                    {
                      GetEventParameter(event, kEventParamMouseDelta,typeQDPoint, NULL, sizeof(Point),NULL	, (void*) &qdGlobalPoint);
                      MouseData currMouse=get_pointer(0);
                      qdGlobalPoint.h+=currMouse.get_x();
                      qdGlobalPoint.v+=currMouse.get_y();
                    } 
                    else
                    {
                      GetEventParameter(event, kEventParamMouseLocation,typeQDPoint, NULL, sizeof(Point),NULL	, (void*) &qdGlobalPoint);                    
                      SystemPointToLocalPoint(qdGlobalPoint);
                    }
                    ButtonHandle button_h = MouseButton::one();
                    if(kEventMouseButtonSecondary == button)
                        button_h = MouseButton::three();
                    if(kEventMouseButtonTertiary == button)
                        button_h = MouseButton::two();
                    _input_devices[0].set_pointer_in_window((int)qdGlobalPoint.h, (int)qdGlobalPoint.v);
                    _input_devices[0].button_down(button_h);
					result = noErr;
                }
                break;
                // stop trackball, pan, or dolly
            case kEventMouseUp:
                {
                    GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(EventMouseButton), NULL, &button);
                    //				GetEventParameter(event, kEventParamWindowMouseLocation, typeHIPoint, NULL, sizeof(HIPoint), NULL, &location);	// Mac OS X v10.1 and later
                    if(_properties.get_mouse_mode()==WindowProperties::M_relative)
                    {
                      GetEventParameter(event, kEventParamMouseDelta,typeQDPoint, NULL, sizeof(Point),NULL	, (void*) &qdGlobalPoint);
                      MouseData currMouse=get_pointer(0);
                      qdGlobalPoint.h+=currMouse.get_x();
                      qdGlobalPoint.v+=currMouse.get_y();
                    }
                    else
                    {
                      GetEventParameter(event, kEventParamMouseLocation,typeQDPoint, NULL, sizeof(Point),NULL	, (void*) &qdGlobalPoint);
                      SystemPointToLocalPoint(qdGlobalPoint);
                    }
                    ButtonHandle button_h = MouseButton::one();
                    if(kEventMouseButtonSecondary == button)
                        button_h = MouseButton::three();
                    if(kEventMouseButtonTertiary == button)
                        button_h = MouseButton::two();
                    _input_devices[0].set_pointer_in_window((int)qdGlobalPoint.h, (int)qdGlobalPoint.v);
                    _input_devices[0].button_up(button_h);
					result = noErr;					
                }
                break;
            case kEventMouseMoved:	
            case kEventMouseDragged:
                if(_properties.get_mouse_mode()==WindowProperties::M_relative)
                {
                  GetEventParameter(event, kEventParamMouseDelta,typeQDPoint, NULL, sizeof(Point),NULL	, (void*) &qdGlobalPoint);
                  
                  MouseData currMouse=get_pointer(0);
                  qdGlobalPoint.h+=currMouse.get_x();
                  qdGlobalPoint.v+=currMouse.get_y();
                }
                else
                { 
                  GetEventParameter(event, kEventParamMouseLocation,typeQDPoint, NULL, sizeof(Point),NULL	, (void*) &qdGlobalPoint);
                  SystemPointToLocalPoint(qdGlobalPoint);
                }
                _input_devices[0].set_pointer_in_window((int)qdGlobalPoint.h, (int)qdGlobalPoint.v);
				result = noErr;

                break;
			//long				wheelDelta = 0;		
				
//            case kEventMouseWheelMoved: 
//					result = NoErr;
  //              GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(long), NULL, &wheelDelta);
                break;
         }
//         result = noErr;
     }	
	 
//	 	 cerr <<" End Mouse Event \n";

     return result;
 }

 ////////////////////////////////////////////////////////////////////
 //     Function: osxGraphicsWindow::OSX_TranslateKey
 //       Access: Private
 //  Description: MAC Key Codes to Panda Key Codes
 ////////////////////////////////////////////////////////////////////
 ButtonHandle osxGraphicsWindow::OSX_TranslateKey(UInt32 key, EventRef event)
 {


     ButtonHandle nk = ButtonHandle::none();
     switch ( key )
     {
     case 0:    nk = KeyboardButton::ascii_key('a');       break;
     case 11:   nk = KeyboardButton::ascii_key('b');       break;
     case 8:    nk = KeyboardButton::ascii_key('c');       break;
     case 2:    nk = KeyboardButton::ascii_key('d');       break;
     case 14:   nk = KeyboardButton::ascii_key('e');       break;
     case 3:    nk = KeyboardButton::ascii_key('f');       break;
     case 5:    nk = KeyboardButton::ascii_key('g');       break;
     case 4:    nk = KeyboardButton::ascii_key('h');       break;
     case 34:   nk = KeyboardButton::ascii_key('i');       break;
     case 38:   nk = KeyboardButton::ascii_key('j');       break;
     case 40:   nk = KeyboardButton::ascii_key('k');       break;
     case 37:   nk = KeyboardButton::ascii_key('l');       break;
     case 46:   nk = KeyboardButton::ascii_key('m');       break;
     case 45:   nk = KeyboardButton::ascii_key('n');       break;
     case 31:   nk = KeyboardButton::ascii_key('o');       break;
     case 35:   nk = KeyboardButton::ascii_key('p');       break;
     case 12:   nk = KeyboardButton::ascii_key('q');       break;
     case 15:   nk = KeyboardButton::ascii_key('r');       break;
     case 1:    nk = KeyboardButton::ascii_key('s');       break;
     case 17:   nk = KeyboardButton::ascii_key('t');       break;
     case 32:   nk = KeyboardButton::ascii_key('u');       break;
     case 9:    nk = KeyboardButton::ascii_key('v');       break;
     case 13:   nk = KeyboardButton::ascii_key('w');       break;
     case 7:    nk = KeyboardButton::ascii_key('x');       break;
     case 16:   nk = KeyboardButton::ascii_key('y');       break;
     case 6:    nk = KeyboardButton::ascii_key('z');       break;

         // top row numbers
     case 29:   nk = KeyboardButton::ascii_key('0');       break;
     case 18:   nk = KeyboardButton::ascii_key('1');       break;
     case 19:   nk = KeyboardButton::ascii_key('2');       break;
     case 20:   nk = KeyboardButton::ascii_key('3');       break;
     case 21:   nk = KeyboardButton::ascii_key('4');       break;
     case 23:   nk = KeyboardButton::ascii_key('5');       break;
     case 22:   nk = KeyboardButton::ascii_key('6');       break;
     case 26:   nk = KeyboardButton::ascii_key('7');       break;
     case 28:   nk = KeyboardButton::ascii_key('8');       break;
     case 25:   nk = KeyboardButton::ascii_key('9');       break;

         // key pad ... do they really map to the top number in panda ?
     case 82:   nk = KeyboardButton::ascii_key('0');       break;
     case 83:   nk = KeyboardButton::ascii_key('1');       break;
     case 84:   nk = KeyboardButton::ascii_key('2');       break;
     case 85:   nk = KeyboardButton::ascii_key('3');       break;
     case 86:   nk = KeyboardButton::ascii_key('4');       break;
     case 87:   nk = KeyboardButton::ascii_key('5');       break;
     case 88:   nk = KeyboardButton::ascii_key('6');       break;
     case 89:   nk = KeyboardButton::ascii_key('7');       break;
     case 91:   nk = KeyboardButton::ascii_key('8');       break;
     case 92:   nk = KeyboardButton::ascii_key('9');       break;


         //	case 36:   nk = KeyboardButton::ret();			  break;   // no return  in panda ???
     case 49:   nk = KeyboardButton::space();		 	  break;
     case 51:   nk = KeyboardButton::backspace();		  break;
     case 48:   nk = KeyboardButton::tab();				  break;
     case 53:   nk = KeyboardButton::escape();			  break;
     case 76:   nk = KeyboardButton::enter();			  break;	
     case 36:   nk = KeyboardButton::enter();			  break;	

     case 123:  nk = KeyboardButton::left();				  break;
     case 124:  nk = KeyboardButton::right();			  break;
     case 125:  nk = KeyboardButton::down();				  break;
     case 126:  nk = KeyboardButton::up();				  break;
     case 116:  nk = KeyboardButton::page_up();				  break;
     case 121:  nk = KeyboardButton::page_down();				  break;
     case 115:  nk = KeyboardButton::home();				  break;
     case 119:  nk = KeyboardButton::end();				  break;
     case 114:  nk = KeyboardButton::help();			  break;			
     case 117:  nk = KeyboardButton::del();			  break;			

         //	case  71:  nk = KeyboardButton::num_lock()        break; 

     case 122:  nk = KeyboardButton::f1();				  break;
     case 120:  nk = KeyboardButton::f2();				  break;
     case  99:  nk = KeyboardButton::f3();				  break;
     case 118:  nk = KeyboardButton::f4();				  break;
     case  96:  nk = KeyboardButton::f5();				  break;
     case  97:  nk = KeyboardButton::f6();				  break;
     case  98:  nk = KeyboardButton::f7();				  break;
     case 100:  nk = KeyboardButton::f8();				  break;
     case 101:  nk = KeyboardButton::f9();				  break;
     case 109:  nk = KeyboardButton::f10();				  break;
     case 103:  nk = KeyboardButton::f11();				  break;
     case 111:  nk = KeyboardButton::f12();				  break;

     case 105:  nk = KeyboardButton::f13();				  break;
     case 107:  nk = KeyboardButton::f14();				  break;
     case 113:  nk = KeyboardButton::f15();				  break;
     case 106:  nk = KeyboardButton::f16();				  break;

         // shiftable chartablet 
     case  50:  nk = KeyboardButton::ascii_key('`');				  break;
     case  27:  nk = KeyboardButton::ascii_key('-');				  break;
     case  24:  nk = KeyboardButton::ascii_key('=');				  break;
     case  33:  nk = KeyboardButton::ascii_key('[');				  break;
     case  30:  nk = KeyboardButton::ascii_key(']');				  break;
     case  42:  nk = KeyboardButton::ascii_key('\\');			  break;
     case  41:  nk = KeyboardButton::ascii_key(';');				  break;
     case  39:  nk = KeyboardButton::ascii_key('\'');			  break;
     case  43:  nk = KeyboardButton::ascii_key(',');				  break;
     case  47:  nk = KeyboardButton::ascii_key('.');				  break;
     case  44:  nk = KeyboardButton::ascii_key('/');				  break;

     default:
       if (osxdisplay_cat.is_debug()) {
         osxdisplay_cat.debug()
           << " Untranslated KeyCode: " << key
           << " (0x" << hex << key << dec << ")\n";
       }

       // not sure this is right .. but no mapping for keypad and such
       // this at least does a best gess..
       
       char charCode =  0;	
       if(GetEventParameter( event, kEventParamKeyMacCharCodes, typeChar, nil, sizeof( charCode ), nil, &charCode ) == noErr)
         nk = KeyboardButton::ascii_key(charCode);	
     }
     return nk;
 }
 ////////////////////////////////////////////////////////////////////
 //     Function: osxGraphicsWindow::HandleModifireDeleta
 //       Access: Private
 //  Description: Used to emulate key events for the MAC key Modifiers..
 ////////////////////////////////////////////////////////////////////
 void     osxGraphicsWindow::HandleModifireDeleta(UInt32 newModifiers)
 {
     UInt32 changed = _last_key_modifiers ^ newModifiers;

     if ((changed & (shiftKey | rightShiftKey)) != 0)
         SendKeyEvent(KeyboardButton::shift(),(newModifiers & (shiftKey | rightShiftKey)) != 0) ;

     if ((changed & (optionKey | rightOptionKey)) != 0) 
         SendKeyEvent(KeyboardButton::alt(),(newModifiers & (optionKey | rightOptionKey)) != 0);


     if ((changed & (controlKey | rightControlKey)) != 0) 
		SendKeyEvent(KeyboardButton::control(),(newModifiers & (controlKey | rightControlKey)) != 0);

     if ((changed & cmdKey) != 0) 
		SendKeyEvent(KeyboardButton::meta(),(newModifiers & cmdKey) != 0);
	
    if ((changed & alphaLock) != 0) 
		SendKeyEvent(KeyboardButton::caps_lock(),(newModifiers & alphaLock) != 0);
	
	// save current state
    _last_key_modifiers = newModifiers;						   
								
 };
 ////////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::move_pointer
//       Access: Published, Virtual
//  Description: Forces the pointer to the indicated position within
//               the window, if possible.  
//
//               Returns true if successful, false on failure.  This
//               may fail if the mouse is not currently within the
//               window, or if the API doesn't support this operation.
////////////////////////////////////////////////////////////////////
bool osxGraphicsWindow::move_pointer(int device, int x, int y) 
{ 
    if(_osx_window == NULL) 
        return false; 
    
if (osxdisplay_cat.is_debug())    
        osxdisplay_cat.debug() << "move_pointer " << device <<" "<<  x <<" "<< y <<"\n"; 
    
    Point pt = {0, 0}; 
    pt.h = x; 
    pt.v = y; 
    _input_devices[0].set_pointer_in_window(x, y);  

    if(_properties.get_mouse_mode()==WindowProperties::M_absolute)
    {
       LocalPointToSystemPoint(pt); 
       CGPoint newCursorPosition = {0, 0}; 
       newCursorPosition.x = pt.h; 
       newCursorPosition.y = pt.v; 
       CGWarpMouseCursorPosition(newCursorPosition); 
    }
    
    return true; 
}; 

bool osxGraphicsWindow::do_reshape_request(int x_origin, int y_origin, bool has_origin,int x_size, int y_size)
{
  if (_properties.get_fullscreen()) {
    // Can't resize fullscreen windows that easily.
    return false;
  }

  // For now, ignore the origin, since we seem to be getting a bogus
  // origin of (0, 0).
  /*
  if (has_origin) {
    MoveWindow(_osx_window, x_origin, y_origin, false);
  }
  */

  if (!_properties.get_undecorated())
  {
    // Constrain the window to the available desktop size.
    Rect bounds;
    GetAvailableWindowPositioningBounds(GetMainDevice(), &bounds);
    
    x_size = min(x_size, bounds.right - bounds.left);
    y_size = min(y_size, bounds.bottom - bounds.top);
  }

  SizeWindow(_osx_window, x_size, y_size, false);

  system_changed_size(x_size, y_size);
  return true;
}								



////////////////////////////////////////////////////////////////////
//     Function: osxGraphicsWindow::set_properties_now
//       Access: Public, Virtual
//  Description: Applies the requested set of properties to the
//               window, if possible, for instance to request a change
//               in size or minimization status.
//
//               The window properties are applied immediately, rather
//               than waiting until the next frame.  This implies that
//               this method may *only* be called from within the
//               window thread.
//
//               The properties that have been applied are cleared
//               from the structure by this function; so on return,
//               whatever remains in the properties structure are
//               those that were unchanged for some reason (probably
//               because the underlying interface does not support
//               changing that property on an open window).
////////////////////////////////////////////////////////////////////
void osxGraphicsWindow::set_properties_now(WindowProperties &properties) 
{
  if (osxdisplay_cat.is_debug())
  {
    osxdisplay_cat.debug()
      << "------------------------------------------------------\n";
    osxdisplay_cat.debug()
      << "set_properties_now " << properties << "\n";
  }
  
  GraphicsWindow::set_properties_now(properties);
	  
  if (osxdisplay_cat.is_debug()) {
    osxdisplay_cat.debug()
      << "set_properties_now After Base Class" << properties << "\n";
  }
 
  // for some changes .. a full rebuild is required for the OS layer Window.
  //    I think it is the crome atribute and full screen behaviour.
  bool need_full_rebuild = false;
 
  // if we are not full and transitioning to full
  if (properties.has_fullscreen() && 
      properties.get_fullscreen() != _properties.get_fullscreen()) {
    need_full_rebuild = true;
  }

  // If we are fullscreen and requesting a size change
  if (_properties.get_fullscreen() &&  
      (properties.has_size() && 
       (properties.get_x_size() != _properties.get_x_size() ||
        properties.get_y_size() != _properties.get_y_size()))) {
    need_full_rebuild = true;
  }
  
  if (need_full_rebuild) {
    // Logic here is ..  take a union of the properties .. with the
    // new allowed to overwrite the old states.  and start a bootstrap
    // of a new window ..
    
    // get a copy of my properties..
    WindowProperties req_properties(_properties); 
    ReleaseSystemResources();
    req_properties.add_properties(properties);	
    
    OSOpenWindow(req_properties); 
    
    // Now we've handled all of the requested properties.
    properties.clear();
  }

  if (properties.has_title()) {
    _properties.set_title(properties.get_title());
    if (_osx_window) {
      SetWindowTitleWithCFString(_osx_window,
                                 CFStringCreateWithCString(NULL,properties.get_title().c_str(),
                                                           kCFStringEncodingMacRoman));
    }
    properties.clear_title();
  }

  // An icon filename means to load up the icon and save it.  We can't
  // necessarily apply it immediately; it will get applied later, in
  // the window event handler.
  if (properties.has_icon_filename()) {
    if (set_icon_filename(properties.get_icon_filename())) {
      properties.clear_icon_filename();
    }
  }

  // decorated .. if this changes it reqires a new window
  if (properties.has_undecorated()) {
    _properties.set_undecorated(properties.get_undecorated());
    properties.clear_undecorated();
  }

  if (properties.has_cursor_hidden()) { 
    _properties.set_cursor_hidden(properties.get_cursor_hidden()); 
    if (properties.get_cursor_hidden()) { 
      CGDisplayHideCursor(kCGDirectMainDisplay); 
    } else { 
      CGDisplayShowCursor(kCGDirectMainDisplay); 
    } 
    properties.clear_cursor_hidden(); 
  } 

  if (osxdisplay_cat.is_debug()) {
    osxdisplay_cat.debug()
      << "set_properties_now  Out....." << _properties << "\n";
  }

  return;
}
//////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////

void osxGraphicsWindow::LocalPointToSystemPoint(Point &qdLocalPoint) 
{ 
	if(_osx_window != NULL) 
	{ 
		GrafPtr	savePort;
		Boolean	portChanged	=	QDSwapPort(GetWindowPort(_osx_window), &savePort);

		LocalToGlobal( &qdLocalPoint );
      
		if (portChanged)
			QDSwapPort(savePort, NULL);
   } 
 }; 


////////////////////////////////////////////////////////////////////
//     Function: OSXGraphicsWindow::mouse_mode_relative
//       Access: Protected, Virtual
//  Description: detaches mouse. Only mouse delta from now on. 
//               
////////////////////////////////////////////////////////////////////
void osxGraphicsWindow::mouse_mode_relative()
{
  CGAssociateMouseAndMouseCursorPosition(false);
}


////////////////////////////////////////////////////////////////////
//     Function: OSXGraphicsWindow::mouse_mode_absolute
//       Access: Protected, Virtual
//  Description: reattaches mouse to location
//               
////////////////////////////////////////////////////////////////////
void osxGraphicsWindow::mouse_mode_absolute()
{
  CGAssociateMouseAndMouseCursorPosition(true);
}
