/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkCarbonRenderWindow.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkCarbonRenderWindow.h"

#include "vtkCarbonRenderWindowInteractor.h"
#include "vtkIdList.h"
#include "vtkObjectFactory.h"
#include "vtkOpenGLActor.h"
#include "vtkOpenGLCamera.h"
#include "vtkOpenGLLight.h"
#include "vtkOpenGLPolyDataMapper.h"
#include "vtkOpenGLProperty.h"
#include "vtkOpenGLRenderer.h"
#include "vtkOpenGLTexture.h"
#include "vtkRendererCollection.h"

#include <math.h>

vtkCxxRevisionMacro(vtkCarbonRenderWindow, "1.39");
vtkStandardNewMacro(vtkCarbonRenderWindow);

//----------------------------------------------------------------------------
// Dump agl errors to string, return error code
OSStatus aglReportError ()
{
  GLenum err = aglGetError();
  if (AGL_NO_ERROR != err)
    cout << ((char *)aglErrorString(err));
  // ensure we are returning an OSStatus noErr if no error condition
  if (err == AGL_NO_ERROR)
    return noErr;
  else
    return (OSStatus) err;
}

//----------------------------------------------------------------------------
// if error dump gl errors, return error
OSStatus glReportError ()
{
  GLenum err = glGetError();
  switch (err)
  {
  case GL_NO_ERROR:
    break;
  case GL_INVALID_ENUM:
    cout << "GL Error: Invalid enumeration\n";
    break;
  case GL_INVALID_VALUE:
    cout << "GL Error: Invalid value\n";
    break;
  case GL_INVALID_OPERATION:
    cout << "GL Error: Invalid operation\n";
    break;
  case GL_STACK_OVERFLOW:
    cout << "GL Error: Stack overflow\n";
    break;
  case GL_STACK_UNDERFLOW:
    cout << "GL Error: Stack underflow\n";
    break;
  case GL_OUT_OF_MEMORY:
    cout << "GL Error: Out of memory\n";
    break;
  }
  // ensure we are returning an OSStatus noErr if no error condition
  if (err == GL_NO_ERROR)
    return noErr;
  else
    return (OSStatus) err;
}

//----------------------------------------------------------------------------
// CheckRenderer
// looks at renderer attributes it has at least the VRAM is accelerated
// Inputs:   hGD: GDHandle to device to look at
// pVRAM: pointer to VRAM in bytes required; out is actual VRAM if
//     a renderer was found, otherwise it is the input parameter
//     pTextureRAM:  pointer to texture RAM in bytes required; out is same
//     (implementation assume VRAM returned by card is total
//     so we add texture and VRAM)
//   fAccelMust: do we check for acceleration
// Returns: true if renderer for the requested device complies, false otherwise
static Boolean CheckRenderer (GDHandle hGD, long* pVRAM, long* pTextureRAM,
     GLint* pDepthSizeSupport, Boolean fAccelMust)
{
  AGLRendererInfo info, head_info;
  GLint inum;
  GLint dAccel = 0;
  GLint dVRAM = 0, dMaxVRAM = 0;
  Boolean canAccel = false, found = false;
  head_info = aglQueryRendererInfo(&hGD, 1);
  aglReportError();
  if(!head_info)
    {
    cout << "aglQueryRendererInfo error.\n";
    return false;
    }
  else
    {
    info = head_info;
    inum = 0;
    // Check for accelerated renderer, if so ignore non-accelerated ones
    // Prevents returning info on software renderer when get a hardware one
    while (info)
      {
      aglDescribeRenderer(info, AGL_ACCELERATED, &dAccel);
      aglReportError ();
      if (dAccel)
 canAccel = true;
      info = aglNextRendererInfo(info);
      aglReportError ();
      inum++;
      }

    info = head_info;
    inum = 0;
    while (info)
      {
      aglDescribeRenderer (info, AGL_ACCELERATED, &dAccel);
      aglReportError ();
      // if we can accel then we will choose the accelerated renderer
      // how about compliant renderers???
      if ((canAccel && dAccel) || (!canAccel && (!fAccelMust || dAccel)))
 {
 aglDescribeRenderer (info, AGL_VIDEO_MEMORY, &dVRAM);
 // we assume that VRAM returned is total thus add
 // texture and VRAM required
 aglReportError ();
 if (dVRAM >= (*pVRAM + *pTextureRAM))
   {
   if (dVRAM >= dMaxVRAM) // find card with max VRAM
     {
     aglDescribeRenderer (info, AGL_DEPTH_MODES, pDepthSizeSupport);
     // which depth buffer modes are supported
     aglReportError ();
     dMaxVRAM = dVRAM; // store max
     found = true;
     }
   }
 }
      info = aglNextRendererInfo(info);
      aglReportError ();
      inum++;
      }
    }
  aglDestroyRendererInfo(head_info);
  if (found) // if found a card with enough VRAM and meets the accel criteria
    {
    *pVRAM = dMaxVRAM; // return VRAM
    return true;
    }
  // VRAM will remain to same as it did when sent in
  return false;
}

//----------------------------------------------------------------------------
// CheckAllDeviceRenderers
// looks at renderer attributes and each device must have at least one 
// renderer that fits the profile.
// Inputs:   pVRAM: pointer to VRAM in bytes required; 
//    out is actual min VRAM of all renderers found,
//    otherwise it is the input parameter
//     pTextureRAM: pointer to texture RAM in bytes required; 
//    out is same (implementation assumes VRAM returned 
//    by card is total so we add texture and VRAM)
//      fAccelMust: do we check fro acceleration
// Returns: true if any renderer on each device complies (not necessarily
//   the same renderer), false otherwise

static Boolean CheckAllDeviceRenderers (long* pVRAM, long* pTextureRAM,
   GLint* pDepthSizeSupport, 
   Boolean fAccelMust)
{
  AGLRendererInfo info, head_info;
  GLint inum;
  GLint dAccel = 0;
  GLint dVRAM = 0, dMaxVRAM = 0;
  Boolean canAccel = false, found = false, goodCheck = true;
  long MinVRAM = 0x8FFFFFFF; // max long
  GDHandle hGD = GetDeviceList (); // get the first screen
  while (hGD && goodCheck)
    {
    head_info = aglQueryRendererInfo(&hGD, 1);
    aglReportError ();
    if(!head_info)
      {
      cout << "aglQueryRendererInfo error";
      return false;
      }
    else
      {
      info = head_info;
      inum = 0;
      // if accelerated renderer, ignore non-accelerated ones
      // prevents returning info on software renderer when get hardware one
      while (info)
 {
 aglDescribeRenderer(info, AGL_ACCELERATED, &dAccel);
 aglReportError ();
 if (dAccel)
   canAccel = true;
 info = aglNextRendererInfo(info);
 aglReportError ();
 inum++;
 }

      info = head_info;
      inum = 0;
      while (info)
 {
 aglDescribeRenderer(info, AGL_ACCELERATED, &dAccel);
 aglReportError ();
 // if we can accel then we will choose the accelerated renderer
 // how about compliant renderers???
 if ((canAccel && dAccel) || (!canAccel && (!fAccelMust || dAccel)))
   {
   aglDescribeRenderer(info, AGL_VIDEO_MEMORY, &dVRAM);
   aglReportError ();
   if (dVRAM >= (*pVRAM + *pTextureRAM))
     {
     if (dVRAM >= dMaxVRAM) // find card with max VRAM
       {// which depth buffer modes are supported
       aglDescribeRenderer(info, AGL_DEPTH_MODES, pDepthSizeSupport);
       aglReportError ();
       dMaxVRAM = dVRAM; // store max
       found = true;
       }
     }
   }
 info = aglNextRendererInfo(info);
 aglReportError ();
 inum++;
 }
      }
    aglDestroyRendererInfo(head_info);
    if (found) // found card with enough VRAM and meets the accel criteria
      {
      if (MinVRAM > dMaxVRAM)
 {
 MinVRAM = dMaxVRAM; // return VRAM
 }
      }
    else
      goodCheck = false; // one device failed thus entire requirement fails
    hGD = GetNextDevice (hGD); // get next device
    } // while
  if (goodCheck) // we check all devices and each was good
    {
    *pVRAM = MinVRAM; // return VRAM
    return true;
    }
  return false; //at least one device failed to have mins
}

//--------------------------------------------------------------------------
// FindGDHandleFromWindow
// Inputs:  a valid WindowPtr
// Outputs:  the GDHandle that that window is mostly on
// returns the number of devices that the windows content touches
short FindGDHandleFromWindow (WindowPtr pWindow, GDHandle * phgdOnThisDevice)
{
  GrafPtr pgpSave;
  Rect rectWind, rectSect;
  long greatestArea, sectArea;
  short numDevices = 0;
  GDHandle hgdNthDevice;

  if (!pWindow || !phgdOnThisDevice)
    return 0;

  *phgdOnThisDevice = NULL;

  GetPort (&pgpSave);
  SetPortWindowPort (pWindow);

  GetWindowPortBounds (pWindow, &rectWind);
  LocalToGlobal ((Point*)& rectWind.top);
  LocalToGlobal ((Point*)& rectWind.bottom);
  hgdNthDevice = GetDeviceList ();
  greatestArea = 0;
  // check window against all gdRects in gDevice list and remember
  // which gdRect contains largest area of window
  while (hgdNthDevice)
    {
    if (TestDeviceAttribute (hgdNthDevice, screenDevice))
      if (TestDeviceAttribute (hgdNthDevice, screenActive))
 {
 // The SectRect routine calculates the intersection
 //  of the window rectangle and this gDevice
 //  rectangle and returns TRUE if the rectangles intersect,
 //  FALSE if they don't.
 SectRect (&rectWind, &(**hgdNthDevice).gdRect, &rectSect);
 // determine which screen holds greatest window area
 //  first, calculate area of rectangle on current device
 sectArea = (long) ((rectSect.right - rectSect.left) * 
  (rectSect.bottom - rectSect.top));
 if (sectArea > 0)
   numDevices++;
 if (sectArea > greatestArea)
   {
   greatestArea = sectArea; // set greatest area so far
   *phgdOnThisDevice = hgdNthDevice; // set zoom device
   }
 hgdNthDevice = GetNextDevice(hgdNthDevice);
 }
    }
    SetPort (pgpSave);
    return numDevices;
}

//--------------------------------------------------------------------------
vtkCarbonRenderWindow::vtkCarbonRenderWindow()
{
  this->ApplicationInitialized = 0;
  this->ContextId = 0;
  this->MultiSamples = 8;
  this->WindowId = 0;
  this->ParentId = 0;
  this->RootWindow = 0;
  this->SetWindowName("Visualization Toolkit - Carbon");
  this->CursorHidden = 0;
  this->ForceMakeCurrent = 0;
  this->RegionEventHandlerUPP = 0;
  this->RegionEventHandler = 0;
}

// --------------------------------------------------------------------------
vtkCarbonRenderWindow::~vtkCarbonRenderWindow()
{
  this->Finalize();
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindow::Clean()
{
  GLuint txId;

  /* finish OpenGL rendering */
  if (this->ContextId)
    {
    this->MakeCurrent();

    /* now delete all textures */
    glDisable(GL_TEXTURE_2D);
    for (int i = 1; i < this->TextureResourceIds->GetNumberOfIds(); i++)
      {
      txId = (GLuint) this->TextureResourceIds->GetId(i);
#ifdef GL_VERSION_1_1
      if (glIsTexture(txId))
        {
        glDeleteTextures(1, &txId);
        }
#else
      if (glIsList(txId))
        {
        glDeleteLists(txId,1);
        }
#endif
      }

    // tell each of the renderers that this render window/graphics context
    // is being removed (the RendererCollection is removed by vtkRenderWindow's
    // destructor)
    vtkCollectionSimpleIterator rsit;
    vtkRenderer *ren;
    for ( this->Renderers->InitTraversal(rsit);
         (ren = this->Renderers->GetNextRenderer(rsit));)
      {
      ren->SetRenderWindow(NULL);
      }

    aglSetCurrentContext(this->ContextId);
    aglDestroyContext(this->ContextId);
    this->ContextId = NULL;
    }
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindow::SetWindowName( const char * _arg )
{
  vtkWindow::SetWindowName(_arg);

  if(this->OwnWindow)
    {
    CFStringRef newTitle =
      CFStringCreateWithCString(kCFAllocatorDefault, _arg,
                                kCFStringEncodingASCII);
    SetWindowTitleWithCFString(this->RootWindow, newTitle);
    CFRelease(newTitle);
    }
}

//--------------------------------------------------------------------------
int vtkCarbonRenderWindow::GetEventPending()
{
  return 0;
}

//--------------------------------------------------------------------------
// Set the window id to a pre-existing window.
void vtkCarbonRenderWindow::SetParentId(HIViewRef arg)
{
  vtkDebugMacro(<< "Setting ParentId to " << arg << "\n");

  this->ParentId = arg;
}

//--------------------------------------------------------------------------
// Begin the rendering process.
void vtkCarbonRenderWindow::Start()
{
  // if the renderer has not been initialized, do so now
  if (!this->ContextId)
    {
    this->Initialize();
    }

  // set the current window
  this->MakeCurrent();
}

// --------------------------------------------------------------------------
void vtkCarbonRenderWindow::MakeCurrent()
{
  if (this->ContextId || this->ForceMakeCurrent)
    {
    aglSetCurrentContext(this->ContextId);
    this->ForceMakeCurrent = 0;
    }
}

// --------------------------------------------------------------------------
void vtkCarbonRenderWindow::SetForceMakeCurrent()
{
  this->ForceMakeCurrent = 1;
}

// --------------------------------------------------------------------------
void vtkCarbonRenderWindow::SetSize(int a[2])
{
  this->SetSize(a[0], a[1]);
}

void vtkCarbonRenderWindow::UpdateGLRegion()
{
  // if WindowId is a child window
  if(this->WindowId)
    {
    // Determine the AGL_BUFFER_RECT for the view. The coordinate
    // system for this rectangle is relative to the owning window, with
    // the origin at the bottom left corner and the y-axis inverted.
    HIRect viewBounds, winBounds;
    HIViewGetBounds(this->WindowId, &viewBounds);
    HIViewRef root = HIViewGetRoot(this->GetRootWindow());
    HIViewRef content_root;
    HIViewFindByID(root, kHIViewWindowContentID, &content_root);

    HIViewGetBounds(content_root, &winBounds);
    HIViewConvertRect(&viewBounds, this->WindowId, content_root);
    GLint bufferRect[4] = { GLint(viewBounds.origin.x), 
                            GLint((winBounds.size.height) - (viewBounds.origin.y + viewBounds.size.height)), 
                            GLint(viewBounds.size.width), 
                            GLint(viewBounds.size.height) };
    if(!HIViewIsVisible(this->WindowId))
      {
      bufferRect[0] = 0;
      bufferRect[1] = 0;
      bufferRect[2] = 0;
      bufferRect[3] = 0;
      }
    
    // Associate the OpenGL context with the control's window, and establish the buffer rect.
    aglSetDrawable(this->ContextId, GetWindowPort(this->GetRootWindow()));
    aglSetInteger(this->ContextId, AGL_BUFFER_RECT, bufferRect);
    aglEnable(this->ContextId, AGL_BUFFER_RECT);
  
    // Establish the clipping region for the OpenGL context. To properly handle clipping
    // within the view hierarchy, walk the hierarchy to determine the intersection
    // of this view's bounds with its children, siblings, and parents also taking into 
    // account z-ordering of the views
    RgnHandle rgn = NewRgn();
    RgnHandle tmp_rgn = NewRgn();

    GetControlRegion(this->WindowId, kControlStructureMetaPart, rgn);
    HIViewConvertRegion(rgn, this->WindowId, content_root);

    HIViewRef current_view = NULL;
    HIViewRef child = NULL;
    HIViewRef last = NULL;

    for(current_view = this->WindowId; 
        (current_view != NULL); 
        current_view = HIViewGetSuperview(current_view))
      {
      if(last)
        {
        // clip view within parent bounds
        GetControlRegion(current_view, kControlStructureMetaPart, tmp_rgn);
        HIViewConvertRegion(tmp_rgn, current_view, content_root);
        DiffRgn(rgn, tmp_rgn, tmp_rgn);
        DiffRgn(rgn, tmp_rgn, rgn);
        }
      for(child = HIViewGetFirstSubview(current_view); 
          (child != last) && (child != NULL); 
          child = HIViewGetNextView(child))
        {
        if(child != last && HIViewIsVisible(child))
          {
          GetControlRegion(child, kControlStructureMetaPart, tmp_rgn);
          HIViewConvertRegion(tmp_rgn, child, content_root);
          DiffRgn(rgn, tmp_rgn, rgn);
          }
        }
        last = current_view;
      }
    
    GetControlRegion(this->WindowId, kControlStructureMetaPart, tmp_rgn);
    
    if(EqualRgn(rgn,tmp_rgn))
      {
      if(aglIsEnabled(this->ContextId, AGL_CLIP_REGION))
        aglDisable(this->ContextId, AGL_CLIP_REGION);
      }
    else
      {
      if(!aglIsEnabled(this->ContextId, AGL_CLIP_REGION))
        aglEnable(this->ContextId, AGL_CLIP_REGION);
      aglSetInteger(this->ContextId, AGL_CLIP_REGION, reinterpret_cast<const GLint*>(rgn));
      }
      
    DisposeRgn(rgn);
    DisposeRgn(tmp_rgn);

    }
  // this is provided for backwards compatibility
  else if(this->WindowId == 0 && this->RootWindow && this->ParentId)
    {
    GLint bufRect[4];
    Rect windowRect;
    GetWindowBounds(this->RootWindow, kWindowContentRgn, &windowRect);
    bufRect[0] = this->Position[0];
    bufRect[1] = (int) (windowRect.bottom-windowRect.top)
                 - (this->Position[1]+this->Size[1]);
    bufRect[2] = this->Size[0];
    bufRect[3] = this->Size[1];
    aglEnable(this->ContextId, AGL_BUFFER_RECT);
    aglSetInteger(this->ContextId, AGL_BUFFER_RECT, bufRect);
    }

  aglUpdateContext(this->ContextId);

}

// --------------------------------------------------------------------------
void vtkCarbonRenderWindow::SetSize(int x, int y)
{
  static int resizing = 0;

  if ((this->Size[0] != x) || (this->Size[1] != y))
    {
    this->Modified();
    this->Size[0] = x;
    this->Size[1] = y;
    if (this->Mapped)
      {
      if (!resizing)
        {
        resizing = 1;
        
        if(this->ParentId && this->RootWindow && !this->WindowId)
          {
          // backwards compatiblity with Tk and who else?
          UpdateGLRegion();
          }
        else if(this->OwnWindow || !this->WindowId)
          {
          SizeWindow(this->RootWindow, x, y, TRUE);
          }
        resizing = 0;
        }
      }
    }
}

// --------------------------------------------------------------------------
void vtkCarbonRenderWindow::SetPosition(int a[2])
{
  this->SetPosition(a[0], a[1]);
}

// --------------------------------------------------------------------------
void vtkCarbonRenderWindow::SetPosition(int x, int y)
{
  static int resizing = 0;

  if ((this->Position[0] != x) || (this->Position[1] != y))
    {
    this->Modified();
    this->Position[0] = x;
    this->Position[1] = y;
    if (this->Mapped)
      {
      if (!resizing)
        {
        resizing = 1;
        
        if(this->ParentId && this->RootWindow && !this->WindowId)
          {
          // backwards compatiblity with Tk and who else?
          UpdateGLRegion();
          }
        else if(this->OwnWindow || !this->WindowId)
          {
          MoveWindow(this->RootWindow, x, y, FALSE);
          }

        resizing = 0;
        }
      }
    }
}


//--------------------------------------------------------------------------
// End the rendering process and display the image.
void vtkCarbonRenderWindow::Frame()
{
  if (!this->AbortRender && this->DoubleBuffer)
    {
    aglSwapBuffers(this->ContextId);
    vtkDebugMacro(<< " SwapBuffers\n");
    }
}

//--------------------------------------------------------------------------
// Specify various window parameters.
void vtkCarbonRenderWindow::WindowConfigure()
{
  // this is all handled by the desiredVisualInfo method
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindow::SetupPixelFormat(void*, void*, int, int, int)
{
  cout << "vtkCarbonRenderWindow::SetupPixelFormat - IMPLEMENT\n";
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindow::SetupPalette(void*)
{
  cout << "vtkCarbonRenderWindow::SetupPalette - IMPLEMENT\n";
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindow::InitializeApplication()
{
  if (!this->ApplicationInitialized)
    {
    if (this->OwnWindow)
      { // Initialize the Toolbox managers if we are running the show
      InitCursor();
      DrawMenuBar();
      this->ApplicationInitialized=1;
      }
    }
}

//--------------------------------------------------------------------------
// Initialize the window for rendering.
void vtkCarbonRenderWindow::CreateAWindow(int vtkNotUsed(x), int vtkNotUsed(y),
                              int vtkNotUsed(width), int vtkNotUsed(height))
{
  GDHandle hGD = NULL;
  GLint depthSizeSupport;
  static int count = 1;
  short i;
  char *windowName;
  short numDevices;     // number of graphics devices our window covers

  if ((this->Size[0]+this->Size[1])==0)
    {
    this->Size[0]=300;
    this->Size[1]=300;
    }
  if ((this->Position[0]+this->Position[1])==0)
    {
    this->Position[0]=50;
    this->Position[1]=50;
    }

  // Rect is defined as {top, left, bottom, right} (really)
  Rect rectWin = {this->Position[1], this->Position[0],
                  this->Position[1]+this->Size[1],
                  this->Position[0]+this->Size[0]};
  
  // if a Window and HIView wasn't given, make a Window and HIView
  if (!this->WindowId && !this->RootWindow)
    {
      WindowAttributes windowAttrs = (kWindowStandardDocumentAttributes | 
                                      kWindowLiveResizeAttribute |
                                      kWindowStandardHandlerAttribute |
                                      kWindowCompositingAttribute);

      if (noErr != CreateNewWindow (kDocumentWindowClass,
                                    windowAttrs,
                                    &rectWin, &(this->RootWindow)))
        {
        vtkErrorMacro("Could not create window, serious error!");
        return;
        }
      
      // get the content view
      HIViewFindByID(HIViewGetRoot(this->RootWindow),
                     kHIViewWindowContentID,
                     &this->WindowId);

      int len = (strlen("vtkX - Carbon #")
                 + (int) ceil((double)log10((double)(count+1)))
                 + 1);
      windowName = new char [ len ];
      sprintf(windowName,"vtkX - Carbon #%i",count++);
      this->SetWindowName(windowName);
      delete [] windowName;
      this->OwnWindow = 1;

      ShowWindow(this->RootWindow);
    }


  // install event handler for updating gl region
  // this works for a supplied HIView and an HIView made here
  if(this->WindowId && !this->RegionEventHandler)
    {
    EventTypeSpec region_events [] = 
      {
        { kEventClassControl, kEventControlOwningWindowChanged},
        { kEventClassControl, kEventControlVisibilityChanged },
        { kEventClassControl, kEventControlBoundsChanged }
      };
    this->RegionEventHandlerUPP = NewEventHandlerUPP(vtkCarbonRenderWindow::RegionEventProcessor);
    InstallControlEventHandler(this->WindowId, this->RegionEventHandlerUPP, 
                               GetEventTypeCount(region_events), region_events,
                               reinterpret_cast<void*>(this), &this->RegionEventHandler);
    }
  
  SetPortWindowPort(this->GetRootWindow());
  this->fAcceleratedMust = false;  //must renderer be accelerated?
  this->VRAM = 0 * 1048576;    // minimum VRAM
  this->textureRAM = 0 * 1048576;  // minimum texture RAM
  this->fmt = 0;      // output pixel format
  i = 0;
  this->aglAttributes [i++] = AGL_RGBA;
  this->aglAttributes [i++] = AGL_DOUBLEBUFFER;
  this->aglAttributes [i++] = AGL_DEPTH_SIZE;
  this->aglAttributes [i++] = 32;
  this->aglAttributes [i++] = AGL_PIXEL_SIZE;
  this->aglAttributes [i++] = 32;
  this->aglAttributes [i++] = AGL_ACCELERATED;
  if (this->AlphaBitPlanes)
    {
    this->aglAttributes [i++] = AGL_ALPHA_SIZE;
    this->aglAttributes [i++] = 8;
    }
  this->aglAttributes [i++] = AGL_NONE;
  this->draggable = true;

  numDevices = FindGDHandleFromWindow(this->GetRootWindow(), &hGD);
  if (!this->draggable)
    {
    if ((numDevices > 1) || (numDevices == 0)) // multiple or no devices
      {
      // software renderer
      // infinite VRAM, infinite textureRAM, not accelerated
      if (this->fAcceleratedMust)
        {
        vtkErrorMacro ("Window spans multiple devices-no HW accel");
        return;
        }
      }
    else // not draggable on single device
      {
      if (!CheckRenderer (hGD, &(this->VRAM), &(this->textureRAM),
                          &depthSizeSupport, this->fAcceleratedMust))
        {
        vtkErrorMacro ("Renderer check failed");
        return;
        }
      }
    }
  // else if draggable - must check all devices for presence of
  // at least one renderer that meets the requirements
  else if(!CheckAllDeviceRenderers(&(this->VRAM), &(this->textureRAM),
   &depthSizeSupport, this->fAcceleratedMust))
    {
    vtkErrorMacro ("Renderer check failed");
    return;
    }

  // do agl
  if ((Ptr) kUnresolvedCFragSymbolAddress == (Ptr) aglChoosePixelFormat)
    {
    vtkErrorMacro ("OpenGL not installed");
    return;
    }
  // we successfully passed the renderer checks!
  
  if ((!this->draggable && (numDevices == 1)))
    {// not draggable on a single device
    this->fmt = aglChoosePixelFormat (&hGD, 1, this->aglAttributes);
    }
  else
    {
    this->fmt = aglChoosePixelFormat (NULL, 0, this->aglAttributes);
    }  
  aglReportError (); // cough up any errors encountered
  if (NULL == this->fmt)
    {
    vtkErrorMacro("Could not find valid pixel format");
    return;
    }
  
  this->ContextId = aglCreateContext (this->fmt, 0); // create without sharing
  aglReportError (); // cough up errors
  if (NULL == this->ContextId)
    {
    vtkErrorMacro ("Could not create context");
    return;
    }
  // attach the CGrafPtr to the context
  if (!aglSetDrawable (this->ContextId, GetWindowPort (this->GetRootWindow())))
    {
    aglReportError();
    return;
    }

  if(!aglSetCurrentContext (this->ContextId))
    // make the context the current context
    {
    aglReportError();
    return;
    }

  this->OpenGLInit();
  this->Mapped = 1;
  UpdateGLRegion();
}

//--------------------------------------------------------------------------
// Initialize the window for rendering.
void vtkCarbonRenderWindow::WindowInitialize()
{
  int x, y, width, height;
  x = ((this->Position[0] >= 0) ? this->Position[0] : 5);
  y = ((this->Position[1] >= 0) ? this->Position[1] : 5);
  height = ((this->Size[1] > 0) ? this->Size[1] : 300);
  width = ((this->Size[0] > 0) ? this->Size[0] : 300);
  
  // create our own window if not already set
  this->OwnWindow = 0;
  this->InitializeApplication();
  this->CreateAWindow(x,y,width,height);

  // tell our renderers about us
  vtkRenderer* ren;
  for (this->Renderers->InitTraversal(); 
       (ren = this->Renderers->GetNextItem());)
    {
    ren->SetRenderWindow(0);
    ren->SetRenderWindow(this);
    }
    
  // set the DPI
  this->SetDPI(72); // this may need to be more clever some day
}

//--------------------------------------------------------------------------
// Initialize the rendering window.
void vtkCarbonRenderWindow::Initialize ()
{
  // make sure we havent already been initialized

  if (this->ContextId)
    {
    return;
    }

  this->WindowInitialize();
}

void vtkCarbonRenderWindow::Finalize(void)
{
  if (this->CursorHidden)
    {
      this->ShowCursor();
    }

  if (this->OffScreenRendering) // does not exist yet
    {
      //this->CleanUpOffScreenRendering()
    }

  this->Clean();

  // remove event filters if we have them
  if(this->RegionEventHandler)
    {
    RemoveEventHandler(this->RegionEventHandler);
    DisposeEventHandlerUPP(this->RegionEventHandlerUPP);
    this->RegionEventHandler = 0;
    this->RegionEventHandlerUPP = 0;
    }

  if (this->RootWindow && this->OwnWindow)
    {
      DisposeWindow(this->RootWindow);
    }
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindow::UpdateSizeAndPosition(int xPos, int yPos, 
 int xSize, int ySize)
{
  this->Size[0]=xSize;
  this->Size[1]=ySize;
  this->Position[0]=xPos;
  this->Position[1]=yPos;
  this->Modified();
}

//--------------------------------------------------------------------------
// Get the current size of the window.
int *vtkCarbonRenderWindow::GetSize()
{
  // if we aren't mapped then just return the ivar
  if (!this->Mapped)
    {
    return this->Superclass::GetSize();
    }

  if(this->WindowId)
    {
    HIRect viewBounds;
    HIViewGetBounds(this->WindowId, &viewBounds);
    this->Size[0] = (int)viewBounds.size.width;
    this->Size[1] = (int)viewBounds.size.height;
    }

  return this->Superclass::GetSize();
}

//--------------------------------------------------------------------------
// Get the current size of the screen.
int *vtkCarbonRenderWindow::GetScreenSize()
{
  cout << "Inside vtkCarbonRenderWindow::GetScreenSize - MUST IMPLEMENT\n";
  this->Size[0] = 0;
  this->Size[1] = 0;

  return this->Size;
}

//--------------------------------------------------------------------------
// Get the position in screen coordinates of the window.
int *vtkCarbonRenderWindow::GetPosition()
{
  // if we aren't mapped then just return the ivar
  if (!this->Mapped)
    {
    return this->Position;
    }

  if(!this->WindowId && !this->ParentId)
    {
    //  Find the current window position
    Rect windowRect;
    GetWindowBounds(this->GetRootWindow(), kWindowContentRgn, &windowRect);
    this->Position[0] = windowRect.left;
    this->Position[1] = windowRect.top;
    }
  else
    {
    HIRect viewBounds;
    HIViewGetBounds(this->WindowId, &viewBounds);
    Rect windowRect;
    GetWindowBounds(this->GetRootWindow(), kWindowContentRgn, &windowRect);
    this->Position[0] = ((int)viewBounds.origin.x) + windowRect.left;
    this->Position[1] = ((int)viewBounds.origin.y) + windowRect.top;
    }
  
  return this->Position;
}

//--------------------------------------------------------------------------
// Change the window to fill the entire screen.
void vtkCarbonRenderWindow::SetFullScreen(int arg)
{
  int *temp;

  if (this->FullScreen == arg)
    {
    return;
    }

  if (!this->Mapped)
    {
    this->PrefFullScreen();
    return;
    }

  // set the mode
  this->FullScreen = arg;
  if (this->FullScreen <= 0)
    {
    this->Position[0] = this->OldScreen[0];
    this->Position[1] = this->OldScreen[1];
    this->Size[0] = this->OldScreen[2];
    this->Size[1] = this->OldScreen[3];
    this->Borders = this->OldScreen[4];
    }
  else
    {
    // if window already up get its values
    if (this->WindowId)
      {
      temp = this->GetPosition();
      this->OldScreen[0] = temp[0];
      this->OldScreen[1] = temp[1];

      this->OldScreen[4] = this->Borders;
      this->PrefFullScreen();
      }
    }

  // remap the window
  this->WindowRemap();

  this->Modified();
}

//--------------------------------------------------------------------------
// Set the variable that indicates that we want a stereo capable window
// be created. This method can only be called before a window is realized.
void vtkCarbonRenderWindow::SetStereoCapableWindow(int capable)
{
  if (this->WindowId == 0)
    {
    vtkRenderWindow::SetStereoCapableWindow(capable);
    }
  else
    {
    vtkWarningMacro(<< "Requesting a StereoCapableWindow must be performed "
      << "before the window is realized, i.e. before a render.");
    }
}

//--------------------------------------------------------------------------
// Set the preferred window size to full screen.
void vtkCarbonRenderWindow::PrefFullScreen()
{
  vtkWarningMacro(<< "Can't get full screen window.");
}

//--------------------------------------------------------------------------
// Remap the window.
void vtkCarbonRenderWindow::WindowRemap()
{
  vtkWarningMacro(<< "Can't remap the window.");
}

//--------------------------------------------------------------------------
void vtkCarbonRenderWindow::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "ContextId: " << this->ContextId << "\n";
  os << indent << "MultiSamples: " << this->MultiSamples << "\n";
}

//--------------------------------------------------------------------------
int vtkCarbonRenderWindow::GetDepthBufferSize()
{
  GLint size;

  if ( this->Mapped )
    {
    size = 0;
    glGetIntegerv( GL_DEPTH_BITS, &size );
    return (int) size;
    }
  else
    {
    vtkDebugMacro(<< "Window is not mapped yet!" );
    return 24;
    }
}

//--------------------------------------------------------------------------
// Get the window id.
HIViewRef vtkCarbonRenderWindow::GetWindowId()
{
  vtkDebugMacro(<< "Returning WindowId of " << this->WindowId << "\n");
  return this->WindowId;
}

//--------------------------------------------------------------------------
// Set the window id to a pre-existing window.
void vtkCarbonRenderWindow::SetWindowId(HIViewRef theWindow)
{
  vtkDebugMacro(<< "Setting WindowId to " << theWindow << "\n");
  this->WindowId = theWindow;
}

//--------------------------------------------------------------------------
// Set this RenderWindow's Carbon window id to a pre-existing window.
void vtkCarbonRenderWindow::SetWindowInfo(char *info)
{
  long tmp;

  sscanf(info,"%ld",&tmp);

  this->WindowId = (HIViewRef)tmp;
  vtkDebugMacro(<< "Setting WindowId to " << this->WindowId << "\n");
}

void vtkCarbonRenderWindow::SetRootWindow(WindowPtr win)
{
  vtkDebugMacro(<< "Setting RootWindow to " << win << "\n");
  this->RootWindow = win;
}

WindowPtr vtkCarbonRenderWindow::GetRootWindow()
{
  // take into account whether the user set the root window or not.
  // if not, then WindowId is set and we're using HIViews.
  // Instead of storing the RootWindow, we ask for it in case of a dynamic 
  // GUI where the root window can change
  return this->RootWindow ? this->RootWindow : HIViewGetWindow(this->WindowId);
}

//----------------------------------------------------------------------------
void vtkCarbonRenderWindow::HideCursor()
{
  if (this->CursorHidden)
    {
    return;
    }
  this->CursorHidden = 1;
  HideCursor();
}

//----------------------------------------------------------------------------
void vtkCarbonRenderWindow::ShowCursor()
{
  if (!this->CursorHidden)
    {
    return;
    }
  this->CursorHidden = 0;
  ShowCursor();
}

OSStatus vtkCarbonRenderWindow::RegionEventProcessor(EventHandlerCallRef, 
                                                     EventRef event, void* win)
{
  vtkCarbonRenderWindow* vtk_win = reinterpret_cast<vtkCarbonRenderWindow*>(win);
  UInt32 event_kind = GetEventKind(event);
  UInt32 event_class = GetEventClass(event);

  switch(event_class)
    {
    case kEventClassControl:
      {
      switch (event_kind)
        {
        case kEventControlVisibilityChanged:
        case kEventControlOwningWindowChanged:
        case kEventControlBoundsChanged:
          vtk_win->UpdateGLRegion();
          break;
        default:
          break;
        }
      }
      break;
    default:
      break;
    }

  return eventNotHandledErr;
}

