// Copyright 2013 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/autofill/autofill_suggestion_container.h"

#include <algorithm>
#include <cmath>

#include "base/logging.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h"
#include "chrome/browser/ui/chrome_style.h"
#include "chrome/browser/ui/cocoa/autofill/autofill_dialog_constants.h"
#import "chrome/browser/ui/cocoa/autofill/autofill_textfield.h"
#include "skia/ext/skia_utils_mac.h"

namespace {

// Horizontal padding between text and other elements (in pixels).
const int kAroundTextPadding = 4;

// Padding at the top of suggestions.
const CGFloat kTopPadding = 10;

// Indicates infinite size in either vertical or horizontal direction.
// Technically, CGFLOAT_MAX should do. Practically, it runs into several issues.
// #1) Many computations on Retina devices overflow with that value.
// #2) In this particular use case, it results in the message
//     "CGAffineTransformInvert: singular matrix."
const CGFloat kInfiniteSize = 1.0e6;

// A line fragment padding that creates the same visual look as text layout in
// an NSTextField does. (Which UX feedback was based on)
const CGFloat kLineFragmentPadding = 2.0;

// Padding added on top of the label so its first line looks centered with
// respect to the input field. Only added when the input field is showing.
const CGFloat kLabelWithInputTopPadding = 5.0;

}

// An attachment cell for a single icon - takes care of proper alignment of
// text and icon.
@interface IconAttachmentCell : NSTextAttachmentCell {
  CGFloat baseline_;  // The cell's baseline adjustment.
}

// Adjust the cell's baseline so that the lower edge of the image aligns with
// the longest descender, not the font baseline
- (void)adjustBaselineForFont:(NSFont*)font;

@end


@interface AutofillSuggestionView : NSView {
 @private
  // The main input field - only view not ignoring mouse events.
  NSView* inputField_;
}

@property (assign, nonatomic) NSView* inputField;

@end


// The suggestion container should ignore any mouse events unless they occur
// within the bounds of an editable field.
@implementation AutofillSuggestionView

@synthesize inputField = inputField_;

- (NSView*)hitTest:(NSPoint)point {
  NSView* hitView = [super hitTest:point];
  if ([hitView isDescendantOf:inputField_])
    return hitView;

  return nil;
}

@end


@implementation IconAttachmentCell

- (NSPoint)cellBaselineOffset {
  return NSMakePoint(0.0, baseline_);
}

// Ensure proper padding between text and icon.
- (NSSize)cellSize {
  NSSize size = [super cellSize];
  size.width += kAroundTextPadding;
  return size;
}

// drawWithFrame: needs to be overridden to left-align the image. Default
// rendering centers images in the cell's frame.
- (void)drawWithFrame:(NSRect)frame inView:(NSView*)view {
  frame.size.width -= kAroundTextPadding;
  [super drawWithFrame:frame inView:view];
}

- (void)adjustBaselineForFont:(NSFont*)font {
  CGFloat lineHeight = [font ascender];
  baseline_ = std::floor((lineHeight - [[self image] size].height) / 2.0);
}

@end


@interface AutofillSuggestionContainer (Private)

// Set the main suggestion text and the corresponding |icon|.
// Attempts to wrap the text if |wrapText| is set.
- (void)setSuggestionText:(NSString*)line
                     icon:(NSImage*)icon
                 wrapText:(BOOL)wrapText;

@end


@implementation AutofillSuggestionContainer

- (AutofillTextField*)inputField {
  return inputField_.get();
}

- (NSTextField*)makeDetailSectionLabel:(NSString*)labelText {
  base::scoped_nsobject<NSTextField> label([[NSTextField alloc] init]);
  [label setFont:
      [[NSFontManager sharedFontManager] convertFont:[label font]
                                         toHaveTrait:NSBoldFontMask]];
  [label setStringValue:labelText];
  [label setEditable:NO];
  [label setBordered:NO];
  [label sizeToFit];
  return label.autorelease();
}

- (void)loadView {
  label_.reset([[NSTextView alloc] initWithFrame:NSZeroRect]);
  [[label_ textContainer] setLineFragmentPadding:kLineFragmentPadding];
  [label_ setEditable:NO];
  [label_ setSelectable:NO];
  [label_ setDrawsBackground:NO];

  base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
      [[NSMutableParagraphStyle alloc] init]);
  [paragraphStyle setLineSpacing:0.5 * [[label_ font] pointSize]];
  [label_ setDefaultParagraphStyle:paragraphStyle];

  inputField_.reset([[AutofillTextField alloc] initWithFrame:NSZeroRect]);
  [inputField_ setHidden:YES];

  spacer_.reset([[NSBox alloc] initWithFrame:NSZeroRect]);
  [spacer_ setBoxType:NSBoxSeparator];
  [spacer_ setBorderType:NSLineBorder];

  base::scoped_nsobject<AutofillSuggestionView> view(
      [[AutofillSuggestionView alloc] initWithFrame:NSZeroRect]);
  [view setSubviews:
      @[ label_, inputField_, spacer_ ]];
  [view setInputField:inputField_];
  [self setView:view];
}

- (void)setSuggestionText:(NSString*)line
                     icon:(NSImage*)icon
                 wrapText:(BOOL)wrapText {
  [label_ setString:@""];

  if ([icon size].width) {
    base::scoped_nsobject<IconAttachmentCell> cell(
        [[IconAttachmentCell alloc] initImageCell:icon]);
    base::scoped_nsobject<NSTextAttachment> attachment(
        [[NSTextAttachment alloc] init]);
    [cell adjustBaselineForFont:[NSFont controlContentFontOfSize:0]];
    [cell setAlignment:NSLeftTextAlignment];
    [attachment setAttachmentCell:cell];
    [[label_ textStorage] setAttributedString:
        [NSAttributedString attributedStringWithAttachment:attachment]];
  }

  NSDictionary* attributes = @{
    NSParagraphStyleAttributeName : [label_ defaultParagraphStyle],
            NSCursorAttributeName : [NSCursor arrowCursor],
              NSFontAttributeName : [NSFont controlContentFontOfSize:0]
  };
  base::scoped_nsobject<NSAttributedString> str1(
      [[NSAttributedString alloc] initWithString:line
                                      attributes:attributes]);
  [[label_ textStorage] appendAttributedString:str1];

  [label_ setVerticallyResizable:YES];
  [label_ setHorizontallyResizable:!wrapText];
  if (wrapText) {
    CGFloat availableWidth =
        4 * autofill::kFieldWidth - [inputField_ frame].size.width;
    [label_ setFrameSize:NSMakeSize(availableWidth, kInfiniteSize)];
  } else {
    [label_ setFrameSize:NSMakeSize(kInfiniteSize, kInfiniteSize)];
  }
  [[label_ layoutManager] ensureLayoutForTextContainer:[label_ textContainer]];
  [label_ sizeToFit];
}

- (void)
    setSuggestionWithVerticallyCompactText:(NSString*)verticallyCompactText
                   horizontallyCompactText:(NSString*)horizontallyCompactText
                                      icon:(NSImage*)icon
                                  maxWidth:(CGFloat)maxWidth {
  // Prefer the vertically compact text when it fits. If it doesn't fit, fall
  // back to the horizontally compact text.
  [self setSuggestionText:verticallyCompactText icon:icon wrapText:NO];
  if ([self preferredSize].width > maxWidth)
    [self setSuggestionText:horizontallyCompactText icon:icon wrapText:YES];
}


- (void)showInputField:(NSString*)text withIcon:(NSImage*)icon {
  [[inputField_ cell] setPlaceholderString:text];
  [[inputField_ cell] setIcon:icon];
  [inputField_ setHidden:NO];
  [inputField_ sizeToFit];

  // Enforce fixed width.
  NSSize frameSize = NSMakeSize(autofill::kFieldWidth,
                                NSHeight([inputField_ frame]));
  [inputField_ setFrameSize:frameSize];
}


- (NSSize)preferredSize {
  NSSize size = [label_ bounds].size;

  // Final inputField_ sizing/spacing depends on a TODO(estade) in Views code.
  if (![inputField_ isHidden]) {
    size.height = std::max(size.height + kLabelWithInputTopPadding,
                           NSHeight([inputField_ frame]));
    size.width += NSWidth([inputField_ frame])  + kAroundTextPadding;
  }

  size.height += kTopPadding;

  return size;
}

- (void)performLayout {
  NSRect bounds = [[self view] bounds];
  NSSize preferredContainerSize = [self preferredSize];
  // width is externally determined.
  preferredContainerSize.width = NSWidth(bounds);

  NSRect spacerFrame = NSMakeRect(0, preferredContainerSize.height - 1,
                                  preferredContainerSize.width, 1);

  NSRect labelFrame = [label_ bounds];
  labelFrame.origin.x = NSMinX(bounds);
  labelFrame.origin.y = NSMaxY(bounds) - NSHeight(labelFrame) - kTopPadding;

  // Position input field - top is aligned to top of label field.
  if (![inputField_ isHidden]) {
    NSRect inputFieldFrame = [inputField_ frame];
    inputFieldFrame.origin.x = NSMaxX(bounds) - NSWidth(inputFieldFrame);
    inputFieldFrame.origin.y = NSMaxY(labelFrame) - NSHeight(inputFieldFrame);
    [inputField_ setFrameOrigin:inputFieldFrame.origin];

    // Vertically center the first line of the label with respect to the input
    // field.
    labelFrame.origin.y -= kLabelWithInputTopPadding;

    // Due to fixed width, fields are guaranteed to not overlap.
    DCHECK_LE(NSMaxX(labelFrame), NSMinX(inputFieldFrame));
  }

  [spacer_ setFrame:spacerFrame];
  [label_ setFrame:labelFrame];
  [[self view] setFrameSize:preferredContainerSize];
}

@end