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

#include <gdk/gdk.h>
#include <signal.h>
#include <sys/types.h>

#include <string>
#include <vector>

#include "base/command_line.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/cryptohome_library.h"
#include "chrome/browser/chromeos/customization_document.h"
#include "chrome/browser/chromeos/language_preferences.h"
#include "chrome/browser/chromeos/login/account_screen.h"
#include "chrome/browser/chromeos/login/enterprise_enrollment_screen.h"
#include "chrome/browser/chromeos/login/eula_view.h"
#include "chrome/browser/chromeos/login/existing_user_controller.h"
#include "chrome/browser/chromeos/login/helper.h"
#include "chrome/browser/chromeos/login/html_page_screen.h"
#include "chrome/browser/chromeos/login/login_display_host.h"
#include "chrome/browser/chromeos/login/login_utils.h"
#include "chrome/browser/chromeos/login/network_screen.h"
#include "chrome/browser/chromeos/login/registration_screen.h"
#include "chrome/browser/chromeos/login/update_screen.h"
#include "chrome/browser/chromeos/login/user_image_screen.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/login/wizard_accessibility_helper.h"
#include "chrome/browser/chromeos/metrics_cros_settings_provider.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/pref_names.h"
#include "content/common/notification_service.h"
#include "content/common/notification_type.h"
#include "ui/base/l10n/l10n_util.h"
#include "views/accelerator.h"
#include "views/view.h"
#include "views/widget/widget_gtk.h"

namespace {

// A boolean pref of the EULA accepted flag.
const char kEulaAccepted[] = "EulaAccepted";

// A string pref with initial locale set in VPD or manifest.
const char kInitialLocale[] = "intl.initial_locale";

// A boolean pref of the OOBE complete flag (first OOBE part before login).
const char kOobeComplete[] = "OobeComplete";

// A boolean pref of the device registered flag (second part after first login).
const char kDeviceRegistered[] = "DeviceRegistered";

// Path to flag file indicating that both parts of OOBE were completed.
const char kOobeCompleteFlagFilePath[] = "/home/chronos/.oobe_completed";

// Time in seconds that we wait for the device to reboot.
// If reboot didn't happen, ask user to reboot device manually.
const int kWaitForRebootTimeSec = 3;

// Interval in ms which is used for smooth screen showing.
static int kShowDelayMs = 400;

// RootView of the Widget WizardController creates. Contains the contents of the
// WizardController.
class ContentView : public views::View {
 public:
  ContentView()
      : accel_toggle_accessibility_(
            chromeos::WizardAccessibilityHelper::GetAccelerator()) {
#if defined(OFFICIAL_BUILD)
    accel_cancel_update_ =  views::Accelerator(ui::VKEY_ESCAPE,
                                               true, true, true);
#else
    accel_cancel_update_ =  views::Accelerator(ui::VKEY_ESCAPE,
                                               false, false, false);
    accel_account_screen_ = views::Accelerator(ui::VKEY_A,
                                               false, true, true);
    accel_login_screen_ = views::Accelerator(ui::VKEY_L,
                                             false, true, true);
    accel_network_screen_ = views::Accelerator(ui::VKEY_N,
                                               false, true, true);
    accel_update_screen_ = views::Accelerator(ui::VKEY_U,
                                              false, true, true);
    accel_image_screen_ = views::Accelerator(ui::VKEY_I,
                                             false, true, true);
    accel_eula_screen_ = views::Accelerator(ui::VKEY_E,
                                            false, true, true);
    accel_register_screen_ = views::Accelerator(ui::VKEY_R,
                                                false, true, true);
    accel_enterprise_enrollment_screen_ =
        views::Accelerator(ui::VKEY_P, false, true, true);
    AddAccelerator(accel_account_screen_);
    AddAccelerator(accel_login_screen_);
    AddAccelerator(accel_network_screen_);
    AddAccelerator(accel_update_screen_);
    AddAccelerator(accel_image_screen_);
    AddAccelerator(accel_eula_screen_);
    AddAccelerator(accel_register_screen_);
    AddAccelerator(accel_enterprise_enrollment_screen_);
#endif
    AddAccelerator(accel_toggle_accessibility_);
    AddAccelerator(accel_cancel_update_);
  }

  ~ContentView() {
    NotificationService::current()->Notify(
        NotificationType::WIZARD_CONTENT_VIEW_DESTROYED,
        NotificationService::AllSources(),
        NotificationService::NoDetails());
  }

  bool AcceleratorPressed(const views::Accelerator& accel) {
    WizardController* controller = WizardController::default_controller();
    if (!controller)
      return false;

    if (accel == accel_toggle_accessibility_) {
      chromeos::WizardAccessibilityHelper::GetInstance()->ToggleAccessibility();
    } else if (accel == accel_cancel_update_) {
      controller->CancelOOBEUpdate();
#if !defined(OFFICIAL_BUILD)
    } else if (accel == accel_account_screen_) {
      controller->ShowAccountScreen();
    } else if (accel == accel_login_screen_) {
      controller->ShowLoginScreen();
    } else if (accel == accel_network_screen_) {
      controller->ShowNetworkScreen();
    } else if (accel == accel_update_screen_) {
      controller->ShowUpdateScreen();
    } else if (accel == accel_image_screen_) {
      controller->ShowUserImageScreen();
    } else if (accel == accel_eula_screen_) {
      controller->ShowEulaScreen();
    } else if (accel == accel_register_screen_) {
      controller->ShowRegistrationScreen();
    } else if (accel == accel_enterprise_enrollment_screen_) {
      controller->ShowEnterpriseEnrollmentScreen();
#endif
    } else {
      return false;
    }

    return true;
  }

  virtual void Layout() {
    for (int i = 0; i < child_count(); ++i) {
      views::View* cur = GetChildViewAt(i);
      if (cur->IsVisible())
        cur->SetBounds(0, 0, width(), height());
    }
  }

 private:
#if !defined(OFFICIAL_BUILD)
  views::Accelerator accel_account_screen_;
  views::Accelerator accel_login_screen_;
  views::Accelerator accel_network_screen_;
  views::Accelerator accel_update_screen_;
  views::Accelerator accel_image_screen_;
  views::Accelerator accel_eula_screen_;
  views::Accelerator accel_register_screen_;
  views::Accelerator accel_enterprise_enrollment_screen_;
#endif
  views::Accelerator accel_toggle_accessibility_;
  views::Accelerator accel_cancel_update_;

  DISALLOW_COPY_AND_ASSIGN(ContentView);
};

// Saves boolean "Local State" preference and forces its persistence to disk.
void SaveBoolPreferenceForced(const char* pref_name, bool value) {
  PrefService* prefs = g_browser_process->local_state();
  prefs->SetBoolean(pref_name, value);
  prefs->SavePersistentPrefs();
}

// Saves integer "Local State" preference and forces its persistence to disk.
void SaveIntegerPreferenceForced(const char* pref_name, int value) {
  PrefService* prefs = g_browser_process->local_state();
  prefs->SetInteger(pref_name, value);
  prefs->SavePersistentPrefs();
}

// Saves string "Local State" preference and forces its persistence to disk.
void SaveStringPreferenceForced(const char* pref_name,
                                const std::string& value) {
  PrefService* prefs = g_browser_process->local_state();
  prefs->SetString(pref_name, value);
  prefs->SavePersistentPrefs();
}

}  // namespace

const char WizardController::kNetworkScreenName[] = "network";
const char WizardController::kLoginScreenName[] = "login";
const char WizardController::kAccountScreenName[] = "account";
const char WizardController::kUpdateScreenName[] = "update";
const char WizardController::kUserImageScreenName[] = "image";
const char WizardController::kEulaScreenName[] = "eula";
const char WizardController::kRegistrationScreenName[] = "register";
const char WizardController::kHTMLPageScreenName[] = "html";
const char WizardController::kEnterpriseEnrollmentScreenName[] = "enroll";

// Passing this parameter as a "first screen" initiates full OOBE flow.
const char WizardController::kOutOfBoxScreenName[] = "oobe";

// Special test value that commands not to create any window yet.
const char WizardController::kTestNoScreenName[] = "test:nowindow";

// Initialize default controller.
// static
WizardController* WizardController::default_controller_ = NULL;

///////////////////////////////////////////////////////////////////////////////
// WizardController, public:

WizardController::WizardController(chromeos::LoginDisplayHost* host,
                                   const gfx::Rect& screen_bounds)
    : widget_(NULL),
      contents_(NULL),
      screen_bounds_(screen_bounds),
      current_screen_(NULL),
      initial_show_(true),
      is_active_(true),
#if defined(OFFICIAL_BUILD)
      is_official_build_(true),
#else
      is_official_build_(false),
#endif
      is_out_of_box_(false),
      host_(host),
      observer_(NULL),
      usage_statistics_reporting_(true) {
  DCHECK(default_controller_ == NULL);
  default_controller_ = this;
}

WizardController::~WizardController() {
  if (widget_) {
    widget_->Close();
    widget_ = NULL;
  }

  if (default_controller_ == this) {
    default_controller_ = NULL;
  } else {
    NOTREACHED() << "More than one controller are alive.";
  }

  chromeos::WizardAccessibilityHelper::GetInstance()->
      UnregisterNotifications();
}

void WizardController::Init(const std::string& first_screen_name) {
  VLOG(1) << "Starting OOBE wizard with screen: " << first_screen_name;
  DCHECK(!contents_);
  first_screen_name_ = first_screen_name;

  contents_ = new ContentView();

  bool oobe_complete = IsOobeCompleted();
  if (!oobe_complete || first_screen_name == kOutOfBoxScreenName) {
    is_out_of_box_ = true;
  }

  ShowFirstScreen(first_screen_name);
}

void WizardController::CancelOOBEUpdate() {
  if (update_screen_.get() &&
      update_screen_.get() == current_screen_) {
    GetUpdateScreen()->CancelUpdate();
  }
}

chromeos::NetworkScreen* WizardController::GetNetworkScreen() {
  if (!network_screen_.get())
    network_screen_.reset(new chromeos::NetworkScreen(this));
  return network_screen_.get();
}

chromeos::AccountScreen* WizardController::GetAccountScreen() {
  if (!account_screen_.get())
    account_screen_.reset(new chromeos::AccountScreen(this));
  return account_screen_.get();
}

chromeos::UpdateScreen* WizardController::GetUpdateScreen() {
  if (!update_screen_.get()) {
    update_screen_.reset(new chromeos::UpdateScreen(this));
    update_screen_->SetRebootCheckDelay(kWaitForRebootTimeSec);
  }
  return update_screen_.get();
}

chromeos::UserImageScreen* WizardController::GetUserImageScreen() {
  if (!user_image_screen_.get())
    user_image_screen_.reset(new chromeos::UserImageScreen(this));
  return user_image_screen_.get();
}

chromeos::EulaScreen* WizardController::GetEulaScreen() {
  if (!eula_screen_.get())
    eula_screen_.reset(new chromeos::EulaScreen(this));
  return eula_screen_.get();
}

chromeos::RegistrationScreen* WizardController::GetRegistrationScreen() {
  if (!registration_screen_.get())
    registration_screen_.reset(new chromeos::RegistrationScreen(this));
  return registration_screen_.get();
}

chromeos::HTMLPageScreen* WizardController::GetHTMLPageScreen() {
  if (!html_page_screen_.get()) {
    CommandLine* command_line = CommandLine::ForCurrentProcess();
    std::string url;
    // It's strange but args may contains empty strings.
    for (size_t i = 0; i < command_line->args().size(); i++) {
      if (!command_line->args()[i].empty()) {
        DCHECK(url.empty()) << "More than one URL in command line";
        url = command_line->args()[i];
      }
    }
    DCHECK(!url.empty()) << "No URL in commane line";
    html_page_screen_.reset(new chromeos::HTMLPageScreen(this, url));
  }
  return html_page_screen_.get();
}

chromeos::EnterpriseEnrollmentScreen*
    WizardController::GetEnterpriseEnrollmentScreen() {
  if (!enterprise_enrollment_screen_.get()) {
    enterprise_enrollment_screen_.reset(
        new chromeos::EnterpriseEnrollmentScreen(this));
  }
  return enterprise_enrollment_screen_.get();
}

void WizardController::ShowNetworkScreen() {
  SetStatusAreaVisible(false);
  SetCurrentScreen(GetNetworkScreen());
  host_->SetOobeProgress(chromeos::BackgroundView::SELECT_NETWORK);
}

void WizardController::ShowLoginScreen() {
  SetStatusAreaVisible(true);
  host_->SetOobeProgress(chromeos::BackgroundView::SIGNIN);
  host_->StartSignInScreen();
  smooth_show_timer_.Stop();
  if (widget_) {
    widget_->Close();
    widget_ = NULL;
  }
  is_active_ = false;
}

void WizardController::ShowAccountScreen() {
  VLOG(1) << "Showing create account screen.";
  SetStatusAreaVisible(true);
  SetCurrentScreen(GetAccountScreen());
}

void WizardController::ShowUpdateScreen() {
  VLOG(1) << "Showing update screen.";
  SetStatusAreaVisible(true);
  SetCurrentScreen(GetUpdateScreen());
  // There is no special step for update.
#if defined(OFFICIAL_BUILD)
  host_->SetOobeProgress(chromeos::BackgroundView::EULA);
#else
  host_->SetOobeProgress(chromeos::BackgroundView::SELECT_NETWORK);
#endif
}

void WizardController::ShowUserImageScreen() {
  VLOG(1) << "Showing user image screen.";
  SetStatusAreaVisible(false);
  SetCurrentScreen(GetUserImageScreen());
  host_->SetOobeProgress(chromeos::BackgroundView::PICTURE);
  host_->SetShutdownButtonEnabled(false);
}

void WizardController::ShowEulaScreen() {
  VLOG(1) << "Showing EULA screen.";
  SetStatusAreaVisible(false);
  SetCurrentScreen(GetEulaScreen());
#if defined(OFFICIAL_BUILD)
  host_->SetOobeProgress(chromeos::BackgroundView::EULA);
#endif
}

void WizardController::ShowRegistrationScreen() {
  if (!IsRegisterScreenDefined()) {
    VLOG(1) << "Skipping registration screen: manifest not defined or invalid "
               "URL.";
    OnRegistrationSkipped();
    return;
  }
  VLOG(1) << "Showing registration screen.";
  SetStatusAreaVisible(true);
  SetCurrentScreen(GetRegistrationScreen());
#if defined(OFFICIAL_BUILD)
  host_->SetOobeProgress(chromeos::BackgroundView::REGISTRATION);
#endif
}

void WizardController::ShowHTMLPageScreen() {
  VLOG(1) << "Showing HTML page screen.";
  SetStatusAreaVisible(true);
  host_->SetOobeProgressBarVisible(false);
  SetCurrentScreen(GetHTMLPageScreen());
}

void WizardController::ShowEnterpriseEnrollmentScreen() {
  SetStatusAreaVisible(true);
  host_->SetOobeProgress(chromeos::BackgroundView::SIGNIN);
  SetCurrentScreen(GetEnterpriseEnrollmentScreen());
}

void WizardController::SkipRegistration() {
  if (current_screen_ == GetRegistrationScreen())
    OnRegistrationSkipped();
  else
    LOG(ERROR) << "Registration screen is not active.";
}

// static
void WizardController::RegisterPrefs(PrefService* local_state) {
  local_state->RegisterBooleanPref(kOobeComplete, false);
  local_state->RegisterIntegerPref(kDeviceRegistered, -1);
  local_state->RegisterBooleanPref(kEulaAccepted, false);
  local_state->RegisterStringPref(kInitialLocale, "en-US");
  // Check if the pref is already registered in case
  // Preferences::RegisterUserPrefs runs before this code in the future.
  if (local_state->FindPreference(prefs::kAccessibilityEnabled) == NULL) {
    local_state->RegisterBooleanPref(prefs::kAccessibilityEnabled, false);
  }
}

///////////////////////////////////////////////////////////////////////////////
// WizardController, ExitHandlers:
void WizardController::OnNetworkConnected() {
  if (is_official_build_) {
    if (!IsEulaAccepted()) {
      ShowEulaScreen();
    } else {
      // Possible cases:
      // 1. EULA was accepted, forced shutdown/reboot during update.
      // 2. EULA was accepted, planned reboot after update.
      // Make sure that device is up-to-date.
      InitiateOOBEUpdate();
    }
  } else {
    InitiateOOBEUpdate();
  }
}

void WizardController::OnNetworkOffline() {
  // TODO(dpolukhin): if(is_out_of_box_) we cannot work offline and
  // should report some error message here and stay on the same screen.
  ShowLoginScreen();
}

void WizardController::OnAccountCreateBack() {
  ShowLoginScreen();
}

void WizardController::OnAccountCreated() {
  ShowLoginScreen();
  // TODO(dpolukhin): clear password memory for real. Now it is not
  // a problem because we can't extract password from the form.
  password_.clear();
}

void WizardController::OnConnectionFailed() {
  // TODO(dpolukhin): show error message after login screen is displayed.
  ShowLoginScreen();
}

void WizardController::OnUpdateCompleted() {
  OnOOBECompleted();
}

void WizardController::OnEulaAccepted() {
  MarkEulaAccepted();
  chromeos::MetricsCrosSettingsProvider::SetMetricsStatus(
      usage_statistics_reporting_);
  InitiateOOBEUpdate();
}

void WizardController::OnUpdateErrorCheckingForUpdate() {
  // TODO(nkostylev): Update should be required during OOBE.
  // We do not want to block users from being able to proceed to the login
  // screen if there is any error checking for an update.
  // They could use "browse without sign-in" feature to set up the network to be
  // able to perform the update later.
  OnOOBECompleted();
}

void WizardController::OnUpdateErrorUpdating() {
  // If there was an error while getting or applying the update,
  // return to network selection screen.
  // TODO(nkostylev): Show message to the user explaining update error.
  // TODO(nkostylev): Update should be required during OOBE.
  // Temporary fix, need to migrate to new API. http://crosbug.com/4321
  OnOOBECompleted();
}

void WizardController::OnUserImageSelected() {
  // Notify host that we're about to launch browser session.
  // Host will mark itself (and all controllers/windows) for deletion.
  host_->OnSessionStart();
  // Launch browser after controller is deleted and its windows are closed.
  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      NewRunnableFunction(&chromeos::LoginUtils::DoBrowserLaunch,
                          ProfileManager::GetDefaultProfile()));
  // TODO(avayvod): Sync image with Google Sync.
}

void WizardController::OnUserImageSkipped() {
  OnUserImageSelected();
}

void WizardController::OnRegistrationSuccess() {
  MarkDeviceRegistered();
  if (chromeos::UserManager::Get()->IsLoggedInAsGuest()) {
    chromeos::LoginUtils::Get()->CompleteOffTheRecordLogin(start_url_);
  } else {
    ShowUserImageScreen();
  }
}

void WizardController::OnRegistrationSkipped() {
  // TODO(nkostylev): Track in a histogram?
  OnRegistrationSuccess();
}

void WizardController::OnEnterpriseEnrollmentDone() {
  ShowLoginScreen();
}

void WizardController::OnOOBECompleted() {
  MarkOobeCompleted();
  ShowLoginScreen();
}

void WizardController::InitiateOOBEUpdate() {
  GetUpdateScreen()->StartUpdate();
  SetCurrentScreenSmooth(GetUpdateScreen(), true);
}

///////////////////////////////////////////////////////////////////////////////
// WizardController, private:

views::Widget* WizardController::CreateScreenWindow(
    const gfx::Rect& bounds, bool initial_show) {
  views::Widget::CreateParams widget_params(
      views::Widget::CreateParams::TYPE_WINDOW);
  widget_params.transparent = true;
  widget_ = views::Widget::CreateWidget(widget_params);
  // Window transparency makes background flicker through controls that
  // are constantly updating its contents (like image view with video
  // stream). Hence enabling double buffer.
  static_cast<views::WidgetGtk*>(widget_)->EnableDoubleBuffer(true);
  widget_->Init(NULL, bounds);
  std::vector<int> params;
  // For initial show WM would animate background window.
  // Otherwise it stays unchaged.
  params.push_back(initial_show);
  chromeos::WmIpc::instance()->SetWindowType(
      widget_->GetNativeView(),
      chromeos::WM_IPC_WINDOW_LOGIN_GUEST,
      &params);
  widget_->SetContentsView(contents_);
  return widget_;
}

gfx::Rect WizardController::GetWizardScreenBounds(int screen_width,
                                                  int screen_height) const {
  int offset_x = (screen_bounds_.width() - screen_width) / 2;
  int offset_y = (screen_bounds_.height() - screen_height) / 2;
  int window_x = screen_bounds_.x() + offset_x;
  int window_y = screen_bounds_.y() + offset_y;
  return gfx::Rect(window_x, window_y, screen_width, screen_height);
}


void WizardController::SetCurrentScreen(WizardScreen* new_current) {
  SetCurrentScreenSmooth(new_current, false);
}

void WizardController::ShowCurrentScreen() {
  // ShowCurrentScreen may get called by smooth_show_timer_ even after
  // flow has been switched to sign in screen (ExistingUserController).
  if (!is_active_)
    return;

  smooth_show_timer_.Stop();

  bool force_widget_show = false;
  views::Widget* window = NULL;

  gfx::Rect current_bounds;
  if (widget_)
    current_bounds = widget_->GetClientAreaScreenBounds();
  gfx::Size new_screen_size = current_screen_->GetScreenSize();
  gfx::Rect new_bounds = GetWizardScreenBounds(new_screen_size.width(),
                                               new_screen_size.height());
  if (new_bounds != current_bounds) {
    if (widget_)
      widget_->Close();
    force_widget_show = true;
    window = CreateScreenWindow(new_bounds, initial_show_);
  }
  current_screen_->Show();
  contents_->Layout();
  contents_->SchedulePaint();
  if (force_widget_show) {
    // This keeps the window from flashing at startup.
    GdkWindow* gdk_window = window->GetNativeView()->window;
    gdk_window_set_back_pixmap(gdk_window, NULL, false);
    if (widget_)
      widget_->Show();
  }
}

void WizardController::SetCurrentScreenSmooth(WizardScreen* new_current,
                                              bool use_smoothing) {
  if (current_screen_ == new_current || new_current == NULL)
    return;

  smooth_show_timer_.Stop();

  if (current_screen_) {
    initial_show_ = false;
    current_screen_->Hide();
  }

  current_screen_ = new_current;

  if (use_smoothing) {
    smooth_show_timer_.Start(
        base::TimeDelta::FromMilliseconds(kShowDelayMs),
        this,
        &WizardController::ShowCurrentScreen);
    contents_->Layout();
    contents_->SchedulePaint();
  } else {
    ShowCurrentScreen();
  }
}

void WizardController::SetStatusAreaVisible(bool visible) {
  host_->SetStatusAreaVisible(visible);
}

void WizardController::ShowFirstScreen(const std::string& first_screen_name) {
  if (first_screen_name == kNetworkScreenName) {
    ShowNetworkScreen();
  } else if (first_screen_name == kLoginScreenName) {
    ShowLoginScreen();
  } else if (first_screen_name == kAccountScreenName) {
    ShowAccountScreen();
  } else if (first_screen_name == kUpdateScreenName) {
    InitiateOOBEUpdate();
  } else if (first_screen_name == kUserImageScreenName) {
    ShowUserImageScreen();
  } else if (first_screen_name == kEulaScreenName) {
    ShowEulaScreen();
  } else if (first_screen_name == kRegistrationScreenName) {
    if (is_official_build_) {
      ShowRegistrationScreen();
    } else {
      // Just proceed to image screen.
      OnRegistrationSuccess();
    }
  } else if (first_screen_name == kHTMLPageScreenName) {
    ShowHTMLPageScreen();
  } else if (first_screen_name == kEnterpriseEnrollmentScreenName) {
    ShowEnterpriseEnrollmentScreen();
  } else if (first_screen_name != kTestNoScreenName) {
    if (is_out_of_box_) {
      ShowNetworkScreen();
    } else {
      ShowLoginScreen();
    }
  }
}

// static
bool WizardController::IsEulaAccepted() {
  return g_browser_process->local_state()->GetBoolean(kEulaAccepted);
}

// static
bool WizardController::IsOobeCompleted() {
  return g_browser_process->local_state()->GetBoolean(kOobeComplete);
}

// static
void WizardController::MarkEulaAccepted() {
  SaveBoolPreferenceForced(kEulaAccepted, true);
}

// static
void WizardController::MarkOobeCompleted() {
  SaveBoolPreferenceForced(kOobeComplete, true);
}

static void CreateOobeCompleteFlagFile() {
  // Create flag file for boot-time init scripts.
  FilePath oobe_complete_path(kOobeCompleteFlagFilePath);
  if (!file_util::PathExists(oobe_complete_path)) {
    FILE* oobe_flag_file = file_util::OpenFile(oobe_complete_path, "w+b");
    if (oobe_flag_file == NULL)
      DLOG(WARNING) << kOobeCompleteFlagFilePath << " doesn't exist.";
    else
      file_util::CloseFile(oobe_flag_file);
  }
}

// static
bool WizardController::IsDeviceRegistered() {
  int value = g_browser_process->local_state()->GetInteger(kDeviceRegistered);
  if (value > 0) {
    // Recreate flag file in case it was lost.
    BrowserThread::PostTask(
        BrowserThread::FILE,
        FROM_HERE,
        NewRunnableFunction(&CreateOobeCompleteFlagFile));
    return true;
  } else if (value == 0) {
    return false;
  } else {
    // Pref is not set. For compatibility check flag file. It causes blocking
    // IO on UI thread. But it's required for update from old versions.
    base::ThreadRestrictions::ScopedAllowIO allow_io;
    FilePath oobe_complete_flag_file_path(kOobeCompleteFlagFilePath);
    bool file_exists = file_util::PathExists(oobe_complete_flag_file_path);
    SaveIntegerPreferenceForced(kDeviceRegistered, file_exists ? 1 : 0);
    return file_exists;
  }
}

// static
void WizardController::MarkDeviceRegistered() {
  SaveIntegerPreferenceForced(kDeviceRegistered, 1);
  BrowserThread::PostTask(
      BrowserThread::FILE,
      FROM_HERE,
      NewRunnableFunction(&CreateOobeCompleteFlagFile));
}

// static
std::string WizardController::GetInitialLocale() {
  std::string locale =
      g_browser_process->local_state()->GetString(kInitialLocale);
  if (!l10n_util::IsValidLocaleSyntax(locale))
    locale = "en-US";
  return locale;
}

// static
void WizardController::SetInitialLocale(const std::string& locale) {
  if (l10n_util::IsValidLocaleSyntax(locale))
    SaveStringPreferenceForced(kInitialLocale, locale);
  else
    NOTREACHED();
}

// static
bool WizardController::IsRegisterScreenDefined() {
  const chromeos::StartupCustomizationDocument* manifest =
      chromeos::StartupCustomizationDocument::GetInstance();
  return manifest->IsReady() &&
         GURL(manifest->registration_url()).is_valid();
}

///////////////////////////////////////////////////////////////////////////////
// WizardController, chromeos::ScreenObserver overrides:
void WizardController::OnExit(ExitCodes exit_code) {
  LOG(INFO) << "Wizard screen exit code: " << exit_code;
  switch (exit_code) {
    case NETWORK_CONNECTED:
      OnNetworkConnected();
      break;
    case NETWORK_OFFLINE:
      OnNetworkOffline();
      break;
    case ACCOUNT_CREATE_BACK:
      OnAccountCreateBack();
      break;
    case ACCOUNT_CREATED:
      OnAccountCreated();
      break;
    case CONNECTION_FAILED:
      OnConnectionFailed();
      break;
    case UPDATE_INSTALLED:
    case UPDATE_NOUPDATE:
      OnUpdateCompleted();
      break;
    case UPDATE_ERROR_CHECKING_FOR_UPDATE:
      OnUpdateErrorCheckingForUpdate();
      break;
    case UPDATE_ERROR_UPDATING:
      OnUpdateErrorUpdating();
      break;
    case USER_IMAGE_SELECTED:
      OnUserImageSelected();
      break;
    case USER_IMAGE_SKIPPED:
      OnUserImageSkipped();
      break;
    case EULA_ACCEPTED:
      OnEulaAccepted();
      break;
    case EULA_BACK:
      ShowNetworkScreen();
      break;
    case REGISTRATION_SUCCESS:
      OnRegistrationSuccess();
      break;
    case REGISTRATION_SKIPPED:
      OnRegistrationSkipped();
      break;
    case ENTERPRISE_ENROLLMENT_CANCELLED:
    case ENTERPRISE_ENROLLMENT_COMPLETED:
      OnEnterpriseEnrollmentDone();
      break;
    default:
      NOTREACHED();
  }
}

void WizardController::OnSetUserNamePassword(const std::string& username,
                                             const std::string& password) {
  username_ = username;
  password_ = password;
}

///////////////////////////////////////////////////////////////////////////////
// WizardController, WizardScreen overrides:
views::View* WizardController::GetWizardView() {
  return contents_;
}

chromeos::ScreenObserver* WizardController::GetObserver(WizardScreen* screen) {
  return observer_ ? observer_ : this;
}

void WizardController::SetZeroDelays() {
  kShowDelayMs = 0;
}