// 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 "ui/views/controls/throbber.h"

#include "base/time/time.h"
#include "grit/ui_resources.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"

using base::Time;
using base::TimeDelta;

namespace views {

Throbber::Throbber(int frame_time_ms,
                   bool paint_while_stopped)
    : running_(false),
      paint_while_stopped_(paint_while_stopped),
      frames_(NULL),
      frame_time_(TimeDelta::FromMilliseconds(frame_time_ms)) {
  SetFrames(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
      IDR_THROBBER).ToImageSkia());
}

Throbber::~Throbber() {
  Stop();
}

void Throbber::Start() {
  if (running_)
    return;

  start_time_ = Time::Now();

  timer_.Start(FROM_HERE, frame_time_ - TimeDelta::FromMilliseconds(10),
               this, &Throbber::Run);

  running_ = true;

  SchedulePaint();  // paint right away
}

void Throbber::Stop() {
  if (!running_)
    return;

  timer_.Stop();

  running_ = false;
  SchedulePaint();  // Important if we're not painting while stopped
}

void Throbber::SetFrames(const gfx::ImageSkia* frames) {
  frames_ = frames;
  DCHECK(frames_->width() > 0 && frames_->height() > 0);
  DCHECK(frames_->width() % frames_->height() == 0);
  frame_count_ = frames_->width() / frames_->height();
  PreferredSizeChanged();
}

void Throbber::Run() {
  DCHECK(running_);

  SchedulePaint();
}

gfx::Size Throbber::GetPreferredSize() const {
  return gfx::Size(frames_->height(), frames_->height());
}

void Throbber::OnPaint(gfx::Canvas* canvas) {
  if (!running_ && !paint_while_stopped_)
    return;

  const TimeDelta elapsed_time = Time::Now() - start_time_;
  const int current_frame =
      static_cast<int>(elapsed_time / frame_time_) % frame_count_;

  int image_size = frames_->height();
  int image_offset = current_frame * image_size;
  canvas->DrawImageInt(*frames_,
                       image_offset, 0, image_size, image_size,
                       0, 0, image_size, image_size,
                       false);
}



// Smoothed throbber ---------------------------------------------------------


// Delay after work starts before starting throbber, in milliseconds.
static const int kStartDelay = 200;

// Delay after work stops before stopping, in milliseconds.
static const int kStopDelay = 50;


SmoothedThrobber::SmoothedThrobber(int frame_time_ms)
    : Throbber(frame_time_ms, /* paint_while_stopped= */ false),
      start_delay_ms_(kStartDelay),
      stop_delay_ms_(kStopDelay) {
}

SmoothedThrobber::~SmoothedThrobber() {}

void SmoothedThrobber::Start() {
  stop_timer_.Stop();

  if (!running_ && !start_timer_.IsRunning()) {
    start_timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(start_delay_ms_),
                       this, &SmoothedThrobber::StartDelayOver);
  }
}

void SmoothedThrobber::StartDelayOver() {
  Throbber::Start();
}

void SmoothedThrobber::Stop() {
  if (!running_)
    start_timer_.Stop();

  stop_timer_.Stop();
  stop_timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(stop_delay_ms_),
                    this, &SmoothedThrobber::StopDelayOver);
}

void SmoothedThrobber::StopDelayOver() {
  Throbber::Stop();
}

// Checkmark throbber ---------------------------------------------------------

CheckmarkThrobber::CheckmarkThrobber()
    : Throbber(kFrameTimeMs, false),
      checked_(false),
      checkmark_(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
          IDR_CHECKMARK).ToImageSkia()) {
}

void CheckmarkThrobber::SetChecked(bool checked) {
  bool changed = checked != checked_;
  if (changed) {
    checked_ = checked;
    SchedulePaint();
  }
}

void CheckmarkThrobber::OnPaint(gfx::Canvas* canvas) {
  if (running_) {
    // Let the throbber throb...
    Throbber::OnPaint(canvas);
    return;
  }
  // Otherwise we paint our tick mark or nothing depending on our state.
  if (checked_) {
    int checkmark_x = (width() - checkmark_->width()) / 2;
    int checkmark_y = (height() - checkmark_->height()) / 2;
    canvas->DrawImageInt(*checkmark_, checkmark_x, checkmark_y);
  }
}

}  // namespace views