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

#include "chrome/browser/chromeos/options/vpn_config_view.h"

#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "views/controls/button/image_button.h"
#include "views/controls/button/native_button.h"
#include "views/controls/label.h"
#include "views/controls/textfield/textfield.h"
#include "views/layout/grid_layout.h"
#include "views/layout/layout_constants.h"
#include "views/window/window.h"

namespace {

string16 ProviderTypeToString(chromeos::VirtualNetwork::ProviderType type) {
  switch (type) {
    case chromeos::VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_PSK:
      return l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_L2TP_IPSEC_PSK);
    case chromeos::VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT:
      return l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_L2TP_IPSEC_USER_CERT);
    case chromeos::VirtualNetwork::PROVIDER_TYPE_OPEN_VPN:
      return l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_OPEN_VPN);
    case chromeos::VirtualNetwork::PROVIDER_TYPE_MAX:
      break;
  }
  NOTREACHED();
  return string16();
}

}  // namespace

namespace chromeos {

int VPNConfigView::ProviderTypeComboboxModel::GetItemCount() {
  // TODO(stevenjb): Include OpenVPN option once enabled.
  return VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT + 1;
  // return VirtualNetwork::PROVIDER_TYPE_MAX;
}

string16 VPNConfigView::ProviderTypeComboboxModel::GetItemAt(int index) {
  VirtualNetwork::ProviderType type =
      static_cast<VirtualNetwork::ProviderType>(index);
  return ProviderTypeToString(type);
}

VPNConfigView::UserCertComboboxModel::UserCertComboboxModel() {
  // TODO(jamescook): populate user_certs_. chromium-os:14111
}

int VPNConfigView::UserCertComboboxModel::GetItemCount() {
  return static_cast<int>(user_certs_.size());
}

string16 VPNConfigView::UserCertComboboxModel::GetItemAt(int index) {
  if (index >= 0 && index < static_cast<int>(user_certs_.size()))
    return ASCIIToUTF16(user_certs_[index]);
  return string16();
}

VPNConfigView::VPNConfigView(NetworkConfigView* parent, VirtualNetwork* vpn)
    : ChildNetworkConfigView(parent, vpn) {
  Init(vpn);
}

VPNConfigView::VPNConfigView(NetworkConfigView* parent)
    : ChildNetworkConfigView(parent) {
  Init(NULL);
}

VPNConfigView::~VPNConfigView() {
}

void VPNConfigView::UpdateCanLogin() {
  parent_->GetDialogClientView()->UpdateDialogButtons();
}

string16 VPNConfigView::GetTitle() {
  return l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_ADD_VPN);
}

bool VPNConfigView::CanLogin() {
  static const size_t kMinPassphraseLen = 0;  // TODO(stevenjb): min length?
  if (service_path_.empty() &&
      (GetService().empty() || GetServer().empty()))
    return false;
  if (provider_type_ == VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_PSK &&
      psk_passphrase_textfield_->text().length() < kMinPassphraseLen)
    return false;
  if (GetUsername().empty())
    return false;
  if (user_passphrase_textfield_->text().length() < kMinPassphraseLen)
    return false;
  return true;
}

void VPNConfigView::UpdateErrorLabel() {
  std::string error_msg;
  if (!service_path_.empty()) {
    // TODO(kuan): differentiate between bad psk and user passphrases.
    NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary();
    VirtualNetwork* vpn = cros->FindVirtualNetworkByPath(service_path_);
    if (vpn && vpn->failed()) {
      if (vpn->error() == ERROR_BAD_PASSPHRASE) {
        error_msg = l10n_util::GetStringUTF8(
            IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_BAD_PASSPHRASE);
      } else {
        error_msg = vpn->GetErrorString();
      }
    }
  }
  if (!error_msg.empty()) {
    error_label_->SetText(UTF8ToWide(error_msg));
    error_label_->SetVisible(true);
  } else {
    error_label_->SetVisible(false);
  }
}

void VPNConfigView::ContentsChanged(views::Textfield* sender,
                                    const string16& new_contents) {
  if (sender == server_textfield_ && !service_text_modified_) {
    // Set the service name to the server name up to '.', unless it has
    // been explicityly set by the user.
    string16 server = server_textfield_->text();
    string16::size_type n = server.find_first_of(L'.');
    service_name_from_server_ = server.substr(0, n);
    service_textfield_->SetText(service_name_from_server_);
  }
  if (sender == service_textfield_) {
    if (new_contents.empty())
      service_text_modified_ = false;
    else if (new_contents != service_name_from_server_)
      service_text_modified_ = true;
  }
  UpdateCanLogin();
}

bool VPNConfigView::HandleKeyEvent(views::Textfield* sender,
                                   const views::KeyEvent& key_event) {
  if ((sender == psk_passphrase_textfield_ ||
       sender == user_passphrase_textfield_) &&
      key_event.key_code() == ui::VKEY_RETURN) {
    parent_->GetDialogClientView()->AcceptWindow();
  }
  return false;
}

void VPNConfigView::ButtonPressed(views::Button* sender,
                                  const views::Event& event) {
}

void VPNConfigView::ItemChanged(views::Combobox* combo_box,
                                int prev_index, int new_index) {
  if (prev_index == new_index)
    return;
  if (combo_box == provider_type_combobox_) {
    provider_type_ = static_cast<VirtualNetwork::ProviderType>(new_index);
    EnableControls();
  } else if (combo_box == user_cert_combobox_) {
    // Nothing to do for now.
  } else {
    NOTREACHED();
  }
  UpdateCanLogin();
}

bool VPNConfigView::Login() {
  NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary();
  if (service_path_.empty()) {
    switch (provider_type_) {
      case VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_PSK:
        cros->ConnectToVirtualNetworkPSK(GetService(),
                                         GetServer(),
                                         GetPSKPassphrase(),
                                         GetUsername(),
                                         GetUserPassphrase());
        break;
      case VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT:
      case VirtualNetwork::PROVIDER_TYPE_OPEN_VPN:
        // TODO(stevenjb): Add support for OpenVPN and user certs.
        LOG(WARNING) << "Unsupported provider type: " << provider_type_;
        break;
      case VirtualNetwork::PROVIDER_TYPE_MAX:
        break;
    }
  } else {
    VirtualNetwork* vpn = cros->FindVirtualNetworkByPath(service_path_);
    if (!vpn) {
      // TODO(stevenjb): Add notification for this.
      LOG(WARNING) << "VPN no longer exists: " << service_path_;
      return true;  // Close dialog.
    }
    switch (provider_type_) {
      case VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_PSK:
        vpn->SetPSKPassphrase(GetPSKPassphrase());
        break;
      case VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT:
      case VirtualNetwork::PROVIDER_TYPE_OPEN_VPN: {
        const std::string user_cert = UTF16ToUTF8(
            user_cert_combobox_->model()->GetItemAt(
                user_cert_combobox_->selected_item()));
        vpn->SetUserCert(user_cert);
        break;
      }
      case VirtualNetwork::PROVIDER_TYPE_MAX:
        break;
    }
    vpn->SetUsername(GetUsername());
    vpn->SetUserPassphrase(GetUserPassphrase());

    cros->ConnectToVirtualNetwork(vpn);
  }
  // Connection failures are responsible for updating the UI, including
  // reopening dialogs.
  return true;  // Close dialog.
}

void VPNConfigView::Cancel() {
}

void VPNConfigView::InitFocus() {
  // TODO(jamescook): Put focus in a more reasonable widget.
}

const std::string VPNConfigView::GetTextFromField(
    views::Textfield* textfield, bool trim_whitespace) const {
  std::string untrimmed = UTF16ToUTF8(textfield->text());
  if (!trim_whitespace)
    return untrimmed;
  std::string result;
  TrimWhitespaceASCII(untrimmed, TRIM_ALL, &result);
  return result;
}

const std::string VPNConfigView::GetService() const {
  if (service_textfield_ != NULL)
    return GetTextFromField(service_textfield_, true);
  return service_path_;
}

const std::string VPNConfigView::GetServer() const {
  if (server_textfield_ != NULL)
    return GetTextFromField(server_textfield_, true);
  return server_hostname_;
}

const std::string VPNConfigView::GetPSKPassphrase() const {
  if (psk_passphrase_textfield_->IsEnabled() &&
      psk_passphrase_textfield_->IsVisible())
    return GetTextFromField(psk_passphrase_textfield_, false);
  return std::string();
}

const std::string VPNConfigView::GetUsername() const {
  return GetTextFromField(username_textfield_, true);
}

const std::string VPNConfigView::GetUserPassphrase() const {
  return GetTextFromField(user_passphrase_textfield_, false);
}

void VPNConfigView::Init(VirtualNetwork* vpn) {
  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
  SetLayoutManager(layout);

  int column_view_set_id = 0;
  views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id);
  // Label.
  column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1,
                        views::GridLayout::USE_PREF, 0, 0);
  column_set->AddPaddingColumn(0, views::kRelatedControlSmallHorizontalSpacing);
  // Textfield, combobox.
  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
                        views::GridLayout::USE_PREF, 0,
                        ChildNetworkConfigView::kPassphraseWidth);
  column_set->AddPaddingColumn(0, views::kRelatedControlSmallHorizontalSpacing);
  // Passphrase visible button.
  column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::FILL, 1,
                        views::GridLayout::USE_PREF, 0, 0);

  // Initialize members.
  service_text_modified_ = false;
  provider_type_ = vpn ?
      vpn->provider_type() :
      chromeos::VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_PSK;

  // Server label and input.
  layout->StartRow(0, column_view_set_id);
  layout->AddView(new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_SERVER_HOSTNAME))));
  if (!vpn) {
    server_textfield_ = new views::Textfield(views::Textfield::STYLE_DEFAULT);
    server_textfield_->SetController(this);
    layout->AddView(server_textfield_);
    server_text_ = NULL;
  } else {
    server_hostname_ = vpn->server_hostname();
    server_text_ = new views::Label(UTF8ToWide(server_hostname_));
    server_text_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
    layout->AddView(server_text_);
    server_textfield_ = NULL;
  }
  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  // Service label and name or input.
  layout->StartRow(0, column_view_set_id);
  layout->AddView(new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_SERVICE_NAME))));
  if (!vpn) {
    service_textfield_ = new views::Textfield(views::Textfield::STYLE_DEFAULT);
    service_textfield_->SetController(this);
    layout->AddView(service_textfield_);
    service_text_ = NULL;
  } else {
    service_text_ = new views::Label(ASCIIToWide(vpn->name()));
    service_text_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
    layout->AddView(service_text_);
    service_textfield_ = NULL;
  }
  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  // Provider type label and select.
  layout->StartRow(0, column_view_set_id);
  layout->AddView(new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_PROVIDER_TYPE))));
  if (!vpn) {
    provider_type_combobox_ =
        new views::Combobox(new ProviderTypeComboboxModel());
    provider_type_combobox_->set_listener(this);
    layout->AddView(provider_type_combobox_);
    provider_type_text_label_ = NULL;
  } else {
    provider_type_text_label_ =
        new views::Label(UTF16ToWide(ProviderTypeToString(provider_type_)));
    layout->AddView(provider_type_text_label_);
    provider_type_combobox_ = NULL;
  }
  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  // PSK passphrase label, input and visible button.
  layout->StartRow(0, column_view_set_id);
  psk_passphrase_label_ =  new views::Label(UTF16ToWide(
      l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_PSK_PASSPHRASE)));
  layout->AddView(psk_passphrase_label_);
  psk_passphrase_textfield_ = new views::Textfield(
      views::Textfield::STYLE_PASSWORD);
  psk_passphrase_textfield_->SetController(this);
  if (vpn && !vpn->psk_passphrase().empty())
    psk_passphrase_textfield_->SetText(UTF8ToUTF16(vpn->psk_passphrase()));
  layout->AddView(psk_passphrase_textfield_);
  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  // User certificate label and input.
  layout->StartRow(0, column_view_set_id);
  user_cert_label_ = new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USER_CERT)));
  layout->AddView(user_cert_label_);
  user_cert_combobox_ = new views::Combobox(new UserCertComboboxModel());
  user_cert_combobox_->set_listener(this);
  if (vpn && !vpn->user_cert().empty()) {
    string16 user_cert = UTF8ToUTF16(vpn->user_cert());
    for (int i = 0; i < user_cert_combobox_->model()->GetItemCount(); ++i) {
      if (user_cert_combobox_->model()->GetItemAt(i) == user_cert) {
        user_cert_combobox_->SetSelectedItem(i);
        break;
      }
    }
  }
  layout->AddView(user_cert_combobox_);
  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  // Username label and input.
  layout->StartRow(0, column_view_set_id);
  layout->AddView(new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(
      IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USERNAME))));
  username_textfield_ = new views::Textfield(views::Textfield::STYLE_DEFAULT);
  username_textfield_->SetController(this);
  if (vpn && !vpn->username().empty())
    username_textfield_->SetText(UTF8ToUTF16(vpn->username()));
  layout->AddView(username_textfield_);
  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  // User passphrase label, input and visble button.
  layout->StartRow(0, column_view_set_id);
  layout->AddView(new views::Label(UTF16ToWide(
      l10n_util::GetStringUTF16(
          IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USER_PASSPHRASE))));
  user_passphrase_textfield_ = new views::Textfield(
      views::Textfield::STYLE_PASSWORD);
  user_passphrase_textfield_->SetController(this);
  if (vpn && !vpn->user_passphrase().empty())
    user_passphrase_textfield_->SetText(UTF8ToUTF16(vpn->user_passphrase()));
  layout->AddView(user_passphrase_textfield_);
  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);

  // Error label.
  layout->StartRow(0, column_view_set_id);
  layout->SkipColumns(1);
  error_label_ = new views::Label();
  error_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
  error_label_->SetColor(SK_ColorRED);
  layout->AddView(error_label_);

  // Enable controls based on provider type combo.
  EnableControls();

  // Set or hide the error text.
  UpdateErrorLabel();
}

void VPNConfigView::EnableControls() {
  switch (provider_type_) {
    case VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_PSK:
      psk_passphrase_label_->SetEnabled(true);
      psk_passphrase_textfield_->SetEnabled(true);
      user_cert_label_->SetEnabled(false);
      user_cert_combobox_->SetEnabled(false);
      break;
    case VirtualNetwork::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT:
    case VirtualNetwork::PROVIDER_TYPE_OPEN_VPN:
      psk_passphrase_label_->SetEnabled(false);
      psk_passphrase_textfield_->SetEnabled(false);
      user_cert_label_->SetEnabled(true);
      user_cert_combobox_->SetEnabled(true);
      break;
    default:
      NOTREACHED();
  }
}

}  // namespace chromeos