// Copyright (c) 2011 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 <Cocoa/Cocoa.h>

#import "base/memory/scoped_nsobject.h"
#import "base/string_util.h"
#include "base/utf_string_conversions.h"
#import "chrome/app/chrome_command_ids.h"  // For translate menu command ids.
#import "chrome/browser/translate/translate_infobar_delegate.h"
#import "chrome/browser/ui/cocoa/browser_test_helper.h"
#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
#import "chrome/browser/ui/cocoa/infobars/infobar.h"
#import "chrome/browser/ui/cocoa/translate/translate_infobar_base.h"
#import "chrome/browser/ui/cocoa/translate/before_translate_infobar_controller.h"
#import "content/browser/site_instance.h"
#import "content/browser/tab_contents/tab_contents.h"
#import "testing/gmock/include/gmock/gmock.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/platform_test.h"

namespace {

// All states the translate toolbar can assume.
TranslateInfoBarDelegate::Type kTranslateToolbarStates[] = {
  TranslateInfoBarDelegate::BEFORE_TRANSLATE,
  TranslateInfoBarDelegate::AFTER_TRANSLATE,
  TranslateInfoBarDelegate::TRANSLATING,
  TranslateInfoBarDelegate::TRANSLATION_ERROR
};

class MockTranslateInfoBarDelegate : public TranslateInfoBarDelegate {
 public:
  MockTranslateInfoBarDelegate(TranslateInfoBarDelegate::Type type,
                               TranslateErrors::Type error,
                               TabContents* contents)
      : TranslateInfoBarDelegate(type, error, contents, "en", "es"){
    // Start out in the "Before Translate" state.
    type_ = type;

  }

  virtual string16 GetDisplayNameForLocale(const std::string& language_code) {
    return ASCIIToUTF16("Foo");
  }

  virtual bool IsLanguageBlacklisted() {
    return false;
  }

  virtual bool IsSiteBlacklisted() {
    return false;
  }

  virtual bool ShouldAlwaysTranslate() {
    return false;
  }

  MOCK_METHOD0(Translate, void());
  MOCK_METHOD0(RevertTranslation, void());
  MOCK_METHOD0(TranslationDeclined, void());
  MOCK_METHOD0(ToggleLanguageBlacklist, void());
  MOCK_METHOD0(ToggleSiteBlacklist, void());
  MOCK_METHOD0(ToggleAlwaysTranslate, void());
};

class TranslationInfoBarTest : public CocoaTest {
 public:
  BrowserTestHelper browser_helper_;
  scoped_ptr<TabContents> tab_contents;
  scoped_ptr<MockTranslateInfoBarDelegate> infobar_delegate;
  scoped_nsobject<TranslateInfoBarControllerBase> infobar_controller;

 public:
  // Each test gets a single Mock translate delegate for the lifetime of
  // the test.
  virtual void SetUp() {
    CocoaTest::SetUp();
   tab_contents.reset(
        new TabContents(browser_helper_.profile(),
                        NULL,
                        MSG_ROUTING_NONE,
                        NULL,
                        NULL));
    CreateInfoBar();
  }

  void CreateInfoBar() {
    CreateInfoBar(TranslateInfoBarDelegate::BEFORE_TRANSLATE);
  }

  void CreateInfoBar(TranslateInfoBarDelegate::Type type) {
    TranslateErrors::Type error = TranslateErrors::NONE;
    if (type == TranslateInfoBarDelegate::TRANSLATION_ERROR)
      error = TranslateErrors::NETWORK;
    infobar_delegate.reset(
        new MockTranslateInfoBarDelegate(type, error, tab_contents.get()));
    [[infobar_controller view] removeFromSuperview];
    scoped_ptr<InfoBar> infobar(
        static_cast<InfoBarDelegate*>(infobar_delegate.get())->CreateInfoBar());
    infobar_controller.reset(
        reinterpret_cast<TranslateInfoBarControllerBase*>(
            infobar->controller()));
    // We need to set the window to be wide so that the options button
    // doesn't overlap the other buttons.
    [test_window() setContentSize:NSMakeSize(2000, 500)];
    [[infobar_controller view] setFrame:NSMakeRect(0, 0, 2000, 500)];
    [[test_window() contentView] addSubview:[infobar_controller view]];
  }
};

// Check that we can instantiate a Translate Infobar correctly.
TEST_F(TranslationInfoBarTest, Instantiate) {
  CreateInfoBar();
  ASSERT_TRUE(infobar_controller.get());
}

// Check that clicking the Translate button calls Translate().
TEST_F(TranslationInfoBarTest, TranslateCalledOnButtonPress) {
  CreateInfoBar();

  EXPECT_CALL(*infobar_delegate, Translate()).Times(1);
  [infobar_controller ok:nil];
}

// Check that clicking the "Retry" button calls Translate() when we're
// in the error mode - http://crbug.com/41315 .
TEST_F(TranslationInfoBarTest, TranslateCalledInErrorMode) {
  CreateInfoBar(TranslateInfoBarDelegate::TRANSLATION_ERROR);

  EXPECT_CALL(*infobar_delegate, Translate()).Times(1);

  [infobar_controller ok:nil];
}

// Check that clicking the "Show Original button calls RevertTranslation().
TEST_F(TranslationInfoBarTest, RevertCalledOnButtonPress) {
  CreateInfoBar();

  EXPECT_CALL(*infobar_delegate, RevertTranslation()).Times(1);
  [infobar_controller showOriginal:nil];
}

// Check that items in the options menu are hooked up correctly.
TEST_F(TranslationInfoBarTest, OptionsMenuItemsHookedUp) {
  EXPECT_CALL(*infobar_delegate, Translate())
    .Times(0);

  [infobar_controller rebuildOptionsMenu:NO];
  NSMenu* optionsMenu = [infobar_controller optionsMenu];
  NSArray* optionsMenuItems = [optionsMenu itemArray];

  EXPECT_EQ(7U, [optionsMenuItems count]);

  // First item is the options menu button's title, so there's no need to test
  // that the target on that is setup correctly.
  for (NSUInteger i = 1; i < [optionsMenuItems count]; ++i) {
    NSMenuItem* item = [optionsMenuItems objectAtIndex:i];
    if (![item isSeparatorItem])
      EXPECT_EQ([item target], infobar_controller.get());
  }
  NSMenuItem* alwaysTranslateLanguateItem = [optionsMenuItems objectAtIndex:1];
  NSMenuItem* neverTranslateLanguateItem = [optionsMenuItems objectAtIndex:2];
  NSMenuItem* neverTranslateSiteItem = [optionsMenuItems objectAtIndex:3];
  // Separator at 4.
  NSMenuItem* reportBadLanguageItem = [optionsMenuItems objectAtIndex:5];
  NSMenuItem* aboutTranslateItem = [optionsMenuItems objectAtIndex:6];

  {
    EXPECT_CALL(*infobar_delegate, ToggleAlwaysTranslate())
    .Times(1);
    [infobar_controller optionsMenuChanged:alwaysTranslateLanguateItem];
  }

  {
    EXPECT_CALL(*infobar_delegate, ToggleLanguageBlacklist())
    .Times(1);
    [infobar_controller optionsMenuChanged:neverTranslateLanguateItem];
  }

  {
    EXPECT_CALL(*infobar_delegate, ToggleSiteBlacklist())
    .Times(1);
    [infobar_controller optionsMenuChanged:neverTranslateSiteItem];
  }

  {
    // Can't mock these effectively, so just check that the tag is set
    // correctly.
    EXPECT_EQ(IDC_TRANSLATE_REPORT_BAD_LANGUAGE_DETECTION,
              [reportBadLanguageItem tag]);
    EXPECT_EQ(IDC_TRANSLATE_OPTIONS_ABOUT, [aboutTranslateItem tag]);
  }
}

// Check that selecting a new item from the "Source Language" popup in "before
// translate" mode doesn't trigger a translation or change state.
// http://crbug.com/36666
TEST_F(TranslationInfoBarTest, Bug36666) {
  EXPECT_CALL(*infobar_delegate, Translate())
    .Times(0);

  CreateInfoBar();
  int arbitrary_index = 2;
  [infobar_controller sourceLanguageModified:arbitrary_index];
  EXPECT_CALL(*infobar_delegate, Translate())
    .Times(0);
}

// Check that the infobar lays itself out correctly when instantiated in
// each of the states.
// http://crbug.com/36895
TEST_F(TranslationInfoBarTest, Bug36895) {
  EXPECT_CALL(*infobar_delegate, Translate())
    .Times(0);

  for (size_t i = 0; i < arraysize(kTranslateToolbarStates); ++i) {
    CreateInfoBar(kTranslateToolbarStates[i]);
    EXPECT_TRUE(
        [infobar_controller verifyLayout]) << "Layout wrong, for state #" << i;
  }
}

// Verify that the infobar shows the "Always translate this language" button
// after doing 3 translations.
TEST_F(TranslationInfoBarTest, TriggerShowAlwaysTranslateButton) {
  TranslatePrefs translate_prefs(browser_helper_.profile()->GetPrefs());
  translate_prefs.ResetTranslationAcceptedCount("en");
  for (int i = 0; i < 4; ++i) {
    translate_prefs.IncrementTranslationAcceptedCount("en");
  }
  CreateInfoBar(TranslateInfoBarDelegate::BEFORE_TRANSLATE);
  BeforeTranslateInfobarController* controller =
      (BeforeTranslateInfobarController*)infobar_controller.get();
  EXPECT_TRUE([[controller alwaysTranslateButton] superview] !=  nil);
  EXPECT_TRUE([[controller neverTranslateButton] superview] == nil);
}

// Verify that the infobar shows the "Never translate this language" button
// after denying 3 translations.
TEST_F(TranslationInfoBarTest, TriggerShowNeverTranslateButton) {
  TranslatePrefs translate_prefs(browser_helper_.profile()->GetPrefs());
  translate_prefs.ResetTranslationDeniedCount("en");
  for (int i = 0; i < 4; ++i) {
    translate_prefs.IncrementTranslationDeniedCount("en");
  }
  CreateInfoBar(TranslateInfoBarDelegate::BEFORE_TRANSLATE);
  BeforeTranslateInfobarController* controller =
      (BeforeTranslateInfobarController*)infobar_controller.get();
  EXPECT_TRUE([[controller alwaysTranslateButton] superview] == nil);
  EXPECT_TRUE([[controller neverTranslateButton] superview] != nil);
}

} // namespace