/* 
 * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#import "MFDrawBox.h"
#import "MFMForms.h"
#include <cairo/cairo-quartz.h>

@implementation MFDrawBoxImpl

- (id)initWithObject:(::mforms::DrawBox*)aBox
{
  self= [super initWithFrame:NSMakeRect(10,10,10,10)];
  if (self)
  {
    mOwner= aBox;
    mOwner->set_data(self);
    mTag= 0;
  }
  return self;
}

- (void) dealloc
{
  [super dealloc];
}



- (mforms::Object*)mformsObject
{
  return mOwner;
}

- (BOOL)isFlipped
{
  return YES;
}

- (void)setTag:(NSInteger)tag
{
  mTag= tag;
}


- (NSInteger)tag
{
  return mTag;
}

- (void)drawRect:(NSRect)rect
{
  NSRect frame = [self frame];

  CGContextRef cgref = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];

  cairo_surface_t *surface= cairo_quartz_surface_create_for_cg_context(cgref, NSWidth(frame), NSHeight(frame));
  
  cairo_t *cr = cairo_create(surface);
  try
  {
    mOwner->repaint(cr, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
  }
  catch (...)
  {
    cairo_destroy(cr);
    cairo_surface_destroy(surface);
    throw;
  }
  cairo_destroy(cr);
  cairo_surface_destroy(surface);
}

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

/**
 * Helper to avoid code duplication.
 */
- (void) doMouseUp: (NSEvent*) event
{
  NSPoint p = [self convertPoint: [event locationInWindow] fromView: nil];
  NSInteger button= [event buttonNumber];
  switch ([event clickCount])
  {
    case 1:
      mOwner->mouse_click(button, p.x, p.y);
      break;
    case 2:
      mOwner->mouse_double_click(button, p.x, p.y);
      break;
  }
  // mouse up is always sent
  mOwner->mouse_up(button, p.x, p.y);
}

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

/**
 * Helper to avoid code duplication.
 */
- (void) doMouseDown: (NSEvent*) event
{
  NSPoint p = [self convertPoint: [event locationInWindow] fromView: nil];
  NSInteger button= [event buttonNumber];
  mOwner->mouse_down(button, p.x, p.y);
}

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

- (void)mouseDown:(NSEvent*)event
{
  [self doMouseDown: event];
}

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

- (void)mouseUp:(NSEvent*)event
{
  [self doMouseUp: event];
}

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

- (void)otherMouseDown:(NSEvent*)event
{
  [self doMouseDown: event];
}

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

- (void)otherMouseUp:(NSEvent*)event
{
  [self doMouseUp: event];
}

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

- (void)rightMouseDown:(NSEvent*)event
{
  [self doMouseDown: event];
}

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

- (void)rightMouseUp:(NSEvent*)event
{
  [self doMouseUp: event];
}

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

- (void)mouseMoved:(NSEvent *)event
{
  NSPoint p = [self convertPoint: [event locationInWindow] fromView: nil];
  mOwner->mouse_move(p.x, p.y);
}

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

- (void)mouseEntered:(NSEvent *)event
{
  mOwner->mouse_enter();
}

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

- (void)mouseExited:(NSEvent *)event
{
  mOwner->mouse_leave();
}

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

- (BOOL)acceptsFirstResponder
{
  return YES;
}

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

- (void) updateTrackingAreas
{
  [super updateTrackingAreas];

  // Create one tracking area which covers the whole control and make it get mouse events
  // only when the mouse is within the area but regardless of the first responder status.
  [self removeTrackingArea: mTrackingArea];
  [mTrackingArea release];
  
  NSTrackingAreaOptions options= NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | 
                                 NSTrackingCursorUpdate | NSTrackingActiveInActiveApp |
                                 NSTrackingInVisibleRect;
  mTrackingArea= [[NSTrackingArea alloc] initWithRect: [self bounds] options: options owner: self userInfo: nil];
  [self addTrackingArea: mTrackingArea];
}

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

- (void) invalidate
{
  [self setNeedsDisplay: YES];
}

- (NSSize)preferredSize
{
  if (mOwner == NULL || mOwner->is_destroying())
    return NSZeroSize;
  
  int w, h;
  mOwner->get_layout_size(&w, &h);
  NSSize size= NSMakeSize(w, h);
  
  // If a fixed size is set honour that but don't go below the
  // minimal required size.
  if ([self widthIsFixed])
    size.width= MAX(size.width, NSWidth([self frame]));
  if ([self heightIsFixed])
    size.height= MAX(size.height, NSHeight([self frame]));
  return size;
}

- (NSSize)minimumSize
{
  return [self preferredSize];
}

static bool drawbox_create(::mforms::DrawBox *self)
{
  [[[MFDrawBoxImpl alloc] initWithObject:self] autorelease];
  
  return true;  
}

static void drawbox_set_needs_repaint(::mforms::DrawBox *self)
{
  // Invalidate the draw box in a thread safe manner.
  // See also http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html
  
  [self->get_data() performSelectorOnMainThread: @selector(invalidate)
                                    withObject: nil
                                  waitUntilDone: NO];
}


void cf_drawbox_init()
{
  ::mforms::ControlFactory *f = ::mforms::ControlFactory::get_instance();
  
  f->_drawbox_impl.create= &drawbox_create;
  f->_drawbox_impl.set_needs_repaint= &drawbox_set_needs_repaint;
}

@end


