// Copyright 2013 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 "ash/wm/caption_buttons/frame_caption_button.h"

#include "grit/ash_resources.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/animation/slide_animation.h"
#include "ui/gfx/canvas.h"

namespace ash {

namespace {

// The duration of the crossfade animation when swapping the button's icon.
const int kCrossfadeDurationMs = 200;

// The duration of the fade out animation of the old icon during a crossfade
// animation as a ratio of |kCrossfadeDurationMs|.
const float kFadeOutRatio = 0.5f;

}  // namespace

// static
const char FrameCaptionButton::kViewClassName[] = "FrameCaptionButton";

FrameCaptionButton::FrameCaptionButton(views::ButtonListener* listener,
                                       CaptionButtonIcon icon)
    : ImageButton(listener),
      icon_(icon),
      style_(STYLE_SHORT_RESTORED),
      last_paint_scale_(1.0f),
      animation_(new gfx::SlideAnimation(this)) {
  animation_->Reset(1);
  UpdateImages();
}

FrameCaptionButton::~FrameCaptionButton() {
}

void FrameCaptionButton::SetIcon(CaptionButtonIcon icon, Animate animate) {
  if (icon_ == icon)
    return;

  if (animate == ANIMATE_YES) {
    gfx::Canvas canvas(size(), last_paint_scale_, false);
    OnPaint(&canvas);
    crossfade_image_ = gfx::ImageSkia(canvas.ExtractImageRep());

    icon_ = icon;
    UpdateImages();

    animation_->Reset(0);
    animation_->SetSlideDuration(kCrossfadeDurationMs);
    animation_->Show();
  } else {
    animation_->Reset(1);
    icon_ = icon;
    UpdateImages();
  }
}

void FrameCaptionButton::SetStyle(Style style) {
  if (style_ == style)
    return;
  animation_->Reset(1);
  style_ = style;
  UpdateImages();
}

const char* FrameCaptionButton::GetClassName() const {
  return kViewClassName;
}

void FrameCaptionButton::OnPaint(gfx::Canvas* canvas) {
  last_paint_scale_ = canvas->image_scale();
  double animation_value = animation_->GetCurrentValue();
  int alpha = static_cast<int>(animation_value * 255);
  int crossfade_alpha = 0;
  if (animation_value < kFadeOutRatio) {
     crossfade_alpha = static_cast<int>(
         255 * (1 - animation_value / kFadeOutRatio));
  }
  if (crossfade_alpha > 0 && !crossfade_image_.isNull()) {
    gfx::Canvas composed_canvas(size(), last_paint_scale_, false);
    PaintWithAnimationEndState(&composed_canvas, alpha);

    SkPaint paint;
    paint.setAlpha(crossfade_alpha);
    paint.setXfermodeMode(SkXfermode::kPlus_Mode);
    composed_canvas.DrawImageInt(crossfade_image_, 0, 0, paint);

    canvas->DrawImageInt(
        gfx::ImageSkia(composed_canvas.ExtractImageRep()), 0, 0);
  } else {
    PaintWithAnimationEndState(canvas, alpha);
  }
}

void FrameCaptionButton::OnGestureEvent(ui::GestureEvent* event) {
  // ImageButton does not become pressed when the user drags off and then back
  // onto the button. Make FrameCaptionButton pressed in this case because this
  // behavior is more consistent with AlternateFrameSizeButton.
  if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
      event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
    if (HitTestPoint(event->location())) {
      SetState(STATE_PRESSED);
      RequestFocus();
      event->StopPropagation();
    } else {
      SetState(STATE_NORMAL);
    }
  } else if (event->type() == ui::ET_GESTURE_SCROLL_END) {
    if (HitTestPoint(event->location())) {
      SetState(STATE_HOVERED);
      NotifyClick(*event);
      event->StopPropagation();
    }
  }
  ImageButton::OnGestureEvent(event);
}

void FrameCaptionButton::StateChanged() {
  if (state_ == STATE_HOVERED || state_ == STATE_PRESSED)
    animation_->Reset(1);
}

void FrameCaptionButton::PaintWithAnimationEndState(gfx::Canvas* canvas,
                                                    int opacity) {
  gfx::ImageSkia img = GetImageToPaint();
  if (img.isNull())
    return;

  SkPaint paint;
  paint.setAlpha(opacity);
  canvas->DrawImageInt(img, 0, 0, paint);
}

void FrameCaptionButton::UpdateImages() {
  switch (icon_) {
    case CAPTION_BUTTON_ICON_MINIMIZE:
      SetImages(IDR_AURA_WINDOW_MINIMIZE_SHORT,
                IDR_AURA_WINDOW_MINIMIZE_SHORT_H,
                IDR_AURA_WINDOW_MINIMIZE_SHORT_P);
      break;
   case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE:
   case CAPTION_BUTTON_ICON_LEFT_SNAPPED:
   case CAPTION_BUTTON_ICON_RIGHT_SNAPPED:
     if (style_ == STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN) {
       SetImages(IDR_AURA_WINDOW_MAXIMIZED_RESTORE2,
                 IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H,
                 IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P);
     } else if (style_ == STYLE_SHORT_RESTORED) {
       SetImages(IDR_AURA_WINDOW_MAXIMIZED_RESTORE,
                 IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H,
                 IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P);
     } else {
       SetImages(IDR_AURA_WINDOW_MAXIMIZE,
                 IDR_AURA_WINDOW_MAXIMIZE_H,
                 IDR_AURA_WINDOW_MAXIMIZE_P);
     }
     break;
   case CAPTION_BUTTON_ICON_CLOSE:
     if (style_ == STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN) {
       SetImages(IDR_AURA_WINDOW_MAXIMIZED_CLOSE2,
                 IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H,
                 IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P);
     } else if (style_ == STYLE_SHORT_RESTORED) {
       SetImages(IDR_AURA_WINDOW_MAXIMIZED_CLOSE,
                 IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H,
                 IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P);
     } else {
       SetImages(IDR_AURA_WINDOW_CLOSE,
                 IDR_AURA_WINDOW_CLOSE_H,
                 IDR_AURA_WINDOW_CLOSE_P);
     }
     break;
  }

  SchedulePaint();
}

void FrameCaptionButton::SetImages(int normal_image_id,
                                   int hot_image_id,
                                   int pushed_image_id) {
  ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance();
  SetImage(STATE_NORMAL, resource_bundle.GetImageSkiaNamed(normal_image_id));
  SetImage(STATE_HOVERED, resource_bundle.GetImageSkiaNamed(hot_image_id));
  SetImage(STATE_PRESSED, resource_bundle.GetImageSkiaNamed(pushed_image_id));
}

void FrameCaptionButton::AnimationProgressed(const gfx::Animation* animation) {
  SchedulePaint();
}

}  // namespace ash
