/* 
 * Copyright (c) 2010, 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 "MFPopup.h"
#import "MFMForms.h"

#include <cairo/cairo-quartz.h>

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

@implementation PopupContentView

- (id) initWithOwner: (mforms::Popup*) popup
{
  self = [self initWithFrame: NSMakeRect(0, 0, 10, 10)];
  if (self)
  {
    mOwner = popup;
  }
  return self;
}

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

- (void) drawRect: (NSRect)rect
{
  if (mOwner == nil)
    return;
  
  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);
}

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

- (BOOL) isFlipped
{
  return YES;
}

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

@end

@implementation MFPopupImpl

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

- (id) initWithObject: (mforms::Popup*) popup
{
  // Default size is by design.
  self = [super initWithContentRect: NSMakeRect(0, 0, 825, 351)
                          styleMask: NSHUDWindowMask | NSUtilityWindowMask //| NSTitledWindowMask
                            backing: NSBackingStoreBuffered
                              defer:YES];
  if (self)
  {
    mResult = 0;
    mDone = NO;
    mOwner = popup;
    mOwner->set_data(self);
    [self setHidesOnDeactivate: YES];
    [self setAcceptsMouseMovedEvents: YES];
    
    NSView* content = [[PopupContentView alloc] initWithOwner: popup];
    [self setContentView: content];
    [self setMovableByWindowBackground: NO];
    
    NSRect windowFrame = [self frame];
    windowFrame.origin.x = 26;
    windowFrame.origin.y = 14;
    windowFrame.size.width -= 52;
    windowFrame.size.height -= 28;
    [content setFrame: windowFrame]; 
  }
  return self;
}

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

- (BOOL) isFlipped
{
  return YES;
}

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

- (void) hidePopup
{
  mOwner = nil;
  
  [[NSAnimationContext currentContext] setDuration: 0.25];
  [[self animator] setAlphaValue: 0];
  [self performSelector: @selector(orderPopupOut) withObject: nil afterDelay: 0.5];
}

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

- (void) orderPopupOut
{
  [self orderOut: nil];
}

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

/**
 * Helper to avoid code duplication.
 */
- (void) doMouseUp: (NSEvent*) event
{
  if (mOwner == nil)
    return;
  
  NSPoint p = [[self contentView] 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;
    default:
      mOwner->mouse_up(button, p.x, p.y);
      break;
  }
}

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

/**
 * Helper to avoid code duplication.
 */
- (void) doMouseDown: (NSEvent*) event
{
  if (mOwner == nil)
    return;
  
  NSPoint p = [[self contentView] 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
{
  if (mOwner == nil)
    return;
  
  NSPoint p = [[self contentView] convertPoint: [event locationInWindow] fromView: nil];
  mOwner->mouse_move(p.x, p.y);
}
	
//--------------------------------------------------------------------------------------------------

- (void) mouseEntered: (NSEvent *) event
{
  if (mOwner != nil)
    mOwner->mouse_enter();
}

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

- (void) mouseExited: (NSEvent *) event
{
  if (mOwner != nil)
    mOwner->mouse_leave();
}

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

- (void) keyDown: (NSEvent *) event
{
  // Stop running the application modally for this window if the user pressed <escape>.
  unsigned short code = [event keyCode];
  if (code == 53) // Esc
  {
    mResult = 0;
    mDone = YES;
    [self hidePopup];
  }
}

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

- (BOOL) acceptsFirstResponder
{
  return YES;
}

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

- (BOOL) canBecomeKeyWindow
{
  return YES;
}

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

- (NSInteger) runModalAtPosition: (NSPoint) position
{
  // By design the window's upper right corner is to set at the given position.
  NSRect frame = [self frame];
  position.x -= frame.size.width;
  [self setFrameTopLeftPoint: position];
  
//  [self setAlphaValue: 0];
  [self makeKeyAndOrderFront: nil];
  
  [[NSAnimationContext currentContext] setDuration: 0.25];
  [[self animator] setAlphaValue: 1];
  //ret= [NSApp runModalForWindow: self];
    
  mDone = NO;
  
  while (!mDone) 
  {
    NSEvent *theEvent = [self nextEventMatchingMask:NSMouseMovedMask|NSMouseEnteredMask|NSMouseExitedMask|
                         NSLeftMouseDownMask|NSLeftMouseUpMask|NSRightMouseDownMask|NSRightMouseUpMask|
                         NSOtherMouseDownMask|NSOtherMouseUpMask|NSKeyDownMask|NSKeyUpMask];
    if ([theEvent type] == NSLeftMouseUp &&
        ![[self contentView] mouse: [theEvent locationInWindow] inRect:[[self contentView] bounds]])
    {
      [self hidePopup];
      mDone = YES;
    }
    else
    {
      [self sendEvent: theEvent];
    }
  }
  
  return mResult;
}

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

static bool popup_create(mforms::Popup *self, mforms::PopupStyle style)
{
  [[[MFPopupImpl alloc] initWithObject: self] autorelease];
  
  return true;  
}

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

static void popup_set_needs_repaint(mforms::Popup *self)
{
  [[self->get_data() contentView] setNeedsDisplay: YES];
}

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

static void popup_set_size(mforms::Popup *self, int w, int h)
{
  NSRect frame = [self->get_data() frame];
  frame.size.width = w;
  frame.size.height = h;
  [self->get_data() setFrame: frame];
}

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

static int popup_show(mforms::Popup *self, int x, int y)
{
  return [self->get_data() runModalAtPosition: NSMakePoint(x, y)];
}

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

static base::Rect popup_get_content_rect(mforms::Popup *self)
{
  NSRect frame = [[self->get_data() contentView] frame];
  return base::Rect(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
}

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

static void popup_set_modal_result(mforms::Popup *self, int result)
{
  MFPopupImpl *popup= (MFPopupImpl*)self->get_data();
  popup->mResult = result;
  popup->mDone = YES;
  [popup hidePopup];
}

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

void cf_popup_init()
{
  mforms::ControlFactory *f = mforms::ControlFactory::get_instance();
  
  f->_popup_impl.create = &popup_create;
  f->_popup_impl.set_needs_repaint = &popup_set_needs_repaint;
  f->_popup_impl.set_size = &popup_set_size;
  f->_popup_impl.show = &popup_show;
  f->_popup_impl.get_content_rect = &popup_get_content_rect;
  f->_popup_impl.set_modal_result = &popup_set_modal_result;
}

@end

