#import "BasicVTKView.h"

#import "vtkRenderer.h"
#import "vtkRenderWindow.h"
#import "vtkRenderWindowInteractor.h"
#import "vtkCocoaRenderWindowInteractor.h"
#import "vtkCocoaRenderWindow.h"

// Private Interface
@interface BasicVTKView()
{
  @private
  vtkRenderer* _renderer;
}
@end

@implementation BasicVTKView

// ----------------------------------------------------------------------------
// Designated initializer
- (instancetype)initWithFrame:(NSRect)frameRect
{
  self = [super initWithFrame:frameRect];
  if (self)
  {
    // nothing to do... add something if you need to
  }

  return self;
}

// ----------------------------------------------------------------------------
// Designated initializer
- (/*nullable*/ instancetype)initWithCoder:(NSCoder *)coder
{
  self = [super initWithCoder:coder];
  if (self)
  {
    // nothing to do... add something if you need to
  }

  return self;
}

// ----------------------------------------------------------------------------
// We are going to override the super class here to do some last minute
// setups. We need to do this because if we initialize in the constructor or
// even later, in say an NSDocument's windowControllerDidLoadNib, then
// we will get a warning about "Invalid Drawable" because the OpenGL Context
// is trying to be set and rendered into an NSView that most likely is not
// on screen yet. This is a way to defer that initialization until the NSWindow
// that contains our NSView subclass is actually on screen and ready to be drawn.
- (void)drawRect:(NSRect)theRect
{
  // Check for a valid vtkWindowInteractor and then initialize it. Technically we
  // do not need to do this, but what happens is that the window that contains
  // this object will not immediately render it so you end up with a big empty
  // space in your gui where this NSView subclass should be. This may or may
  // not be what is wanted. If you allow this code then what you end up with is the
  // typical empty black OpenGL view which seems more 'correct' or at least is
  // more soothing to the eye.
  vtkRenderWindowInteractor* theRenWinInt = [self getInteractor];
  if (theRenWinInt && (theRenWinInt->GetInitialized() == NO))
  {
    theRenWinInt->Initialize();
  }

  // Let the vtkCocoaGLView do its regular drawing
  [super drawRect:theRect];
}

// ----------------------------------------------------------------------------
- (void)initializeVTKSupport
{
  // The usual vtk object creation.
  vtkRenderer* ren = vtkRenderer::New();
  vtkRenderWindow* renWin = vtkRenderWindow::New();
  vtkRenderWindowInteractor* renWinInt = vtkRenderWindowInteractor::New();

  // The cast should never fail, but we do it anyway, as
  // it's more correct to do so.
  vtkCocoaRenderWindow* cocoaRenWin = vtkCocoaRenderWindow::SafeDownCast(renWin);

  if (ren && cocoaRenWin && renWinInt)
  {
    // This is special to our usage of vtk.  To prevent vtk
    // from creating an NSWindow and NSView automatically (its
    // default behaviour) we tell vtk that they exist already.
    // The APIs names are a bit misleading, due to the cross
    // platform nature of vtk, but this usage is correct.
    cocoaRenWin->SetRootWindow((__bridge void*)[self window]);
    cocoaRenWin->SetWindowId((__bridge void*)self);

    // The usual vtk connections
    cocoaRenWin->AddRenderer(ren);
    renWinInt->SetRenderWindow(cocoaRenWin);

    // This is special to our usage of vtk.  vtkCocoaGLView
    // keeps track of the renderWindow, and has a get
    // accessor if you ever need it.
    [self setVTKRenderWindow:cocoaRenWin];

    // Likewise, BasicVTKView keeps track of the renderer.
    [self setRenderer:ren];
  }
}

// ----------------------------------------------------------------------------
- (void)cleanUpVTKSupport
{
  vtkRenderer* ren = [self getRenderer];
  vtkRenderWindow* renWin = [self getVTKRenderWindow];
  vtkRenderWindowInteractor* renWinInt = [self getInteractor];

  if (ren)
  {
    ren->Delete();
  }
  if (renWin)
  {
    renWin->Delete();
  }
  if (renWinInt)
  {
    renWinInt->Delete();
  }

  [self setRenderer:nil];
  [self setVTKRenderWindow:nil];

  // There is no setter accessor for the render window
  // interactor, that's ok.
}

// ----------------------------------------------------------------------------
- (/*nullable*/ vtkRenderer*)getRenderer
{
  return _renderer;
}

// ----------------------------------------------------------------------------
- (void)setRenderer:(/*nullable*/ vtkRenderer*)theRenderer
{
  _renderer = theRenderer;
}

@end
