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

#include "base/bind.h"
#include "base/command_line.h"
#include "base/prefs/pref_registry_simple.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/ui/browser_otr_state.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/notification_service.h"
#include "grit/theme_resources.h"

// How long to wait between checks for whether the user has been idle.
static const int kIdleRepeatingTimerWait = 10;  // Minutes (seconds if testing).

// How much idle time (since last input even was detected) must have passed
// until we notify that a critical update has occurred.
static const int kIdleAmount = 2;  // Hours (or seconds, if testing).

bool UseTestingIntervals() {
  // If a command line parameter specifying how long the upgrade check should
  // be, we assume it is for testing and switch to using seconds instead of
  // hours.
  const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
  return !cmd_line.GetSwitchValueASCII(
      switches::kCheckForUpdateIntervalSec).empty();
}

// static
void UpgradeDetector::RegisterPrefs(PrefRegistrySimple* registry) {
  registry->RegisterBooleanPref(prefs::kRestartLastSessionOnShutdown, false);
  registry->RegisterBooleanPref(prefs::kWasRestarted, false);
  registry->RegisterBooleanPref(prefs::kAttemptedToEnableAutoupdate, false);
}

int UpgradeDetector::GetIconResourceID(UpgradeNotificationIconType type) {
  if (type == UPGRADE_ICON_TYPE_BADGE) {
    // TODO(oshima): Badges no longer exist. remove this.
    NOTREACHED();
    return 0;
  }

  switch (upgrade_notification_stage_) {
    case UPGRADE_ANNOYANCE_NONE:
      return 0;
    case UPGRADE_ANNOYANCE_LOW:
      return IDR_UPDATE_MENU_SEVERITY_LOW;
    case UPGRADE_ANNOYANCE_ELEVATED:
      return IDR_UPDATE_MENU_SEVERITY_MEDIUM;
    case UPGRADE_ANNOYANCE_HIGH:
      return IDR_UPDATE_MENU_SEVERITY_HIGH;
    case UPGRADE_ANNOYANCE_SEVERE:
      return IDR_UPDATE_MENU_SEVERITY_HIGH;
    case UPGRADE_ANNOYANCE_CRITICAL:
      return IDR_UPDATE_MENU_SEVERITY_HIGH;
  }
  NOTREACHED();
  return 0;
}

UpgradeDetector::UpgradeDetector()
    : upgrade_available_(UPGRADE_AVAILABLE_NONE),
      critical_update_acknowledged_(false),
      upgrade_notification_stage_(UPGRADE_ANNOYANCE_NONE),
      notify_upgrade_(false) {
}

UpgradeDetector::~UpgradeDetector() {
}

void UpgradeDetector::NotifyUpgradeDetected() {
  upgrade_detected_time_ = base::Time::Now();
  critical_update_acknowledged_ = false;
}

void UpgradeDetector::NotifyUpgradeRecommended() {
  notify_upgrade_ = true;

  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_UPGRADE_RECOMMENDED,
      content::Source<UpgradeDetector>(this),
      content::NotificationService::NoDetails());

  switch (upgrade_available_) {
    case UPGRADE_NEEDED_OUTDATED_INSTALL: {
      content::NotificationService::current()->Notify(
          chrome::NOTIFICATION_OUTDATED_INSTALL,
          content::Source<UpgradeDetector>(this),
          content::NotificationService::NoDetails());
      break;
    }
    case UPGRADE_NEEDED_OUTDATED_INSTALL_NO_AU: {
      content::NotificationService::current()->Notify(
          chrome::NOTIFICATION_OUTDATED_INSTALL_NO_AU,
          content::Source<UpgradeDetector>(this),
          content::NotificationService::NoDetails());
      break;
    }
    case UPGRADE_AVAILABLE_CRITICAL: {
      int idle_timer = UseTestingIntervals() ?
          kIdleRepeatingTimerWait :
          kIdleRepeatingTimerWait * 60;  // To minutes.
      idle_check_timer_.Start(FROM_HERE,
          base::TimeDelta::FromSeconds(idle_timer),
          this, &UpgradeDetector::CheckIdle);
      break;
    }
    default:
      break;
  }
}

void UpgradeDetector::CheckIdle() {
  // CalculateIdleState expects an interval in seconds.
  int idle_time_allowed = UseTestingIntervals() ? kIdleAmount :
                                                  kIdleAmount * 60 * 60;

  CalculateIdleState(
      idle_time_allowed, base::Bind(&UpgradeDetector::IdleCallback,
                                    base::Unretained(this)));
}

void UpgradeDetector::IdleCallback(IdleState state) {
  // Don't proceed while an incognito window is open. The timer will still
  // keep firing, so this function will get a chance to re-evaluate this.
  if (chrome::IsOffTheRecordSessionActive())
    return;

  switch (state) {
    case IDLE_STATE_LOCKED:
      // Computer is locked, auto-restart.
      idle_check_timer_.Stop();
      chrome::AttemptRestart();
      break;
    case IDLE_STATE_IDLE:
      // Computer has been idle for long enough, show warning.
      idle_check_timer_.Stop();
      content::NotificationService::current()->Notify(
          chrome::NOTIFICATION_CRITICAL_UPGRADE_INSTALLED,
          content::Source<UpgradeDetector>(this),
          content::NotificationService::NoDetails());
      break;
    case IDLE_STATE_ACTIVE:
    case IDLE_STATE_UNKNOWN:
      break;
    default:
      NOTREACHED();  // Need to add any new value above (either providing
                     // automatic restart or show notification to user).
      break;
  }
}