/** \file
 * \brief Button Control
 *
 * See Copyright Notice in "iup.h"
 */

#import <Cocoa/Cocoa.h>
#import <objc/runtime.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <stdarg.h>

#include "iup.h"
#include "iupcbs.h"

#include "iup_object.h"
#include "iup_layout.h"
#include "iup_attrib.h"
#include "iup_str.h"
#include "iup_image.h"
#include "iup_button.h"
#include "iup_drv.h"
#include "iup_drvfont.h"
#include "iup_image.h"
#include "iup_key.h"

#include "iupcocoa_drv.h"
#include "iupcocoa_keycodes.h"

static const void* IUP_COCOA_BUTTON_RECEIVER_OBJ_KEY = @"IUP_COCOA_BUTTON_RECEIVER_OBJ_KEY";

@interface IupCocoaButtonCell : NSButtonCell
{
  CGFloat _horizPadding;
  CGFloat _vertPadding;
  BOOL _hasUserPadding;
}
- (void)setIupPaddingHoriz:(CGFloat)horiz vert:(CGFloat)vert;
- (BOOL)hasUserPadding;
@end

@implementation IupCocoaButtonCell

- (instancetype)initTextCell:(NSString *)string
{
  self = [super initTextCell:string];
  if (self)
  {
    _horizPadding = 0;
    _vertPadding = 0;
    _hasUserPadding = NO;
  }
  return self;
}

- (instancetype)initWithCoder:(NSCoder *)coder
{
  self = [super initWithCoder:coder];
  if (self)
  {
    _horizPadding = 0;
    _vertPadding = 0;
    _hasUserPadding = NO;
  }
  return self;
}

- (void)setIupPaddingHoriz:(CGFloat)horiz vert:(CGFloat)vert
{
  _horizPadding = horiz;
  _vertPadding = vert;
  _hasUserPadding = YES;
}

- (BOOL)hasUserPadding
{
  return _hasUserPadding;
}

- (NSRect)drawingRectForBounds:(NSRect)rect
{
  if (!_hasUserPadding)
    return [super drawingRectForBounds:rect];

  NSRect drawingRect = [super drawingRectForBounds:rect];

  CGFloat currentInsetX = drawingRect.origin.x - rect.origin.x;
  CGFloat adjustmentX = _horizPadding - currentInsetX;
  drawingRect.origin.x += adjustmentX;
  drawingRect.size.width -= (2.0 * adjustmentX);

  CGFloat currentInsetY = drawingRect.origin.y - rect.origin.y;
  CGFloat adjustmentY = _vertPadding - currentInsetY;
  drawingRect.origin.y += adjustmentY;
  drawingRect.size.height -= (2.0 * adjustmentY);

  return drawingRect;
}

@end

@interface IupCocoaFlatButton : NSButton
@property (nonatomic, assign) BOOL isFlat;
@property (nonatomic, assign) BOOL isHovering;
@end

@implementation IupCocoaFlatButton

+ (Class)cellClass
{
  return [IupCocoaButtonCell class];
}

- (void)updateTrackingAreas
{
  [super updateTrackingAreas];

  for (NSTrackingArea *area in [self trackingAreas])
  {
    [self removeTrackingArea:area];
  }

  NSTrackingArea *trackingArea = [[NSTrackingArea alloc]
    initWithRect:[self bounds]
         options:(NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved |
                  NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect)
           owner:self
        userInfo:nil];

  [self addTrackingArea:trackingArea];
  [trackingArea release];
}

- (void)mouseEntered:(NSEvent *)event
{
  Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
  if (!ih) return;

  if (self.isFlat && !self.isHovering && [self isEnabled])
  {
    self.isHovering = YES;
    [self setBordered:YES];
    [self setNeedsDisplay:YES];
  }

  IFn cb = (IFn)IupGetCallback(ih, "ENTERWINDOW_CB");
  if (cb)
  {
    if (cb(ih) == IUP_CLOSE)
      IupExitLoop();
  }
}

- (void)mouseExited:(NSEvent *)event
{
  Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
  if (!ih) return;

  if (self.isFlat && self.isHovering)
  {
    self.isHovering = NO;
    [self setBordered:NO];
    [self setNeedsDisplay:YES];
  }

  IFn cb = (IFn)IupGetCallback(ih, "LEAVEWINDOW_CB");
  if (cb)
  {
    if (cb(ih) == IUP_CLOSE)
      IupExitLoop();
  }
}

- (void)mouseMoved:(NSEvent *)event
{
  [super mouseMoved:event];

  Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
  if (ih)
    iupcocoaCommonBaseHandleMouseMotionCallback(ih, event, self);
}

- (void)mouseDown:(NSEvent *)event
{
  Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
  if (ih)
    iupcocoaCommonBaseHandleMouseButtonCallback(ih, event, self, true);

  [self highlight:YES];

  BOOL keepTracking = YES;
  BOOL mouseIsInside = YES;
  NSPoint currentLocation;

  while (keepTracking)
  {
    NSEvent* nextEvent = [[self window] nextEventMatchingMask:(NSEventMaskLeftMouseUp | NSEventMaskLeftMouseDragged)];

    switch ([nextEvent type])
    {
      case NSEventTypeLeftMouseDragged:
        currentLocation = [self convertPoint:[nextEvent locationInWindow] fromView:nil];
        mouseIsInside = [self mouse:currentLocation inRect:[self bounds]];
        [self highlight:mouseIsInside];
        break;

      case NSEventTypeLeftMouseUp:
        currentLocation = [self convertPoint:[nextEvent locationInWindow] fromView:nil];
        mouseIsInside = [self mouse:currentLocation inRect:[self bounds]];

        [self highlight:NO];

        if (ih)
          iupcocoaCommonBaseHandleMouseButtonCallback(ih, nextEvent, self, false);

        if (mouseIsInside)
          [self performClick:self];

        keepTracking = NO;
        break;

      default:
        break;
    }
  }
}

- (void)mouseUp:(NSEvent *)event
{
  /* This method is kept for right and other mouse buttons which don't use custom tracking */
  [super mouseUp:event];

  Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
  if (ih)
    iupcocoaCommonBaseHandleMouseButtonCallback(ih, event, self, false);
}

- (void)rightMouseDown:(NSEvent *)event
{
  [super rightMouseDown:event];

  Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
  if (ih)
    iupcocoaCommonBaseHandleMouseButtonCallback(ih, event, self, true);
}

- (void)rightMouseUp:(NSEvent *)event
{
  [super rightMouseUp:event];

  Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
  if (ih)
    iupcocoaCommonBaseHandleMouseButtonCallback(ih, event, self, false);
}

- (void)otherMouseDown:(NSEvent *)event
{
  [super otherMouseDown:event];

  Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
  if (ih)
    iupcocoaCommonBaseHandleMouseButtonCallback(ih, event, self, true);
}

- (void)otherMouseUp:(NSEvent *)event
{
  [super otherMouseUp:event];

  Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
  if (ih)
    iupcocoaCommonBaseHandleMouseButtonCallback(ih, event, self, false);
}

- (void)scrollWheel:(NSEvent *)event
{
  [super scrollWheel:event];

  Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
  if (ih)
  {
    if (!iupcocoaCommonBaseScrollWheelCallback(ih, event, self))
      [super scrollWheel:event];
  }
}

- (void)keyDown:(NSEvent *)event
{
  Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
  if (ih)
  {
    int mac_key_code = [event keyCode];

    /* Enter activates the button when it has focus */
    if (mac_key_code == kVK_Return)
    {
      [self performClick:nil];
      return;
    }

    if (!iupcocoaKeyEvent(ih, event, mac_key_code, true))
      [super keyDown:event];
  }
  else
    [super keyDown:event];
}

- (void)keyUp:(NSEvent *)event
{
  Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
  if (ih)
  {
    int mac_key_code = [event keyCode];
    if (!iupcocoaKeyEvent(ih, event, mac_key_code, false))
      [super keyUp:event];
  }
  else
    [super keyUp:event];
}

- (void)flagsChanged:(NSEvent *)event
{
  Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
  if (ih)
  {
    int mac_key_code = [event keyCode];
    if (!iupcocoaModifierEvent(ih, event, mac_key_code))
      [super flagsChanged:event];
  }
  else
    [super flagsChanged:event];
}

- (BOOL)becomeFirstResponder
{
  BOOL result = [super becomeFirstResponder];
  if (result)
  {
    Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
    if (ih)
      iupcocoaFocusIn(ih);
  }
  return result;
}

- (BOOL)resignFirstResponder
{
  BOOL result = [super resignFirstResponder];
  if (result)
  {
    Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
    if (ih)
      iupcocoaFocusOut(ih);
  }
  return result;
}

- (BOOL)acceptsFirstResponder
{
  Ihandle* ih = (Ihandle*)objc_getAssociatedObject(self, IHANDLE_ASSOCIATED_OBJ_KEY);
  if (ih)
  {
    if (iupAttribGet(ih, "_IUPCOCOA_CANFOCUS"))
      return iupAttribGetBoolean(ih, "_IUPCOCOA_CANFOCUS");
    return iupAttribGetBoolean(ih, "CANFOCUS");
  }
  return [super acceptsFirstResponder];
}

- (BOOL) needsPanelToBecomeKey
{
  return YES;
}

@end


@interface IupCocoaButtonReceiver : NSObject
   - (IBAction)myButtonClickAction:(id)the_sender;
   @end

   @implementation IupCocoaButtonReceiver

   - (IBAction)myButtonClickAction:(id)the_sender
{
  Icallback callback_function;
  Ihandle* ih = (Ihandle*)objc_getAssociatedObject(the_sender, IHANDLE_ASSOCIATED_OBJ_KEY);

  callback_function = IupGetCallback(ih, "ACTION");
  if(callback_function)
  {
    if(callback_function(ih) == IUP_CLOSE)
    {
      IupExitLoop();
    }
  }
}
@end

static int text_border_x = -1, text_border_y = -1;
static int image_border_x = -1, image_border_y = -1;
static int image_text_border_x = -1, image_text_border_y = -1;

static void cocoaButtonMeasureBorders(Ihandle* ih, int has_image, int has_text, int img_position, int *border_x, int *border_y)
{
  NSButton* temp_button = [[NSButton alloc] initWithFrame:NSZeroRect];
  [temp_button setBezelStyle:NSBezelStyleRegularSquare];
  [temp_button setFont:[NSFont systemFontOfSize:0]];
  [temp_button setBordered:YES];

  if (has_image)
  {
    NSImage* temp_image = [[NSImage alloc] initWithSize:NSMakeSize(16, 16)];

    if (has_text)
    {
      [temp_button setTitle:@"Test"];
      [temp_button setImage:temp_image];

      NSCellImagePosition position;
      switch(img_position)
      {
        case IUP_IMGPOS_TOP:
          position = NSImageAbove;
          break;
        case IUP_IMGPOS_BOTTOM:
          position = NSImageBelow;
          break;
        case IUP_IMGPOS_RIGHT:
          position = NSImageRight;
          break;
        case IUP_IMGPOS_LEFT:
        default:
          position = NSImageLeft;
          break;
      }
      [temp_button setImagePosition:position];
    }
    else
    {
      [temp_button setImage:temp_image];
      [temp_button setImagePosition:NSImageOnly];
    }

    [temp_image release];
  }
  else
  {
    [temp_button setTitle:@"WWWWWWWWWW"];
    [temp_button setImagePosition:NSNoImage];
  }

  NSSize fitting_size = [temp_button fittingSize];
  NSSize intrinsic_size = [temp_button intrinsicContentSize];

  *border_x = (int)(fitting_size.width - intrinsic_size.width);
  *border_y = (int)(fitting_size.height - intrinsic_size.height);

  /* When intrinsic == fitting, calculate border using known content size */
  if (*border_x == 0 && *border_y == 0)
  {
    if (has_image && has_text)
    {
      int iup_text_w = iupdrvFontGetStringWidth(ih, "Test");
      int iup_text_h;
      iupdrvFontGetCharSize(ih, NULL, &iup_text_h);
      int content_w = 16 + 2 + iup_text_w;  /* image + spacing + text */
      int content_h = (16 > iup_text_h) ? 16 : iup_text_h;

      *border_x = (int)fitting_size.width - content_w;
      *border_y = (int)fitting_size.height - content_h;
    }
    else if (!has_image)
    {
      int iup_text_w = iupdrvFontGetStringWidth(ih, "WWWWWWWWWW");
      int iup_text_h;
      iupdrvFontGetCharSize(ih, NULL, &iup_text_h);

      *border_x = (int)fitting_size.width - iup_text_w;
      *border_y = (int)fitting_size.height - iup_text_h;
    }
  }

  if (*border_x < 0) *border_x = 0;
  if (*border_y < 0) *border_y = 0;

  [temp_button release];
}

void iupdrvButtonAddBorders(Ihandle* ih, int *x, int *y)
{
  int border_x = 0, border_y = 0;
  int has_image = 0;
  int has_text = 0;
  int has_bgcolor = 0;
  int has_user_padding = 0;
  int img_position = IUP_IMGPOS_LEFT;

  if (ih)
  {
    char* image = iupAttribGet(ih, "IMAGE");
    char* title = iupAttribGet(ih, "TITLE");
    char* bgcolor = iupAttribGet(ih, "BGCOLOR");
    has_image = (image != NULL);
    has_text = (title != NULL && *title != 0);
    has_bgcolor = (!has_image && !has_text && bgcolor != NULL);
    img_position = ih->data->img_position;

    int horiz_padding = 0, vert_padding = 0;
    char* padding = IupGetAttribute(ih, "PADDING");
    if (padding)
      iupStrToIntInt(padding, &horiz_padding, &vert_padding, 'x');
    has_user_padding = (horiz_padding > 0 || vert_padding > 0);
  }

  if (!has_user_padding)
  {
    if (has_bgcolor)
    {
      if (text_border_x == -1)
        cocoaButtonMeasureBorders(ih, 0, 1, IUP_IMGPOS_LEFT, &text_border_x, &text_border_y);

      border_x = text_border_x;
      border_y = text_border_y;
    }
    else if (has_image && has_text)
    {
      if (image_text_border_x == -1)
        cocoaButtonMeasureBorders(ih, 1, 1, img_position, &image_text_border_x, &image_text_border_y);

      border_x = image_text_border_x;
      border_y = image_text_border_y;
    }
    else if (has_image)
    {
      if (image_border_x == -1)
        cocoaButtonMeasureBorders(ih, 1, 0, IUP_IMGPOS_LEFT, &image_border_x, &image_border_y);

      border_x = image_border_x;
      border_y = image_border_y;
    }
    else
    {
      if (text_border_x == -1)
        cocoaButtonMeasureBorders(ih, 0, 1, IUP_IMGPOS_LEFT, &text_border_x, &text_border_y);

      border_x = text_border_x;
      border_y = text_border_y;
    }
  }

  *x += border_x;
  *y += border_y;
}

static int cocoaButtonSetTitleAttrib(Ihandle* ih, const char* value)
{
  NSButton* the_button = ih->handle;

  if (ih->data->type & IUP_BUTTON_TEXT)
  {
    if(value && *value!=0)
    {
      char* stripped_str = iupStrProcessMnemonic(value, NULL, 0);
      NSString* ns_string = [NSString stringWithUTF8String:stripped_str];

      if(stripped_str && stripped_str != value)
      {
        free(stripped_str);
      }

      char* fgcolor = iupAttribGet(ih, "FGCOLOR");
      unsigned char r, g, b;
      if (fgcolor && iupStrToRGB(fgcolor, &r, &g, &b))
      {
        NSColor* color = [NSColor colorWithCalibratedRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1.0];
        NSMutableAttributedString* attributed_title = [[NSMutableAttributedString alloc] initWithString:ns_string];
        NSRange range = NSMakeRange(0, [attributed_title length]);
        [attributed_title addAttribute:NSForegroundColorAttributeName value:color range:range];
        [the_button setAttributedTitle:attributed_title];
        [attributed_title release];
      }
      else
      {
        [the_button setTitle:ns_string];
      }

      /* Prevent text wrapping */
      [[the_button cell] setLineBreakMode:NSLineBreakByClipping];

      if(ih->data->type & IUP_BUTTON_IMAGE)
      {
        NSCellImagePosition position;
        switch(ih->data->img_position)
        {
          case IUP_IMGPOS_TOP:
            position = NSImageAbove;
            break;
          case IUP_IMGPOS_BOTTOM:
            position = NSImageBelow;
            break;
          case IUP_IMGPOS_RIGHT:
            position = NSImageRight;
            break;
          case IUP_IMGPOS_LEFT:
          default:
            position = NSImageLeft;
            break;
        }
        [the_button setImagePosition:position];
      }

      if(ih->handle)
      {
        IupRefresh(ih);
      }
      return 1;
    }
  }

  return 0;
}

static int cocoaButtonSetFontAttrib(Ihandle* ih, const char* value)
{
  if (!value)
    return 0;

  NSButton* the_button = ih->handle;
  IupCocoaFont* font = iupcocoaFindFont(value);

  if (font && font.nativeFont)
  {
    [the_button setFont:font.nativeFont];

    if (ih->handle)
      IupRefresh(ih);

    return 1;
  }

  return 0;
}

static int cocoaButtonSetAlignmentAttrib(Ihandle* ih, const char* value)
{
  NSButton* the_button = ih->handle;
  char value1[30], value2[30];
  NSTextAlignment alignment = NSTextAlignmentCenter;

  iupStrToStrStr(value, value1, value2, ':');

  if (iupStrEqualNoCase(value1, "ARIGHT"))
  {
    alignment = NSTextAlignmentRight;
    ih->data->horiz_alignment = IUP_ALIGN_ARIGHT;
  }
  else if (iupStrEqualNoCase(value1, "ALEFT"))
  {
    alignment = NSTextAlignmentLeft;
    ih->data->horiz_alignment = IUP_ALIGN_ALEFT;
  }
  else /* ACENTER (default) */
  {
    alignment = NSTextAlignmentCenter;
    ih->data->horiz_alignment = IUP_ALIGN_ACENTER;
  }

  [[the_button cell] setAlignment:alignment];

  if (iupStrEqualNoCase(value2, "ABOTTOM"))
    ih->data->vert_alignment = IUP_ALIGN_ABOTTOM;
  else if (iupStrEqualNoCase(value2, "ATOP"))
    ih->data->vert_alignment = IUP_ALIGN_ATOP;
  else /* ACENTER (default) */
    ih->data->vert_alignment = IUP_ALIGN_ACENTER;

  return 1;
}

static char* cocoaButtonGetAlignmentAttrib(Ihandle* ih)
{
  char* horiz_align2str[3] = {"ALEFT", "ACENTER", "ARIGHT"};
  char* vert_align2str[3] = {"ATOP", "ACENTER", "ABOTTOM"};

  int horiz = ih->data->horiz_alignment;
  int vert = ih->data->vert_alignment;

  if (horiz < IUP_ALIGN_ALEFT || horiz > IUP_ALIGN_ARIGHT)
    horiz = IUP_ALIGN_ACENTER;
  if (vert < IUP_ALIGN_ATOP || vert > IUP_ALIGN_ABOTTOM)
    vert = IUP_ALIGN_ACENTER;

  return iupStrReturnStrf("%s:%s", horiz_align2str[horiz], vert_align2str[vert]);
}

static int cocoaButtonSetPaddingAttrib(Ihandle* ih, const char* value)
{
  if (iupStrEqual(value, "DEFAULTBUTTONPADDING"))
    value = IupGetGlobal("DEFAULTBUTTONPADDING");

  iupStrToIntInt(value, &ih->data->horiz_padding, &ih->data->vert_padding, 'x');

  if (ih->handle)
  {
    NSButton* the_button = ih->handle;
    IupCocoaButtonCell* cell = (IupCocoaButtonCell*)[the_button cell];
    if (ih->data->horiz_padding > 0 || ih->data->vert_padding > 0)
      [cell setIupPaddingHoriz:ih->data->horiz_padding vert:ih->data->vert_padding];

    [the_button setNeedsDisplay:YES];
    IupRefresh(ih);
    return 0;
  }

  return 1;
}

static int cocoaButtonSetBgColorAttrib(Ihandle* ih, const char* value)
{
  NSButton* the_button = ih->handle;
  unsigned char r, g, b;

  if (!iupStrToRGB(value, &r, &g, &b))
    return 0;

  [the_button setWantsLayer:YES];
  [[the_button layer] setBackgroundColor:[[NSColor colorWithCalibratedRed:r/255.0
                                   green:g/255.0
                                    blue:b/255.0
                                   alpha:1.0] CGColor]];

  return 1;
}

static int cocoaButtonSetFgColorAttrib(Ihandle* ih, const char* value)
{
  NSButton* the_button = ih->handle;
  unsigned char r, g, b;

  if (!iupStrToRGB(value, &r, &g, &b))
    return 0;

  NSColor* color = [NSColor colorWithCalibratedRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1.0];
  NSString* title = [the_button title];

  if (title && [title length] > 0)
  {
    NSMutableAttributedString* attributed_title = [[NSMutableAttributedString alloc] initWithString:title];
    NSRange range = NSMakeRange(0, [attributed_title length]);
    [attributed_title addAttribute:NSForegroundColorAttributeName value:color range:range];
    [the_button setAttributedTitle:attributed_title];
    [attributed_title release];
  }

  return 1;
}

static int cocoaButtonSetImageAttrib(Ihandle* ih, const char* value)
{
  NSButton* the_button = ih->handle;

  if (ih->data->type & IUP_BUTTON_IMAGE)
  {
    NSImage* the_bitmap;
    int make_inactive = 0;

    if (!iupAttribGetBoolean(ih, "ACTIVE"))
      make_inactive = 1;

    the_bitmap = iupImageGetImage(value, ih, make_inactive, NULL);
    [the_button setImage:the_bitmap];

    if (ih->handle)
    {
      IupRefresh(ih);
    }
    return 1;
  }

  return 0;
}

static int cocoaButtonSetImInactiveAttrib(Ihandle* ih, const char* value)
{
  if (ih->data->type & IUP_BUTTON_IMAGE)
  {
    if (!iupAttribGetBoolean(ih, "ACTIVE"))
    {
      NSButton* the_button = ih->handle;
      NSImage* the_bitmap = iupImageGetImage(value, ih, 0, NULL);
      [the_button setImage:the_bitmap];
    }
    return 1;
  }

  return 0;
}

static int cocoaButtonSetImpressAttrib(Ihandle* ih, const char* value)
{
  NSButton* the_button = ih->handle;

  if (ih->data->type & IUP_BUTTON_IMAGE)
  {
    if (value && *value != 0)
    {
      int make_inactive = 0;
      if (!iupAttribGetBoolean(ih, "ACTIVE"))
        make_inactive = 1;

      NSImage* the_bitmap = iupImageGetImage(value, ih, make_inactive, NULL);
      [the_button setAlternateImage:the_bitmap];
    }
    else
    {
      [the_button setAlternateImage:nil];
    }

    if (ih->handle)
    {
      IupRefresh(ih);
    }
    return 1;
  }

  return 0;
}

static int cocoaButtonSetActiveAttrib(Ihandle* ih, const char* value)
{
  NSButton* the_button = ih->handle;
  int active = iupStrBoolean(value);

  [the_button setEnabled:active];

  if (ih->data->type & IUP_BUTTON_IMAGE)
  {
    char* image_name = iupAttribGet(ih, active ? "IMAGE" : "IMINACTIVE");
    if (!image_name && !active)
      image_name = iupAttribGet(ih, "IMAGE");

    if (image_name)
    {
      int make_inactive = !active && !iupAttribGet(ih, "IMINACTIVE");
      NSImage* the_bitmap = iupImageGetImage(image_name, ih, make_inactive, NULL);
      [the_button setImage:the_bitmap];
    }

    char* impress_name = iupAttribGet(ih, "IMPRESS");
    if (impress_name)
    {
      int make_inactive = !active;
      NSImage* the_bitmap = iupImageGetImage(impress_name, ih, make_inactive, NULL);
      [the_button setAlternateImage:the_bitmap];
    }
  }

  return iupBaseSetActiveAttrib(ih, value);
}

void cocoaButtonLayoutUpdateMethod(Ihandle *ih)
{
  NSView* parent_view = iupcocoaCommonBaseLayoutGetParentView(ih);
  NSView* child_view = iupcocoaCommonBaseLayoutGetChildView(ih);

  if (!parent_view || !child_view)
  {
    return;
  }

  NSRect parent_rect = [parent_view frame];
  NSRect child_rect;

  if ([parent_view isFlipped])
  {
    child_rect = NSMakeRect(
        ih->x,
        ih->y,
        ih->currentwidth,
        ih->currentheight
    );
  }
  else
  {
    child_rect = NSMakeRect(
        ih->x,
        parent_rect.size.height - ih->y - ih->currentheight,
        ih->currentwidth,
        ih->currentheight
    );
  }

  (void)child_view;

  [child_view setFrame:NSIntegralRect(child_rect)];
}

static int cocoaButtonMapMethod(Ihandle* ih)
{
  char* value;
  NSButton* the_button;
  int has_border = 1;
  BOOL is_flat = iupAttribGetBoolean(ih, "FLAT");

  the_button = [[IupCocoaFlatButton alloc] initWithFrame:NSZeroRect];
  [(IupCocoaFlatButton*)the_button setIsFlat:is_flat];
  [(IupCocoaFlatButton*)the_button setIsHovering:NO];

  [the_button setTitle:@""];
  [the_button setBezelStyle:NSBezelStyleRegularSquare];
  [the_button setButtonType:NSButtonTypeMomentaryPushIn];

  value = iupAttribGet(ih, "IMAGE");
  if(value)
  {
    ih->data->type = IUP_BUTTON_IMAGE;

    const char* title = iupAttribGet(ih, "TITLE");
    if (title && *title!=0)
    {
      ih->data->type |= IUP_BUTTON_TEXT;

      char* stripped_str = iupStrProcessMnemonic(title, NULL, 0);
      NSString* ns_string = [NSString stringWithUTF8String:stripped_str];

      if(stripped_str && stripped_str != title)
        free(stripped_str);

      [the_button setTitle:ns_string];

      NSCellImagePosition position;
      switch(ih->data->img_position)
      {
        case IUP_IMGPOS_TOP:
          position = NSImageAbove;
          break;
        case IUP_IMGPOS_BOTTOM:
          position = NSImageBelow;
          break;
        case IUP_IMGPOS_RIGHT:
          position = NSImageRight;
          break;
        case IUP_IMGPOS_LEFT:
        default:
          position = NSImageLeft;
          break;
      }
      [the_button setImagePosition:position];
    }
    else
    {
      [the_button setImagePosition:NSImageOnly];
    }

    if (iupAttribGet(ih, "IMPRESS") && !iupAttribGetBoolean(ih, "IMPRESSBORDER"))
    {
      has_border = 0;
      [the_button setButtonType:NSButtonTypeMomentaryChange];
      [the_button setBordered:NO];
    }

    NSImage* the_bitmap;
    int make_inactive = 0;

    if(iupAttribGet(ih, "IMINACTIVE") && !iupAttribGetBoolean(ih, "ACTIVE"))
    {
      value = iupAttribGet(ih, "IMINACTIVE");
    }
    else if(!iupAttribGetBoolean(ih, "ACTIVE"))
    {
      make_inactive = 1;
    }

    the_bitmap = iupImageGetImage(value, ih, make_inactive, NULL);
    [the_button setImage:the_bitmap];

    value = iupAttribGet(ih, "IMPRESS");
    if(value && *value!=0)
    {
      the_bitmap = iupImageGetImage(value, ih, make_inactive, NULL);
      [the_button setAlternateImage:the_bitmap];
    }
  }
  else
  {
    ih->data->type = IUP_BUTTON_TEXT;
  }

  [the_button setFont:[NSFont systemFontOfSize:0]];
  [[the_button cell] setLineBreakMode:NSLineBreakByClipping];

  if (ih->data->horiz_padding != 0 || ih->data->vert_padding != 0)
  {
    IupCocoaButtonCell* cell = (IupCocoaButtonCell*)[the_button cell];
    [cell setIupPaddingHoriz:ih->data->horiz_padding vert:ih->data->vert_padding];
  }

  ih->handle = the_button;
  iupcocoaSetAssociatedViews(ih, the_button, the_button);

  objc_setAssociatedObject(the_button, IHANDLE_ASSOCIATED_OBJ_KEY, (id)ih, OBJC_ASSOCIATION_ASSIGN);

  IupCocoaButtonReceiver* button_receiver = [[IupCocoaButtonReceiver alloc] init];
  [the_button setTarget:button_receiver];
  [the_button setAction:@selector(myButtonClickAction:)];
  objc_setAssociatedObject(the_button, IUP_COCOA_BUTTON_RECEIVER_OBJ_KEY, (id)button_receiver, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

  iupcocoaAddToParent(ih);

  if (!iupAttribGetBoolean(ih, "CANFOCUS"))
  {
    [the_button setRefusesFirstResponder:YES];
    iupcocoaSetCanFocus(ih, 0);
  }
  else
  {
    iupcocoaSetCanFocus(ih, 1);
  }

  if (is_flat && has_border)
  {
    [the_button setBordered:NO];
    [the_button updateTrackingAreas];
  }

  if (!iupAttribGetBoolean(ih, "ACTIVE"))
    [the_button setEnabled:NO];

  return IUP_NOERROR;
}

static void cocoaButtonUnMapMethod(Ihandle* ih)
{
  id the_button = ih->handle;

  Ihandle* context_menu_ih = (Ihandle*)iupcocoaCommonBaseGetContextMenuAttrib(ih);
  if(context_menu_ih)
  {
    IupDestroy(context_menu_ih);
    iupcocoaCommonBaseSetContextMenuAttrib(ih, NULL);
  }

  [the_button setTarget:nil];
  id button_receiver = objc_getAssociatedObject(the_button, IUP_COCOA_BUTTON_RECEIVER_OBJ_KEY);
  objc_setAssociatedObject(the_button, IUP_COCOA_BUTTON_RECEIVER_OBJ_KEY, nil, OBJC_ASSOCIATION_ASSIGN);
  [button_receiver release];

  iupcocoaRemoveFromParent(ih);
  iupcocoaSetAssociatedViews(ih, nil, nil);

  [the_button release];
  ih->handle = NULL;
}

void iupdrvButtonInitClass(Iclass* ic)
{
  ic->Map = cocoaButtonMapMethod;
  ic->UnMap = cocoaButtonUnMapMethod;
  ic->LayoutUpdate = cocoaButtonLayoutUpdateMethod;

  iupClassRegisterAttribute(ic, "BGCOLOR", NULL, cocoaButtonSetBgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGBGCOLOR", IUPAF_DEFAULT);
  iupClassRegisterAttribute(ic, "FGCOLOR", NULL, cocoaButtonSetFgColorAttrib, IUPAF_SAMEASSYSTEM, "DLGFGCOLOR", IUPAF_DEFAULT);
  iupClassRegisterAttribute(ic, "ACTIVE", iupBaseGetActiveAttrib, cocoaButtonSetActiveAttrib, IUPAF_SAMEASSYSTEM, "YES", IUPAF_DEFAULT);
  iupClassRegisterAttribute(ic, "FONT", NULL, cocoaButtonSetFontAttrib, IUPAF_SAMEASSYSTEM, "DEFAULTFONT", IUPAF_NOT_MAPPED);

  iupClassRegisterAttribute(ic, "TITLE", NULL, cocoaButtonSetTitleAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "ALIGNMENT", cocoaButtonGetAlignmentAttrib, cocoaButtonSetAlignmentAttrib, "ACENTER:ACENTER", NULL, IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "IMAGE", NULL, cocoaButtonSetImageAttrib, NULL, NULL, IUPAF_IHANDLENAME|IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "IMINACTIVE", NULL, cocoaButtonSetImInactiveAttrib, NULL, NULL, IUPAF_IHANDLENAME|IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "IMPRESS", NULL, cocoaButtonSetImpressAttrib, NULL, NULL, IUPAF_IHANDLENAME|IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "IMPRESSBORDER", NULL, NULL, NULL, NULL, IUPAF_NO_DEFAULTVALUE|IUPAF_NO_INHERIT);
  iupClassRegisterAttribute(ic, "PADDING", iupButtonGetPaddingAttrib, cocoaButtonSetPaddingAttrib, IUPAF_SAMEASSYSTEM, "0x0", IUPAF_NOT_MAPPED);

  iupClassRegisterAttribute(ic, "MARKUP", NULL, NULL, NULL, NULL, IUPAF_NOT_SUPPORTED);

  iupClassRegisterAttribute(ic, "LAYERBACKED", iupCocoaCommonBaseGetLayerBackedAttrib, iupcocoaCommonBaseSetLayerBackedAttrib, NULL, NULL, IUPAF_NO_DEFAULTVALUE);
}
