// 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.

#include "chrome/browser/ui/cocoa/extensions/media_galleries_dialog_cocoa.h"

#include "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/browser/ui/chrome_style.h"
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_alert.h"
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h"
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_control_utils.h"
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
#import "chrome/browser/ui/cocoa/key_equivalent_constants.h"
#include "content/public/browser/web_contents.h"
#include "grit/generated_resources.h"
#import "ui/base/cocoa/flipped_view.h"
#import "ui/base/cocoa/menu_controller.h"
#import "ui/base/models/menu_model.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"

// Controller for UI events on items in the media galleries dialog.
@interface MediaGalleriesCocoaController : NSObject {
 @private
  MediaGalleriesDialogCocoa* dialog_;
}

@property(nonatomic, assign) MediaGalleriesDialogCocoa* dialog;

@end

@implementation MediaGalleriesCocoaController

@synthesize dialog = dialog_;

- (void)onAcceptButton:(id)sender {
  dialog_->OnAcceptClicked();
}

- (void)onCancelButton:(id)sender {
  dialog_->OnCancelClicked();
}

- (void)onAddFolderClicked:(id)sender {
  DCHECK(dialog_);
  dialog_->OnAddFolderClicked();
}

- (void)onCheckboxToggled:(id)sender {
  DCHECK(dialog_);
  dialog_->OnCheckboxToggled(sender);
}

@end

@interface MediaGalleriesCheckbox : NSButton {
 @private
  MediaGalleriesDialogCocoa* dialog_;
  MediaGalleryPrefId prefId_;
  base::scoped_nsobject<MenuController> menu_controller_;
}

- (id)initWithFrame:(NSRect)frameRect
             dialog:(MediaGalleriesDialogCocoa*)dialog
             prefId:(MediaGalleryPrefId)prefId;
- (NSMenu*)menuForEvent:(NSEvent*)theEvent;

@end

@implementation MediaGalleriesCheckbox

- (id)initWithFrame:(NSRect)frameRect
             dialog:(MediaGalleriesDialogCocoa*)dialog
             prefId:(MediaGalleryPrefId)prefId {
  if ((self = [super initWithFrame:frameRect])) {
    dialog_ = dialog;
    prefId_ = prefId;
  }
  return self;
}

- (NSMenu*)menuForEvent:(NSEvent*)theEvent {
  menu_controller_.reset(
    [[MenuController alloc] initWithModel:dialog_->GetContextMenuModel(prefId_)
                   useWithPopUpButtonCell:NO]);
  return [menu_controller_ menu];
}

@end

namespace {

const CGFloat kCheckboxMargin = 10;
const CGFloat kCheckboxMaxWidth = 440;
const CGFloat kScrollAreaHeight = 220;

NSString* GetUniqueIDForGallery(const MediaGalleryPrefInfo& gallery) {
  return base::SysUTF8ToNSString(gallery.device_id + gallery.path.value());
}

}  // namespace

MediaGalleriesDialogCocoa::MediaGalleriesDialogCocoa(
    MediaGalleriesDialogController* controller,
    MediaGalleriesCocoaController* cocoa_controller)
    : controller_(controller),
      accepted_(false),
      cocoa_controller_([cocoa_controller retain]) {
  [cocoa_controller_ setDialog:this];

  alert_.reset([[ConstrainedWindowAlert alloc] init]);

  [alert_ setMessageText:base::SysUTF16ToNSString(controller_->GetHeader())];
  [alert_ setInformativeText:SysUTF16ToNSString(controller_->GetSubtext())];
  [alert_ addButtonWithTitle:
    l10n_util::GetNSString(IDS_MEDIA_GALLERIES_DIALOG_CONFIRM)
               keyEquivalent:kKeyEquivalentReturn
                      target:cocoa_controller_
                      action:@selector(onAcceptButton:)];
  [alert_ addButtonWithTitle:
      l10n_util::GetNSString(IDS_MEDIA_GALLERIES_DIALOG_CANCEL)
               keyEquivalent:kKeyEquivalentEscape
                      target:cocoa_controller_
                      action:@selector(onCancelButton:)];
  [alert_ addButtonWithTitle:
      l10n_util::GetNSString(IDS_MEDIA_GALLERIES_DIALOG_ADD_GALLERY)
               keyEquivalent:kKeyEquivalentNone
                      target:cocoa_controller_
                      action:@selector(onAddFolderClicked:)];
  [[alert_ closeButton] setTarget:cocoa_controller_];
  [[alert_ closeButton] setAction:@selector(onCancelButton:)];

  InitDialogControls();

  // May be NULL during tests.
  if (controller->web_contents()) {
    base::scoped_nsobject<CustomConstrainedWindowSheet> sheet(
        [[CustomConstrainedWindowSheet alloc]
            initWithCustomWindow:[alert_ window]]);
    window_.reset(new ConstrainedWindowMac(
        this, controller->web_contents(), sheet));
  }
}

MediaGalleriesDialogCocoa::~MediaGalleriesDialogCocoa() {
}

void MediaGalleriesDialogCocoa::InitDialogControls() {
  accessory_.reset([[NSBox alloc] init]);
  [accessory_ setBoxType:NSBoxCustom];
  [accessory_ setBorderType:NSLineBorder];
  [accessory_ setBorderWidth:1];
  [accessory_ setCornerRadius:0];
  [accessory_ setTitlePosition:NSNoTitle];
  [accessory_ setBorderColor:[NSColor colorWithCalibratedRed:0.625
                                                       green:0.625
                                                        blue:0.625
                                                       alpha:1.0]];

  base::scoped_nsobject<NSScrollView> scroll_view([[NSScrollView alloc]
      initWithFrame:NSMakeRect(0, 0, kCheckboxMaxWidth, kScrollAreaHeight)]);
  [scroll_view setHasVerticalScroller:YES];
  [scroll_view setHasHorizontalScroller:NO];
  [scroll_view setBorderType:NSNoBorder];
  [scroll_view setAutohidesScrollers:YES];
  [[accessory_ contentView] addSubview:scroll_view];

  // Add gallery permission checkboxes inside the scrolling view.
  checkbox_container_.reset([[FlippedView alloc] initWithFrame:NSZeroRect]);
  checkboxes_.reset([[NSMutableArray alloc] init]);
  [scroll_view setDocumentView:checkbox_container_];

  CGFloat y_pos = kCheckboxMargin;

  y_pos = CreateAttachedCheckboxes(y_pos, controller_->AttachedPermissions());

  if (!controller_->UnattachedPermissions().empty()) {
    y_pos = CreateCheckboxSeparator(y_pos);

    y_pos = CreateUnattachedCheckboxes(
        y_pos, controller_->UnattachedPermissions());
  }

  [checkbox_container_ setFrame:NSMakeRect(0, 0, kCheckboxMaxWidth, y_pos + 2)];

  // Resize to pack the scroll view if possible.
  NSRect scroll_frame = [scroll_view frame];
  if (NSHeight(scroll_frame) > NSHeight([checkbox_container_ frame])) {
    scroll_frame.size.height = NSHeight([checkbox_container_ frame]);
    [scroll_view setFrame:scroll_frame];
  }

  [accessory_ setFrame:NSMakeRect(
      0, 0, kCheckboxMaxWidth, NSHeight(scroll_frame))];
  [alert_ setAccessoryView:accessory_];

  // As a safeguard against the user skipping reading over the dialog and just
  // confirming, the button will be unavailable for dialogs without any checks
  // until the user toggles something.
  [[[alert_ buttons] objectAtIndex:0] setEnabled:
      controller_->HasPermittedGalleries()];

  [alert_ layout];
}

CGFloat MediaGalleriesDialogCocoa::CreateAttachedCheckboxes(
    CGFloat y_pos,
    const MediaGalleriesDialogController::GalleryPermissionsVector&
        permissions) {
  y_pos += kCheckboxMargin;

  for (MediaGalleriesDialogController::GalleryPermissionsVector::
       const_iterator iter = permissions.begin();
       iter != permissions.end(); iter++) {
    const MediaGalleriesDialogController::GalleryPermission& permission = *iter;
    UpdateGalleryCheckbox(permission.pref_info, permission.allowed, y_pos);
    y_pos = NSMaxY([[checkboxes_ lastObject] frame]) + kCheckboxMargin;
  }

  return y_pos;
}

// Add checkboxes for galleries that aren't available (i.e. removable
// volumes that are not currently attached).
CGFloat MediaGalleriesDialogCocoa::CreateUnattachedCheckboxes(
    CGFloat y_pos,
    const MediaGalleriesDialogController::GalleryPermissionsVector&
        permissions) {
  y_pos += kCheckboxMargin;

  for (MediaGalleriesDialogController::GalleryPermissionsVector::
       const_iterator iter = permissions.begin();
       iter != permissions.end(); iter++) {
    const MediaGalleriesDialogController::GalleryPermission& permission = *iter;
    UpdateGalleryCheckbox(permission.pref_info, permission.allowed, y_pos);
    y_pos = NSMaxY([[checkboxes_ lastObject] frame]) + kCheckboxMargin;
  }

  return y_pos;
}

CGFloat MediaGalleriesDialogCocoa::CreateCheckboxSeparator(CGFloat y_pos) {
  base::scoped_nsobject<NSBox> separator(
      [[NSBox alloc] initWithFrame:NSMakeRect(
          0, y_pos + kCheckboxMargin * 0.5, kCheckboxMaxWidth, 1.0)]);
  [separator setBoxType:NSBoxSeparator];
  [separator setBorderType:NSLineBorder];
  [separator setAlphaValue:0.2];
  [checkbox_container_ addSubview:separator];
  y_pos += kCheckboxMargin * 0.5 + 4;

  base::scoped_nsobject<NSTextField> unattached_label(
      [[NSTextField alloc] initWithFrame:NSZeroRect]);
  [unattached_label setEditable:NO];
  [unattached_label setSelectable:NO];
  [unattached_label setBezeled:NO];
  [unattached_label setAttributedStringValue:
      constrained_window::GetAttributedLabelString(
          base::SysUTF16ToNSString(
              controller_->GetUnattachedLocationsHeader()),
          chrome_style::kTextFontStyle,
          NSNaturalTextAlignment,
          NSLineBreakByClipping
      )];
  [unattached_label sizeToFit];
  NSSize unattached_label_size = [unattached_label frame].size;
  [unattached_label setFrame:NSMakeRect(
      kCheckboxMargin, y_pos + kCheckboxMargin,
      kCheckboxMaxWidth, unattached_label_size.height)];
  [checkbox_container_ addSubview:unattached_label];
  y_pos = NSMaxY([unattached_label frame]) + kCheckboxMargin;

  return y_pos;
}

void MediaGalleriesDialogCocoa::OnAcceptClicked() {
  accepted_ = true;

  if (window_)
    window_->CloseWebContentsModalDialog();
}

void MediaGalleriesDialogCocoa::OnCancelClicked() {
  if (window_)
    window_->CloseWebContentsModalDialog();
}

void MediaGalleriesDialogCocoa::OnAddFolderClicked() {
  controller_->OnAddFolderClicked();
}

void MediaGalleriesDialogCocoa::OnCheckboxToggled(NSButton* checkbox) {
  [[[alert_ buttons] objectAtIndex:0] setEnabled:YES];

  const MediaGalleriesDialogController::GalleryPermissionsVector&
      attached_permissions = controller_->AttachedPermissions();
  for (MediaGalleriesDialogController::GalleryPermissionsVector::
       const_reverse_iterator iter = attached_permissions.rbegin();
       iter != attached_permissions.rend(); iter++) {
    const MediaGalleryPrefInfo* gallery = &iter->pref_info;
    NSString* unique_id = GetUniqueIDForGallery(*gallery);
    if ([[[checkbox cell] representedObject] isEqual:unique_id]) {
      controller_->DidToggleGalleryId(gallery->pref_id,
                                      [checkbox state] == NSOnState);
      break;
    }
  }

  const MediaGalleriesDialogController::GalleryPermissionsVector&
      unattached_permissions = controller_->UnattachedPermissions();
  for (MediaGalleriesDialogController::GalleryPermissionsVector::
       const_reverse_iterator iter = unattached_permissions.rbegin();
       iter != unattached_permissions.rend(); iter++) {
    const MediaGalleryPrefInfo* gallery = &iter->pref_info;
    NSString* unique_id = GetUniqueIDForGallery(*gallery);
    if ([[[checkbox cell] representedObject] isEqual:unique_id]) {
      controller_->DidToggleGalleryId(gallery->pref_id,
                                      [checkbox state] == NSOnState);
      break;
    }
  }

}

void MediaGalleriesDialogCocoa::UpdateGalleryCheckbox(
    const MediaGalleryPrefInfo& gallery,
    bool permitted,
    CGFloat y_pos) {
  base::scoped_nsobject<MediaGalleriesCheckbox> checkbox(
      [[MediaGalleriesCheckbox alloc] initWithFrame:NSZeroRect
                                             dialog:this
                                             prefId:gallery.pref_id]);
  NSString* unique_id = GetUniqueIDForGallery(gallery);
  [[checkbox cell] setRepresentedObject:unique_id];
  [[checkbox cell] setLineBreakMode:NSLineBreakByTruncatingMiddle];
  [checkbox setButtonType:NSSwitchButton];
  [checkbox setTarget:cocoa_controller_];
  [checkbox setAction:@selector(onCheckboxToggled:)];
  [checkboxes_ addObject:checkbox];

  // TODO(gbillock): Would be nice to add middle text elide behavior here.
  [checkbox setTitle:base::SysUTF16ToNSString(
      gallery.GetGalleryDisplayName())];
  [checkbox setToolTip:base::SysUTF16ToNSString(gallery.GetGalleryTooltip())];
  [checkbox setState:permitted ? NSOnState : NSOffState];

  [checkbox sizeToFit];
  NSRect rect = [checkbox bounds];
  rect.origin.y = y_pos;
  rect.origin.x = kCheckboxMargin;
  rect.size.width = std::min(NSWidth(rect), kCheckboxMaxWidth);
  [checkbox setFrame:rect];
  [checkbox_container_ addSubview:checkbox];

  base::scoped_nsobject<NSTextField> details(
      [[NSTextField alloc] initWithFrame:NSZeroRect]);
  [details setEditable:NO];
  [details setSelectable:NO];
  [details setBezeled:NO];
  [details setAttributedStringValue:
      constrained_window::GetAttributedLabelString(
          base::SysUTF16ToNSString(gallery.GetGalleryAdditionalDetails()),
          chrome_style::kTextFontStyle,
          NSNaturalTextAlignment,
          NSLineBreakByClipping
      )];
  [details setTextColor:[NSColor colorWithCalibratedRed:0.625
                                                  green:0.625
                                                   blue:0.625
                                                  alpha:1.0]];
  [details sizeToFit];
  NSRect details_rect = [details bounds];
  details_rect.origin.y = y_pos - 1;
  details_rect.origin.x = kCheckboxMargin + rect.size.width + kCheckboxMargin;
  details_rect.size.width = kCheckboxMaxWidth - details_rect.origin.x;
  [details setFrame:details_rect];

  [checkbox_container_ addSubview:details];
}

void MediaGalleriesDialogCocoa::UpdateGalleries() {
  InitDialogControls();
}

void MediaGalleriesDialogCocoa::OnConstrainedWindowClosed(
    ConstrainedWindowMac* window) {
  controller_->DialogFinished(accepted_);
}

ui::MenuModel* MediaGalleriesDialogCocoa::GetContextMenuModel(
    MediaGalleryPrefId prefid) {
  return controller_->GetContextMenuModel(prefid);
}

// static
MediaGalleriesDialog* MediaGalleriesDialog::Create(
    MediaGalleriesDialogController* controller) {
  base::scoped_nsobject<MediaGalleriesCocoaController> cocoa_controller(
      [[MediaGalleriesCocoaController alloc] init]);
  return new MediaGalleriesDialogCocoa(controller, cocoa_controller);
}