// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "chrome/browser/ui/cocoa/hover_close_button.h"

#include "base/mac/foundation_util.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/themes/theme_service.h"
#import "chrome/browser/ui/cocoa/browser_window_controller.h"
#import "chrome/browser/ui/cocoa/tabs/tab_view.h"
#include "chrome/grit/generated_resources.h"
#include "grit/components_strings.h"
#include "grit/theme_resources.h"
#import "third_party/google_toolbox_for_mac/src/AppKit/GTMKeyValueAnimation.h"
#include "ui/base/cocoa/animation_utils.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/material_design/material_design_controller.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image_skia_util_mac.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/vector_icons_public.h"
#include "ui/resources/grit/ui_resources.h"

namespace  {
const CGFloat kFramesPerSecond = 16; // Determined experimentally to look good.
const CGFloat kCloseAnimationDuration = 0.1;
const int kTabCloseButtonSize = 16;

// Strings that are used for all close buttons. Set up in +initialize.
NSString* gBasicAccessibilityTitle = nil;
NSString* gTooltip = nil;

// If this string is changed, the setter (currently setFadeOutValue:) must
// be changed as well to match.
NSString* const kFadeOutValueKeyPath = @"fadeOutValue";
}  // namespace

@interface HoverCloseButton ()

// Common initialization routine called from initWithFrame and awakeFromNib.
- (void)commonInit;

// Called by |fadeOutAnimation_| when animated value changes.
- (void)setFadeOutValue:(CGFloat)value;

// Gets the image for the given hover state.
- (NSImage*)imageForHoverState:(HoverState)hoverState;

@end

@implementation HoverCloseButton

+ (void)initialize {
  // Grab some strings that are used by all close buttons.
  if (!gBasicAccessibilityTitle) {
    gBasicAccessibilityTitle = [l10n_util::GetNSStringWithFixup(
        IDS_ACCNAME_CLOSE) copy];
  }
  if (!gTooltip)
    gTooltip = [l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_CLOSE_TAB) copy];
}

- (id)initWithFrame:(NSRect)frameRect {
  if ((self = [super initWithFrame:frameRect])) {
    [self commonInit];
  }
  return self;
}

- (void)awakeFromNib {
  [super awakeFromNib];
  [self commonInit];
}

- (void)removeFromSuperview {
  // -stopAnimation will call the animationDidStop: delegate method
  // which will release our animation.
  [fadeOutAnimation_ stopAnimation];
  [super removeFromSuperview];
}

- (void)animationDidStop:(NSAnimation*)animation {
  DCHECK(animation == fadeOutAnimation_);
  [fadeOutAnimation_ setDelegate:nil];
  [fadeOutAnimation_ release];
  fadeOutAnimation_ = nil;
}

- (void)animationDidEnd:(NSAnimation*)animation {
  [self animationDidStop:animation];
}

- (void)drawRect:(NSRect)dirtyRect {
  NSImage* image = [self imageForHoverState:[self hoverState]];

  // Close boxes align left horizontally, and align center vertically.
  // http:crbug.com/14739 requires this.
  NSRect imageRect = NSZeroRect;
  imageRect.size = [image size];

  NSRect destRect = [self bounds];
  destRect.origin.y = floor((NSHeight(destRect) / 2)
                            - (NSHeight(imageRect) / 2));
  destRect.size = imageRect.size;

  switch(self.hoverState) {
    case kHoverStateMouseOver:
    case kHoverStateMouseDown:
      [image drawInRect:destRect
               fromRect:imageRect
              operation:NSCompositeSourceOver
               fraction:1.0
         respectFlipped:YES
                  hints:nil];
      break;

    case kHoverStateNone: {
      CGFloat value = 1.0;
      if (fadeOutAnimation_) {
        value = [fadeOutAnimation_ currentValue];
        NSImage* previousImage = nil;
        if (previousState_ == kHoverStateMouseOver)
          previousImage = [self imageForHoverState:kHoverStateMouseOver];
        else
          previousImage = [self imageForHoverState:kHoverStateMouseDown];
        [previousImage drawInRect:destRect
                         fromRect:imageRect
                        operation:NSCompositeSourceOver
                         fraction:1.0 - value
                   respectFlipped:YES
                            hints:nil];
      }
      [image drawInRect:destRect
               fromRect:imageRect
              operation:NSCompositeSourceOver
               fraction:value
         respectFlipped:YES
                  hints:nil];
      break;
    }
  }
}

- (void)drawFocusRingMask {
  // Match the hover image's shape.
  NSRect circleRect = NSInsetRect([self bounds], 2, 2);
  [[NSBezierPath bezierPathWithOvalInRect:circleRect] fill];
}

- (void)setFadeOutValue:(CGFloat)value {
  [self setNeedsDisplay];
}

- (TabView *)tabView {
  return base::mac::ObjCCast<TabView>([self superview]);
}

- (NSImage*)imageForHoverState:(HoverState)hoverState {
  int imageID = IDR_CLOSE_1;

  if (!ui::MaterialDesignController::IsModeMaterial()) {
    switch (hoverState) {
      case kHoverStateNone:
        imageID = IDR_CLOSE_1;
        break;
      case kHoverStateMouseOver:
        imageID = IDR_CLOSE_1_H;
        break;
      case kHoverStateMouseDown:
        imageID = IDR_CLOSE_1_P;
        break;
    }
    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
    return bundle.GetNativeImageNamed(imageID).ToNSImage();
  }

  gfx::VectorIconId vectorIconID;
  SkColor vectorIconColor;
  const ui::ThemeProvider* themeProvider = nullptr;
  TabView* tabView;

  switch (hoverState) {
    case kHoverStateNone:
      vectorIconID = gfx::VectorIconId::TAB_CLOSE_NORMAL;
      // Determine the vector icon color, which for this icon is the color
      // of the "x".
      themeProvider = [[self window] themeProvider];
      if (themeProvider) {
        if (themeProvider->InIncognitoMode()) {
          vectorIconColor = SkColorSetARGB(0xFF, 0xC4, 0xC4, 0xC4);
        } else {
          tabView = [self tabView];
          bool use_active_tab_text_color = !tabView || [tabView isActiveTab];

          const SkColor titleColor = use_active_tab_text_color ?
              themeProvider->GetColor(ThemeProperties::COLOR_TAB_TEXT) :
              themeProvider->GetColor(
                  ThemeProperties::COLOR_BACKGROUND_TAB_TEXT);
          vectorIconColor = SkColorSetA(titleColor, 0xA0);
        }
      } else {
        // 0x000000 is the default COLOR_TAB_TEXT color.
        vectorIconColor = SkColorSetARGB(0xA0, 0x00, 0x00, 0x00);
      }
      break;
    case kHoverStateMouseOver:
      // For mouse over, the icon color is the fill color of the circle.
      vectorIconID = gfx::VectorIconId::TAB_CLOSE_HOVERED_PRESSED;
      vectorIconColor = SkColorSetARGB(0xFF, 0xDB, 0x44, 0x37);
      break;
    case kHoverStateMouseDown:
      // For mouse pressed, the icon color is the fill color of the circle.
      vectorIconID = gfx::VectorIconId::TAB_CLOSE_HOVERED_PRESSED;
      vectorIconColor = SkColorSetARGB(0xFF, 0xA8, 0x35, 0x2A);
      break;
  }

  return NSImageFromImageSkia(
      gfx::CreateVectorIcon(vectorIconID, kTabCloseButtonSize,
          vectorIconColor));
}

- (void)setHoverState:(HoverState)state {
  if (state != self.hoverState) {
    previousState_ = self.hoverState;
    [super setHoverState:state];
    // Only animate the HoverStateNone case and only if this is still in the
    // view hierarchy.
    if (state == kHoverStateNone && [self superview] != nil) {
      DCHECK(fadeOutAnimation_ == nil);
      fadeOutAnimation_ =
          [[GTMKeyValueAnimation alloc] initWithTarget:self
                                               keyPath:kFadeOutValueKeyPath];
      [fadeOutAnimation_ setDuration:kCloseAnimationDuration];
      [fadeOutAnimation_ setFrameRate:kFramesPerSecond];
      [fadeOutAnimation_ setDelegate:self];
      [fadeOutAnimation_ startAnimation];
    } else {
      // -stopAnimation will call the animationDidStop: delegate method
      // which will clean up the animation.
      [fadeOutAnimation_ stopAnimation];
    }
  }
}

- (void)commonInit {
  [self setAccessibilityTitle:nil];

  // Add a tooltip. Using 'owner:self' means that
  // -view:stringForToolTip:point:userData: will be called to provide the
  // tooltip contents immediately before showing it.
  [self addToolTipRect:[self bounds] owner:self userData:NULL];

  // Initialize previousState.
  previousState_ = kHoverStateNone;
}

// Called each time a tooltip is about to be shown.
- (NSString*)view:(NSView*)view
 stringForToolTip:(NSToolTipTag)tag
            point:(NSPoint)point
         userData:(void*)userData {
  if (self.hoverState == kHoverStateMouseOver) {
    // In some cases (e.g. the download tray), the button is still in the
    // hover state, but is outside the bounds of its parent and not visible.
    // Don't show the tooltip in that case.
    NSRect buttonRect = [self frame];
    NSRect parentRect = [[self superview] bounds];
    if (NSIntersectsRect(buttonRect, parentRect))
      return gTooltip;
  }

  return nil;  // Do not show the tooltip.
}

- (void)setAccessibilityTitle:(NSString*)accessibilityTitle {
  if (!accessibilityTitle) {
    [super setAccessibilityTitle:gBasicAccessibilityTitle];
    return;
  }

  NSString* extendedTitle = l10n_util::GetNSStringFWithFixup(
      IDS_ACCNAME_CLOSE_TAB,
      base::SysNSStringToUTF16(accessibilityTitle));
  [super setAccessibilityTitle:extendedTitle];
}

@end

@implementation WebUIHoverCloseButton

- (NSImage*)imageForHoverState:(HoverState)hoverState {
  int imageID = IDR_CLOSE_DIALOG;
  switch (hoverState) {
    case kHoverStateNone:
      imageID = IDR_CLOSE_DIALOG;
      break;
    case kHoverStateMouseOver:
      imageID = IDR_CLOSE_DIALOG_H;
      break;
    case kHoverStateMouseDown:
      imageID = IDR_CLOSE_DIALOG_P;
      break;
  }
  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
  return bundle.GetNativeImageNamed(imageID).ToNSImage();
}

@end