// Copyright (c) 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_section_container.h"

#include <stddef.h>

#include <algorithm>

#include "base/mac/foundation_util.h"
#include "base/mac/sdk_forward_declarations.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h"
#include "chrome/browser/ui/chrome_style.h"
#import "chrome/browser/ui/cocoa/autofill/autofill_pop_up_button.h"
#import "chrome/browser/ui/cocoa/autofill/autofill_section_view.h"
#import "chrome/browser/ui/cocoa/autofill/autofill_suggestion_container.h"
#import "chrome/browser/ui/cocoa/autofill/autofill_textfield.h"
#import "chrome/browser/ui/cocoa/autofill/autofill_tooltip_controller.h"
#import "chrome/browser/ui/cocoa/autofill/layout_view.h"
#include "chrome/browser/ui/cocoa/autofill/simple_grid_layout.h"
#import "chrome/browser/ui/cocoa/image_button_cell.h"
#import "chrome/browser/ui/cocoa/menu_button.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "grit/components_scaled_resources.h"
#include "grit/theme_resources.h"
#import "ui/base/cocoa/menu_controller.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/base/models/combobox_model.h"
#include "ui/base/resource/resource_bundle.h"

namespace {

// Constants used for layouting controls. These variables are copied from
// "ui/views/layout/layout_constants.h".

// Horizontal spacing between controls that are logically related.
const int kRelatedControlHorizontalSpacing = 8;

// Vertical spacing between controls that are logically related.
const int kRelatedControlVerticalSpacing = 8;

// TODO(estade): pull out these constants, and figure out better values
// for them. Note: These are duplicated from Views code.

// Fixed width for the details section.
const int kDetailsWidth = 440;

// Top/bottom inset for contents of a detail section.
const size_t kDetailSectionInset = 10;

// Vertical padding around the section header.
const CGFloat kVerticalHeaderPadding = 6;

// If the Autofill data comes from a credit card, make sure to overwrite the
// CC comboboxes (even if they already have something in them). If the
// Autofill data comes from an AutofillProfile, leave the comboboxes alone.
// TODO(groby): This kind of logic should _really_ live on the delegate.
bool ShouldOverwriteComboboxes(autofill::DialogSection section,
                               autofill::ServerFieldType type) {
  if (autofill::AutofillType(type).group() != autofill::CREDIT_CARD) {
    return false;
  }

  return section == autofill::SECTION_CC;
}

}  // namespace

@interface AutofillSectionContainer ()

// An input field has been edited or activated - inform the delegate and
// possibly reset the validity of the input (if it's a textfield).
- (void)fieldEditedOrActivated:(NSControl<AutofillInputField>*)field
                        edited:(BOOL)edited;

// Convenience method to retrieve a field type via the control's tag.
- (autofill::ServerFieldType)fieldTypeForControl:(NSControl*)control;

// Find the DetailInput* associated with a field type.
- (const autofill::DetailInput*)detailInputForType:
    (autofill::ServerFieldType)type;

// Takes an NSArray of controls and builds a FieldValueMap from them.
// Translates between Cocoa code and delegate, essentially.
// All controls must inherit from NSControl and conform to AutofillInputView.
- (void)fillDetailOutputs:(autofill::FieldValueMap*)outputs
             fromControls:(NSArray*)controls;

// Updates input fields based on delegate status. If |shouldClobber| is YES,
// will clobber existing data and reset fields to the initial values.
- (void)updateAndClobber:(BOOL)shouldClobber;

// Return YES if this is a section that contains CC info. (And, more
// importantly, a potential CVV field)
- (BOOL)isCreditCardSection;

// Create properly styled label for section. Autoreleased.
- (NSTextField*)makeDetailSectionLabel:(NSString*)labelText;

// Create a button offering input suggestions.
- (MenuButton*)makeSuggestionButton;

// Create a view with all inputs requested by |delegate_| and resets |input_|.
- (void)makeInputControls;

// Refresh all field icons based on |delegate_| status.
- (void)updateFieldIcons;

@end

@implementation AutofillSectionContainer

@synthesize section = section_;
@synthesize validationDelegate = validationDelegate_;

- (id)initWithDelegate:(autofill::AutofillDialogViewDelegate*)delegate
            forSection:(autofill::DialogSection)section {
  if (self = [super init]) {
    section_ = section;
    delegate_ = delegate;
  }
  return self;
}

- (void)getInputs:(autofill::FieldValueMap*)output {
  [self fillDetailOutputs:output fromControls:[inputs_ subviews]];
}

// Note: This corresponds to Views' "UpdateDetailsGroupState".
- (void)modelChanged {
  ui::MenuModel* suggestionModel = delegate_->MenuModelForSection(section_);
  menuController_.reset([[MenuController alloc] initWithModel:suggestionModel
                                       useWithPopUpButtonCell:YES]);
  NSMenu* menu = [menuController_ menu];

  const BOOL hasSuggestions = [menu numberOfItems] > 0;
  [suggestButton_ setHidden:!hasSuggestions];

  [suggestButton_ setAttachedMenu:menu];

  [self updateSuggestionState];

  if (![[self view] isHidden])
    [self validateFor:autofill::VALIDATE_EDIT];

  // Always request re-layout on state change.
  [self requestRelayout];
}

- (void)requestRelayout {
  id delegate = [[view_ window] windowController];
  if ([delegate respondsToSelector:@selector(requestRelayout)])
    [delegate performSelector:@selector(requestRelayout)];
}

- (void)loadView {
  [self makeInputControls];

  base::string16 labelText = delegate_->LabelForSection(section_);
  label_.reset(
      [[self makeDetailSectionLabel:base::SysUTF16ToNSString(labelText)]
          retain]);

  suggestButton_.reset([[self makeSuggestionButton] retain]);
  suggestContainer_.reset([[AutofillSuggestionContainer alloc] init]);

  view_.reset([[AutofillSectionView alloc] initWithFrame:NSZeroRect]);
  [self setView:view_];
  [view_ setSubviews:
      @[label_, inputs_, [suggestContainer_ view], suggestButton_]];
  if (tooltipController_) {
    [view_ addSubview:[tooltipController_ view]
           positioned:NSWindowAbove
           relativeTo:inputs_];
  }

  if ([self isCreditCardSection]) {
    // Credit card sections *MUST* have a CREDIT_CARD_VERIFICATION_CODE input.
    DCHECK([self detailInputForType:autofill::CREDIT_CARD_VERIFICATION_CODE]);
    [[suggestContainer_ inputField] setTag:
        autofill::CREDIT_CARD_VERIFICATION_CODE];
    [[suggestContainer_ inputField] setInputDelegate:self];
  }

  [self modelChanged];
}

- (NSSize)preferredSize {
  if ([view_ isHidden])
    return NSZeroSize;

  NSSize labelSize = [label_ frame].size;  // Assumes sizeToFit was called.
  CGFloat controlHeight = [inputs_ preferredHeightForWidth:kDetailsWidth];
  if (showSuggestions_)
    controlHeight = [suggestContainer_ preferredSize].height;

  return NSMakeSize(kDetailsWidth + 2 * chrome_style::kHorizontalPadding,
                    labelSize.height + kVerticalHeaderPadding +
                        controlHeight + 2 * kDetailSectionInset);
}

- (void)performLayout {
  if ([view_ isHidden])
    return;

  NSSize buttonSize = [suggestButton_ frame].size;  // Assume sizeToFit.
  NSSize labelSize = [label_ frame].size;  // Assumes sizeToFit was called.
  CGFloat controlHeight = [inputs_ preferredHeightForWidth:kDetailsWidth];
  if (showSuggestions_)
    controlHeight = [suggestContainer_ preferredSize].height;

  NSRect viewFrame = NSZeroRect;
  viewFrame.size = [self preferredSize];

  NSRect contentFrame = NSInsetRect(viewFrame,
                                    chrome_style::kHorizontalPadding,
                                    kDetailSectionInset);
  NSRect controlFrame, labelFrame, buttonFrame;

  // Label is top left, suggestion button is top right, controls are below that.
  NSDivideRect(contentFrame, &labelFrame, &controlFrame,
               kVerticalHeaderPadding + labelSize.height, NSMaxYEdge);
  NSDivideRect(labelFrame, &buttonFrame, &labelFrame,
               buttonSize.width, NSMaxXEdge);

  labelFrame = NSOffsetRect(labelFrame, 0, kVerticalHeaderPadding);
  labelFrame.size = labelSize;

  buttonFrame = NSOffsetRect(buttonFrame, 0, 5);
  buttonFrame.size = buttonSize;

  if (showSuggestions_) {
    [[suggestContainer_ view] setFrame:controlFrame];
    [suggestContainer_ performLayout];
  } else {
    [inputs_ setFrame:controlFrame];
  }
  [label_ setFrame:labelFrame];
  [suggestButton_ setFrame:buttonFrame];
  [inputs_ setHidden:showSuggestions_];
  [[suggestContainer_ view] setHidden:!showSuggestions_];
  [view_ setFrameSize:viewFrame.size];
  if (tooltipController_) {
    [[tooltipController_ view] setHidden:showSuggestions_];
    NSRect tooltipIconFrame = [tooltipField_ decorationFrame];
    tooltipIconFrame.origin =
        [[self view] convertPoint:tooltipIconFrame.origin
                         fromView:[tooltipField_ superview]];
    [[tooltipController_ view] setFrame:tooltipIconFrame];
  }
}

- (KeyEventHandled)keyEvent:(NSEvent*)event forInput:(id)sender {
  content::NativeWebKeyboardEvent webEvent(event);

  // Only handle keyDown, to handle key repeats without duplicates.
  if (webEvent.type != content::NativeWebKeyboardEvent::RawKeyDown)
    return kKeyEventNotHandled;

  // Allow the delegate to intercept key messages.
  if (delegate_->HandleKeyPressEventInInput(webEvent))
    return kKeyEventHandled;
  return kKeyEventNotHandled;
}

- (void)onMouseDown:(NSControl<AutofillInputField>*)field {
  [self fieldEditedOrActivated:field edited:NO];
  [validationDelegate_ updateMessageForField:field];
}

- (void)fieldBecameFirstResponder:(NSControl<AutofillInputField>*)field {
  [validationDelegate_ updateMessageForField:field];
}

- (void)didChange:(id)sender {
  [self fieldEditedOrActivated:sender edited:YES];
}

- (void)didEndEditing:(id)sender {
  delegate_->FocusMoved();
  [validationDelegate_ hideErrorBubble];
  [self validateFor:autofill::VALIDATE_EDIT];
}

- (void)updateSuggestionState {
  const autofill::SuggestionState& suggestionState =
      delegate_->SuggestionStateForSection(section_);
  showSuggestions_ = suggestionState.visible;

  if (!suggestionState.extra_text.empty()) {
    NSString* extraText =
        base::SysUTF16ToNSString(suggestionState.extra_text);
    NSImage* extraIcon = suggestionState.extra_icon.AsNSImage();
    [suggestContainer_ showInputField:extraText withIcon:extraIcon];
  }

  // NOTE: It's important to set the input field, if there is one, _before_
  // setting the suggestion text, since the suggestion container needs to
  // account for the input field's width when deciding which of the two string
  // representations to use.
  NSString* verticallyCompactText =
      base::SysUTF16ToNSString(suggestionState.vertically_compact_text);
  NSString* horizontallyCompactText =
      base::SysUTF16ToNSString(suggestionState.horizontally_compact_text);
  [suggestContainer_
      setSuggestionWithVerticallyCompactText:verticallyCompactText
                     horizontallyCompactText:horizontallyCompactText
                                        icon:suggestionState.icon.AsNSImage()
                                    maxWidth:kDetailsWidth];

  [view_ setShouldHighlightOnHover:showSuggestions_];
  if (showSuggestions_)
    [view_ setClickTarget:suggestButton_];
  else
    [view_ setClickTarget:nil];
  [view_ setHidden:!delegate_->SectionIsActive(section_)];
}

- (void)update {
  [self updateAndClobber:YES];
  [view_ updateHoverState];
}

- (void)fillForType:(const autofill::ServerFieldType)type {
  // Make sure to overwrite the originating input if it is a text field.
  AutofillTextField* field =
      base::mac::ObjCCast<AutofillTextField>([inputs_ viewWithTag:type]);
  [field setFieldValue:@""];

  if (ShouldOverwriteComboboxes(section_, type)) {
    for (NSControl* control in [inputs_ subviews]) {
      AutofillPopUpButton* popup =
          base::mac::ObjCCast<AutofillPopUpButton>(control);
      if (popup) {
        autofill::ServerFieldType fieldType =
            [self fieldTypeForControl:popup];
        if (autofill::AutofillType(fieldType).group() ==
                autofill::CREDIT_CARD) {
          ui::ComboboxModel* model =
              delegate_->ComboboxModelForAutofillType(fieldType);
          DCHECK(model);
          [popup selectItemAtIndex:model->GetDefaultIndex()];
        }
      }
    }
  }

  [self updateAndClobber:NO];
}

- (BOOL)validateFor:(autofill::ValidationType)validationType {
  NSArray* fields = nil;
  if (!showSuggestions_) {
    fields = [inputs_ subviews];
  } else if ([self isCreditCardSection]) {
    if (![[suggestContainer_ inputField] isHidden])
      fields = @[ [suggestContainer_ inputField] ];
  }

  // Ensure only editable fields are validated.
  fields = [fields filteredArrayUsingPredicate:
      [NSPredicate predicateWithBlock:
          ^BOOL(NSControl<AutofillInputField>* field, NSDictionary* bindings) {
              return [field isEnabled];
          }]];

  autofill::FieldValueMap detailOutputs;
  [self fillDetailOutputs:&detailOutputs fromControls:fields];
  autofill::ValidityMessages messages = delegate_->InputsAreValid(
      section_, detailOutputs);

  for (NSControl<AutofillInputField>* input in fields) {
    const autofill::ValidityMessage& message =
        messages.GetMessageOrDefault([self fieldTypeForControl:input]);
    if (validationType != autofill::VALIDATE_FINAL && !message.sure)
      continue;
    [input setValidityMessage:base::SysUTF16ToNSString(message.text)];
    [validationDelegate_ updateMessageForField:input];
  }

  return !messages.HasErrors();
}

- (NSString*)suggestionText {
  return showSuggestions_ ? [[suggestContainer_ inputField] stringValue] : nil;
}

- (void)addInputsToArray:(NSMutableArray*)array {
  [array addObjectsFromArray:[inputs_ subviews]];

  // Only credit card sections can have a suggestion input.
  if ([self isCreditCardSection])
    [array addObject:[suggestContainer_ inputField]];
}

#pragma mark Internal API for AutofillSectionContainer.

- (void)fieldEditedOrActivated:(NSControl<AutofillInputField>*)field
                        edited:(BOOL)edited {
  autofill::ServerFieldType type = [self fieldTypeForControl:field];
  base::string16 fieldValue = base::SysNSStringToUTF16([field fieldValue]);

  // Get the frame rectangle for the designated field, in screen coordinates.
  NSRect textFrameInScreen = [field convertRect:[field bounds] toView:nil];
  textFrameInScreen = [[field window] convertRectToScreen:textFrameInScreen];

  // And adjust for gfx::Rect being flipped compared to OSX coordinates.
  NSScreen* screen = [[NSScreen screens] firstObject];
  textFrameInScreen.origin.y =
      NSMaxY([screen frame]) - NSMaxY(textFrameInScreen);
  gfx::Rect textFrameRect(NSRectToCGRect(textFrameInScreen));

  delegate_->UserEditedOrActivatedInput(section_,
                                        type,
                                        [self view],
                                        textFrameRect,
                                        fieldValue,
                                        edited);

  AutofillTextField* textfield = base::mac::ObjCCast<AutofillTextField>(field);
  if (!textfield)
    return;

  // If the field is marked as invalid, check if the text is now valid. Many
  // fields (i.e. CC#) are invalid for most of the duration of editing, so
  // flagging them as invalid prematurely is not helpful. However, correcting a
  // minor mistake (i.e. a wrong CC digit) should immediately result in
  // validation - positive user feedback.
  if ([textfield invalid] && edited) {
    base::string16 message = delegate_->InputValidityMessage(section_,
                                                             type,
                                                             fieldValue);
    [textfield setValidityMessage:base::SysUTF16ToNSString(message)];

    // If the field transitioned from invalid to valid, re-validate the group,
    // since inter-field checks become meaningful with valid fields.
    if (![textfield invalid])
      [self validateFor:autofill::VALIDATE_EDIT];

    // The validity message has potentially changed - notify the error bubble.
    [validationDelegate_ updateMessageForField:textfield];
  }

  // Update the icon if necessary.
  if (delegate_->FieldControlsIcons(type))
    [self updateFieldIcons];
}

- (autofill::ServerFieldType)fieldTypeForControl:(NSControl*)control {
  DCHECK([control tag]);
  return static_cast<autofill::ServerFieldType>([control tag]);
}

- (const autofill::DetailInput*)detailInputForType:
    (autofill::ServerFieldType)type {
  for (size_t i = 0; i < detailInputs_.size(); ++i) {
    if (detailInputs_[i]->type == type)
      return detailInputs_[i];
  }
  // TODO(groby): Needs to be NOTREACHED. Can't, due to the fact that tests
  // blindly call setFieldValue:forType:, even for non-existing inputs.
  return NULL;
}

- (void)fillDetailOutputs:(autofill::FieldValueMap*)outputs
             fromControls:(NSArray*)controls {
  for (NSControl<AutofillInputField>* input in controls) {
    DCHECK([input isKindOfClass:[NSControl class]]);
    DCHECK([input conformsToProtocol:@protocol(AutofillInputField)]);
    outputs->insert(std::make_pair(
        [self fieldTypeForControl:input],
        base::SysNSStringToUTF16([input fieldValue])));
  }
}

- (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 setDrawsBackground:NO];
  [label sizeToFit];
  return label.autorelease();
}

- (void)updateAndClobber:(BOOL)shouldClobber {
  if (shouldClobber) {
    // Remember which one of the inputs was first responder so focus can be
    // restored after the inputs are rebuilt.
    NSView* firstResponderView =
        base::mac::ObjCCast<NSView>([[inputs_ window] firstResponder]);
    autofill::ServerFieldType type = autofill::UNKNOWN_TYPE;
    for (NSControl* field in [inputs_ subviews]) {
      if ([firstResponderView isDescendantOf:field]) {
        type = [self fieldTypeForControl:field];
        break;
      }
    }

    [self makeInputControls];

    if (type != autofill::UNKNOWN_TYPE) {
      NSView* view = [inputs_ viewWithTag:type];
      if (view)
        [[inputs_ window] makeFirstResponder:view];
    }
  } else {
    const autofill::DetailInputs& updatedInputs =
        delegate_->RequestedFieldsForSection(section_);

    for (autofill::DetailInputs::const_iterator iter = updatedInputs.begin();
         iter != updatedInputs.end();
         ++iter) {
      NSControl<AutofillInputField>* field = [inputs_ viewWithTag:iter->type];
      DCHECK(field);
      if ([field isDefault])
        [field setFieldValue:base::SysUTF16ToNSString(iter->initial_value)];
    }
    [self updateFieldIcons];
  }

  [self modelChanged];
}

- (BOOL)isCreditCardSection {
  return section_ == autofill::SECTION_CC;
}

- (MenuButton*)makeSuggestionButton {
  base::scoped_nsobject<MenuButton> button([[MenuButton alloc] init]);

  [button setOpenMenuOnClick:YES];
  [button setBordered:NO];
  [button setShowsBorderOnlyWhileMouseInside:YES];

  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
  NSImage* image =
      rb.GetNativeImageNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON).ToNSImage();
  [[button cell] setImage:image
           forButtonState:image_button_cell::kDefaultState];
  image = rb.GetNativeImageNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON_H).
      ToNSImage();
  [[button cell] setImage:image
           forButtonState:image_button_cell::kHoverState];
  image = rb.GetNativeImageNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON_P).
      ToNSImage();
  [[button cell] setImage:image
           forButtonState:image_button_cell::kPressedState];
  image = rb.GetNativeImageNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON_D).
      ToNSImage();
  [[button cell] setImage:image
           forButtonState:image_button_cell::kDisabledState];

  // ImageButtonCell's cellSize is not working. (http://crbug.com/298501)
  [button setFrameSize:[image size]];
  return button.autorelease();
}

// TODO(estade): we should be using Chrome-style constrained window padding
// values.
- (void)makeInputControls {
  if (inputs_) {
    // When |inputs_| is replaced in response to a country change, there's a
    // didEndEditing dispatched that segfaults or DCHECKS() as it's operating on
    // stale input fields. Nil out the input delegate so this doesn't happen.
    for (NSControl<AutofillInputField>* input in [inputs_ subviews]) {
      [input setInputDelegate:nil];
    }
  }

  detailInputs_.clear();

  // Keep a list of weak pointers to DetailInputs.
  const autofill::DetailInputs& inputs =
      delegate_->RequestedFieldsForSection(section_);

  // Reverse the order of all the inputs.
  for (int i = inputs.size() - 1; i >= 0; --i) {
    detailInputs_.push_back(&(inputs[i]));
  }

  // Then right the reversal in each row.
  std::vector<const autofill::DetailInput*>::iterator it;
  for (it = detailInputs_.begin(); it < detailInputs_.end(); ++it) {
    std::vector<const autofill::DetailInput*>::iterator start = it;
    while (it != detailInputs_.end() &&
           (*it)->length != autofill::DetailInput::LONG) {
      ++it;
    }
    std::reverse(start, it);
  }

  base::scoped_nsobject<LayoutView> view([[LayoutView alloc] init]);
  [view setLayoutManager:
      scoped_ptr<SimpleGridLayout>(new SimpleGridLayout(view))];
  SimpleGridLayout* layout = [view layoutManager];

  int column_set_id = 0;
  for (size_t i = 0; i < detailInputs_.size(); ++i) {
    const autofill::DetailInput& input = *detailInputs_[i];

    if (input.length == autofill::DetailInput::LONG)
      ++column_set_id;

    int kColumnSetId =
        input.length == autofill::DetailInput::NONE ? -1 : column_set_id;

    ColumnSet* columnSet = layout->GetColumnSet(kColumnSetId);
    if (!columnSet) {
      // Create a new column set and row.
      columnSet = layout->AddColumnSet(kColumnSetId);
      if (i != 0 && kColumnSetId != -1)
        layout->AddPaddingRow(kRelatedControlVerticalSpacing);
      layout->StartRow(0, kColumnSetId);
    } else {
      // Add a new column to existing row.
      columnSet->AddPaddingColumn(kRelatedControlHorizontalSpacing);
      // Must explicitly skip the padding column since we've already started
      // adding views.
      layout->SkipColumns(1);
    }

    columnSet->AddColumn(input.expand_weight ? input.expand_weight : 1.0f);

    ui::ComboboxModel* inputModel =
        delegate_->ComboboxModelForAutofillType(input.type);
    base::scoped_nsprotocol<NSControl<AutofillInputField>*> control;
    if (inputModel) {
      base::scoped_nsobject<AutofillPopUpButton> popup(
          [[AutofillPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO]);
      for (int i = 0; i < inputModel->GetItemCount(); ++i) {
        if (!inputModel->IsItemSeparatorAt(i)) {
          // Currently, the first item in |inputModel| is duplicated later in
          // the list. The second item is a separator. Because NSPopUpButton
          // de-duplicates, the menu's just left with a separator on the top of
          // the list (with nothing it's separating). For that reason,
          // separators are ignored on Mac for now. http://crbug.com/347653
          [popup addItemWithTitle:
              base::SysUTF16ToNSString(inputModel->GetItemAt(i))];
        }
      }
      [popup setDefaultValue:base::SysUTF16ToNSString(
          inputModel->GetItemAt(inputModel->GetDefaultIndex()))];
      control.reset(popup.release());
    } else {
      base::scoped_nsobject<AutofillTextField> field(
          [[AutofillTextField alloc] init]);
      [field setIsMultiline:input.IsMultiline()];
      [[field cell] setLineBreakMode:NSLineBreakByClipping];
      [[field cell] setScrollable:YES];
      [[field cell] setPlaceholderString:
          l10n_util::FixUpWindowsStyleLabel(input.placeholder_text)];
      NSString* tooltipText =
          base::SysUTF16ToNSString(delegate_->TooltipForField(input.type));
      // VoiceOver onlys seems to pick up the help message on [field cell]
      // (rather than just field).
      BOOL success = [[field cell]
          accessibilitySetOverrideValue:tooltipText
                           forAttribute:NSAccessibilityHelpAttribute];
      DCHECK(success);
      if ([tooltipText length] > 0) {
        if (!tooltipController_) {
          tooltipController_.reset(
              [[AutofillTooltipController alloc]
                   initWithArrowLocation:info_bubble::kTopRight]);
        }
        tooltipField_ = field.get();
        NSImage* icon =
            ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
                IDR_AUTOFILL_TOOLTIP_ICON).ToNSImage();
        [tooltipController_ setImage:icon];
        [tooltipController_ setMessage:tooltipText];
        [[field cell] setDecorationSize:[icon size]];
      }
      [field setDefaultValue:@""];
      control.reset(field.release());
    }
    [control setTag:input.type];
    [control setFieldValue:base::SysUTF16ToNSString(input.initial_value)];
    [control sizeToFit];
    [control setFrame:NSIntegralRect([control frame])];
    [control setInputDelegate:self];
    // Hide away fields that cannot be edited.
    if (kColumnSetId == -1) {
      [control setFrame:NSZeroRect];
      [control setHidden:YES];
    }
    layout->AddView(control);

    if (input.length == autofill::DetailInput::LONG ||
        input.length == autofill::DetailInput::SHORT_EOL) {
      ++column_set_id;
    }
  }

  if (inputs_) {
    [[self view] replaceSubview:inputs_ with:view];
    [self requestRelayout];
  }

  inputs_ = view;
  [self updateFieldIcons];
}

- (void)updateFieldIcons {
  autofill::FieldValueMap fieldValues;
  for (NSControl<AutofillInputField>* input in [inputs_ subviews]) {
    DCHECK([input isKindOfClass:[NSControl class]]);
    DCHECK([input conformsToProtocol:@protocol(AutofillInputField)]);
    autofill::ServerFieldType fieldType = [self fieldTypeForControl:input];
    NSString* value = [input fieldValue];
    fieldValues[fieldType] = base::SysNSStringToUTF16(value);
  }

  autofill::FieldIconMap fieldIcons = delegate_->IconsForFields(fieldValues);
  for (autofill::FieldIconMap::const_iterator iter = fieldIcons.begin();
       iter!= fieldIcons.end(); ++iter) {
    AutofillTextField* textfield = base::mac::ObjCCastStrict<AutofillTextField>(
        [inputs_ viewWithTag:iter->first]);
    [[textfield cell] setIcon:iter->second.ToNSImage()];
  }
}

@end


@implementation AutofillSectionContainer (ForTesting)

- (NSControl*)getField:(autofill::ServerFieldType)type {
  return [inputs_ viewWithTag:type];
}

- (void)setFieldValue:(NSString*)text
              forType:(autofill::ServerFieldType)type {
  NSControl<AutofillInputField>* field = [inputs_ viewWithTag:type];
  if (field)
    [field setFieldValue:text];
}

- (void)setSuggestionFieldValue:(NSString*)text {
  [[suggestContainer_ inputField] setFieldValue:text];
}

- (void)activateFieldForType:(autofill::ServerFieldType)type {
  NSControl<AutofillInputField>* field = [inputs_ viewWithTag:type];
  if (field) {
    [[field window] makeFirstResponder:field];
    [self fieldEditedOrActivated:field edited:NO];
  }
}

@end