/*M///////////////////////////////////////////////////////////////////////////////////////
 //
 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
 //
 //  By downloading, copying, installing or using the software you agree to this license.
 //  If you do not agree to this license, do not download, install,
 //  copy or use the software.
 //
 //
 //                           License Agreement
 //                For Open Source Computer Vision Library
 //
 // Copyright (C) 2013, OpenCV Foundation, all rights reserved.
 // Third party copyrights are property of their respective owners.
 //
 // Redistribution and use in source and binary forms, with or without modification,
 // are permitted provided that the following conditions are met:
 //
 //   * Redistribution's of source code must retain the above copyright notice,
 //     this list of conditions and the following disclaimer.
 //
 //   * Redistribution's in binary form must reproduce the above copyright notice,
 //     this list of conditions and the following disclaimer in the documentation
 //     and/or other materials provided with the distribution.
 //
 //   * The name of the copyright holders may not be used to endorse or promote products
 //     derived from this software without specific prior written permission.
 //
 // This software is provided by the copyright holders and contributors "as is" and
 // any express or implied warranties, including, but not limited to, the implied
 // warranties of merchantability and fitness for a particular purpose are disclaimed.
 // In no event shall the Intel Corporation or contributors be liable for any direct,
 // indirect, incidental, special, exemplary, or consequential damages
 // (including, but not limited to, procurement of substitute goods or services;
 // loss of use, data, or profits; or business interruption) however caused
 // and on any theory of liability, whether in contract, strict liability,
 // or tort (including negligence or otherwise) arising in any way out of
 // the use of this software, even if advised of the possibility of such damage.
 //
 // Authors:
 //  * Anatoly Baksheev, Itseez Inc.  myname.mysurname <> mycompany.com
 //
 //  This workaround code was taken from PCL library(www.pointclouds.org)
 //
 //  Modified by Jasper Shemilt to work with VTK 6.2
 //  The fix was needed because GetCocoaServer has been moved from
 //  vtkCocoaRenderWindowInteractor to vtkCocoaRenderWindow in VTK 6.2.
 //  This alteration to VTK happened almost a year ago according to the gitHub
 //  commit a3e9fc9.
 //
 //M*/

#import <Cocoa/Cocoa.h>
#include <vtkCocoaRenderWindow.h>
#include <vtkCocoaRenderWindowInteractor.h>
#include <vtkObjectFactory.h>
#include <vtkSmartPointer.h>

//----------------------------------------------------------------------------
@interface vtkCocoaServerFix : NSObject
{
    vtkCocoaRenderWindow* renWin;
}

+ (id)cocoaServerWithRenderWindow:(vtkCocoaRenderWindow*)inRenderWindow;

- (void)start;
- (void)stop;
- (void)breakEventLoop;

@end

//----------------------------------------------------------------------------
@implementation vtkCocoaServerFix

//----------------------------------------------------------------------------
- (id)initWithRenderWindow:(vtkCocoaRenderWindow *)inRenderWindow
{
    self = [super init];
    if (self)
        renWin = inRenderWindow;
    return self;
}

//----------------------------------------------------------------------------
+ (id)cocoaServerWithRenderWindow:(vtkCocoaRenderWindow *)inRenderWindow
{
    vtkCocoaServerFix *server = [[[vtkCocoaServerFix alloc] initWithRenderWindow:inRenderWindow] autorelease];
    return server;
}

//----------------------------------------------------------------------------
- (void)start
{
    // Retrieve the NSWindow.
    NSWindow *win = nil;
    if (renWin)
    {
        win = reinterpret_cast<NSWindow*> (renWin->GetRootWindow ());

        // We don't want to be informed of every window closing, so check for nil.
        if (win != nil)
        {
            // Register for the windowWillClose notification in order to stop the run loop if the window closes.
            NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
            [nc addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:win];
        }
    }
    // Start the NSApplication's run loop
    NSApplication* application = [NSApplication sharedApplication];
    [application run];
}

//----------------------------------------------------------------------------
- (void)stop
{
    [self breakEventLoop];
}

//----------------------------------------------------------------------------
- (void)breakEventLoop
{
    NSApplication* application = [NSApplication sharedApplication];
    [application stop:application];

    NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined
                                        location:NSMakePoint(0.0,0.0)
                                   modifierFlags:0
                                       timestamp:0
                                    windowNumber:-1
                                         context:nil
                                         subtype:0
                                           data1:0
                                           data2:0];
    [application postEvent:event atStart:YES];
}

//----------------------------------------------------------------------------
- (void)windowWillClose:(NSNotification*)aNotification
{
    (void)aNotification;

    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc removeObserver:self name:NSWindowWillCloseNotification object:nil];

    if (renWin)
    {
        int windowCreated = renWin->GetWindowCreated ();
        if (windowCreated)
        {
            [self breakEventLoop];

            // The NSWindow is closing, so prevent anyone from accidently using it
            renWin->SetRootWindow(NULL);
        }
    }
}

@end

//----------------------------------------------------------------------------

#if VTK_MAJOR_VERSION >= 6 && VTK_MINOR_VERSION  >=2

namespace cv { namespace viz
    {
        class vtkCocoaRenderWindowInteractorFix : public vtkCocoaRenderWindowInteractor
        {
        public:
            static vtkCocoaRenderWindowInteractorFix *New ();
            vtkTypeMacro (vtkCocoaRenderWindowInteractorFix, vtkCocoaRenderWindowInteractor)

            virtual void Start ();
            virtual void TerminateApp ();

        protected:
            vtkCocoaRenderWindowInteractorFix () {}
            ~vtkCocoaRenderWindowInteractorFix () {}

        private:
            vtkCocoaRenderWindowInteractorFix (const vtkCocoaRenderWindowInteractorFix&);  // Not implemented.
            void operator = (const vtkCocoaRenderWindowInteractorFix&);  // Not implemented.
        };

        vtkStandardNewMacro (vtkCocoaRenderWindowInteractorFix)

        vtkSmartPointer<vtkRenderWindowInteractor> vtkCocoaRenderWindowInteractorNew();

        class vtkCocoaRenderWindowFix : public vtkCocoaRenderWindow
        {
        public:
            static vtkCocoaRenderWindowFix *New ();
            vtkTypeMacro ( vtkCocoaRenderWindowFix, vtkCocoaRenderWindow)

            virtual vtkCocoaServerFix * GetCocoaServer ();
            virtual void SetCocoaServer (void* );

        protected:
            vtkCocoaRenderWindowFix () {}
            ~vtkCocoaRenderWindowFix () {}

        private:
            vtkCocoaRenderWindowFix (const vtkCocoaRenderWindowInteractorFix&);  // Not implemented.
            void operator = (const vtkCocoaRenderWindowFix&);  // Not implemented.
        };

        vtkStandardNewMacro (vtkCocoaRenderWindowFix)

        vtkSmartPointer<vtkRenderWindow> vtkCocoaRenderWindowNew();
    }}

vtkCocoaServerFix * cv::viz::vtkCocoaRenderWindowFix::GetCocoaServer ()
{
    return reinterpret_cast<vtkCocoaServerFix*> (this->GetCocoaServer ());
}

void cv::viz::vtkCocoaRenderWindowFix::SetCocoaServer (void* server)
{
    this->SetCocoaServer (server);
}

void cv::viz::vtkCocoaRenderWindowInteractorFix::Start ()
{
    vtkCocoaRenderWindowFix* renWin = vtkCocoaRenderWindowFix::SafeDownCast(this->GetRenderWindow ());
    if (renWin != NULL)
    {
        vtkCocoaServerFix *server = reinterpret_cast<vtkCocoaServerFix*> (renWin->GetCocoaServer ());
        if (!renWin->GetCocoaServer ())
        {
            server = [vtkCocoaServerFix cocoaServerWithRenderWindow:renWin];
            renWin->SetCocoaServer (reinterpret_cast<void*> (server));
        }

        [server start];
    }
}

void cv::viz::vtkCocoaRenderWindowInteractorFix::TerminateApp ()
{
    vtkCocoaRenderWindowFix *renWin = vtkCocoaRenderWindowFix::SafeDownCast (this->RenderWindow);
    if (renWin)
    {
        vtkCocoaServerFix *server = reinterpret_cast<vtkCocoaServerFix*> (renWin->GetCocoaServer ());
        [server stop];
    }
}

vtkSmartPointer<vtkRenderWindowInteractor> cv::viz::vtkCocoaRenderWindowInteractorNew()
{
    return vtkSmartPointer<vtkCocoaRenderWindowInteractorFix>::New();
}

#else
namespace cv { namespace viz
    {
        class vtkCocoaRenderWindowInteractorFix : public vtkCocoaRenderWindowInteractor
        {
        public:
            static vtkCocoaRenderWindowInteractorFix *New ();
            vtkTypeMacro (vtkCocoaRenderWindowInteractorFix, vtkCocoaRenderWindowInteractor)

            virtual void Start ();
            virtual void TerminateApp ();

        protected:
            vtkCocoaRenderWindowInteractorFix () {}
            ~vtkCocoaRenderWindowInteractorFix () {}

        private:
            vtkCocoaRenderWindowInteractorFix (const vtkCocoaRenderWindowInteractorFix&);  // Not implemented.
            void operator = (const vtkCocoaRenderWindowInteractorFix&);  // Not implemented.
        };

        vtkStandardNewMacro (vtkCocoaRenderWindowInteractorFix)

        vtkSmartPointer<vtkRenderWindowInteractor> vtkCocoaRenderWindowInteractorNew();
    }}

void cv::viz::vtkCocoaRenderWindowInteractorFix::Start ()
{
    vtkCocoaRenderWindow* renWin = vtkCocoaRenderWindow::SafeDownCast(this->GetRenderWindow ());
    if (renWin != NULL)
    {
        vtkCocoaServerFix *server = reinterpret_cast<vtkCocoaServerFix*> (this->GetCocoaServer ());
        if (!this->GetCocoaServer ())
        {
            server = [vtkCocoaServerFix cocoaServerWithRenderWindow:renWin];
            this->SetCocoaServer (reinterpret_cast<void*> (server));
        }

        [server start];
    }
}

void cv::viz::vtkCocoaRenderWindowInteractorFix::TerminateApp ()
{
    vtkCocoaRenderWindow *renWin = vtkCocoaRenderWindow::SafeDownCast (this->RenderWindow);
    if (renWin)
    {
        vtkCocoaServerFix *server = reinterpret_cast<vtkCocoaServerFix*> (this->GetCocoaServer ());
        [server stop];
    }
}

vtkSmartPointer<vtkRenderWindowInteractor> cv::viz::vtkCocoaRenderWindowInteractorNew()
{
    return vtkSmartPointer<vtkCocoaRenderWindowInteractorFix>::New();
}

#endif