// 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/chromeos/network_message_observer.h"

#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/system/chromeos/network/network_observer.h"
#include "ash/system/tray/system_tray.h"
#include "ash/system/tray/system_tray_notifier.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/stl_util.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/network_library.h"
#include "chrome/browser/chromeos/notifications/balloon_view_host_chromeos.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/host_desktop.h"
#include "chrome/browser/ui/singleton_tabs.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/time_format.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"

namespace {
ash::NetworkObserver::NetworkType GetAshNetworkType(
    chromeos::ConnectionType connection_type) {
  switch (connection_type) {
   case chromeos::TYPE_CELLULAR:
     return ash::NetworkObserver::NETWORK_CELLULAR;
   case chromeos::TYPE_ETHERNET:
     return ash::NetworkObserver::NETWORK_ETHERNET;
   case chromeos::TYPE_WIFI:
     return ash::NetworkObserver::NETWORK_WIFI;
   case chromeos::TYPE_BLUETOOTH:
     return ash::NetworkObserver::NETWORK_BLUETOOTH;
   default:
     return ash::NetworkObserver::NETWORK_UNKNOWN;
  }
}

}  // namespace

namespace chromeos {

class NetworkMessageNotification : public ash::NetworkTrayDelegate {
 public:
  NetworkMessageNotification(Profile* profile,
                             ash::NetworkObserver::MessageType error_type)
      : error_type_(error_type) {
    switch (error_type) {
      case ash::NetworkObserver::ERROR_CONNECT_FAILED:
        title_ = l10n_util::GetStringUTF16(IDS_NETWORK_CONNECTION_ERROR_TITLE);
        return;
      case ash::NetworkObserver::MESSAGE_DATA_PROMO:
        NOTREACHED();
        return;
    }
    NOTREACHED();
  }

  // Overridden from ash::NetworkTrayDelegate:
  virtual void NotificationLinkClicked(size_t index) OVERRIDE {
    base::ListValue empty_value;
    if (!callback_.is_null())
      callback_.Run(&empty_value);
  }

  void Hide() {
    ash::Shell::GetInstance()->system_tray_notifier()->
        NotifyClearNetworkMessage(error_type_);
  }

  void SetTitle(const string16& title) {
    title_ = title;
  }

  void Show(chromeos::ConnectionType connection_type,
            const string16& message,
            const string16& link_text,
            const BalloonViewHost::MessageCallback& callback,
            bool urgent, bool sticky) {
    callback_ = callback;
    std::vector<string16> links;
    links.push_back(link_text);
    ash::NetworkObserver::NetworkType network_type = GetAshNetworkType(
        connection_type);
    ash::Shell::GetInstance()->system_tray_notifier()->NotifySetNetworkMessage(
        this, error_type_, network_type, title_, message, links);
  }

  void ShowAlways(chromeos::ConnectionType connection_type,
                  const string16& message,
                  const string16& link_text,
                  const BalloonViewHost::MessageCallback& callback,
                  bool urgent, bool sticky) {
    Show(connection_type, message, link_text, callback, urgent, sticky);
  }

 private:
  string16 title_;
  ash::NetworkObserver::MessageType error_type_;
  BalloonViewHost::MessageCallback callback_;
};

NetworkMessageObserver::NetworkMessageObserver(Profile* profile) {
  notification_connection_error_.reset(
      new NetworkMessageNotification(
          profile, ash::NetworkObserver::ERROR_CONNECT_FAILED));
  NetworkLibrary* netlib = CrosLibrary::Get()->GetNetworkLibrary();
  OnNetworkManagerChanged(netlib);
  // Note that this gets added as a NetworkManagerObserver
  // and UserActionObserver in startup_browser_creator.cc
}

NetworkMessageObserver::~NetworkMessageObserver() {
  NetworkLibrary* netlib = CrosLibrary::Get()->GetNetworkLibrary();
  netlib->RemoveNetworkManagerObserver(this);
  netlib->RemoveUserActionObserver(this);
  notification_connection_error_->Hide();
}

void NetworkMessageObserver::OnNetworkManagerChanged(NetworkLibrary* cros) {
  const Network* new_failed_network = NULL;
  // Check to see if we have any newly failed networks.
  for (WifiNetworkVector::const_iterator it = cros->wifi_networks().begin();
       it != cros->wifi_networks().end(); it++) {
    const WifiNetwork* net = *it;
    if (net->notify_failure()) {
      new_failed_network = net;
      break;  // There should only be one failed network.
    }
  }

  if (!new_failed_network) {
    for (WimaxNetworkVector::const_iterator it = cros->wimax_networks().begin();
         it != cros->wimax_networks().end(); ++it) {
      const WimaxNetwork* net = *it;
      if (net->notify_failure()) {
        new_failed_network = net;
        break;  // There should only be one failed network.
      }
    }
  }

  if (!new_failed_network) {
    for (CellularNetworkVector::const_iterator it =
             cros->cellular_networks().begin();
         it != cros->cellular_networks().end(); it++) {
      const CellularNetwork* net = *it;
      if (net->notify_failure()) {
        new_failed_network = net;
        break;  // There should only be one failed network.
      }
    }
  }

  if (!new_failed_network) {
    for (VirtualNetworkVector::const_iterator it =
             cros->virtual_networks().begin();
         it != cros->virtual_networks().end(); it++) {
      const VirtualNetwork* net = *it;
      if (net->notify_failure()) {
        new_failed_network = net;
        break;  // There should only be one failed network.
      }
    }
  }

  // Show connection error notification if necessary.
  if (new_failed_network) {
    VLOG(1) << "Failed Network: " << new_failed_network->service_path();
    notification_connection_error_->ShowAlways(
        new_failed_network->type(),
        l10n_util::GetStringFUTF16(
            IDS_NETWORK_CONNECTION_ERROR_MESSAGE_WITH_DETAILS,
            UTF8ToUTF16(new_failed_network->name()),
            UTF8ToUTF16(new_failed_network->GetErrorString())),
        string16(), BalloonViewHost::MessageCallback(), false, false);
  }
}

void NetworkMessageObserver::OnConnectionInitiated(NetworkLibrary* cros,
                                                   const Network* network) {
  // If user initiated any network connection, we hide the error notification.
  notification_connection_error_->Hide();
}

}  // namespace chromeos