// Copyright (c) 2009 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.

#ifndef CHROME_BROWSER_UI_COCOA_STATUS_BUBBLE_MAC_H_
#define CHROME_BROWSER_UI_COCOA_STATUS_BUBBLE_MAC_H_
#pragma once

#include <string>

#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>

#include "base/string16.h"
#include "base/task.h"
#include "chrome/browser/ui/status_bubble.h"
#include "googleurl/src/gurl.h"

class GURL;
class StatusBubbleMacTest;

class StatusBubbleMac : public StatusBubble {
 public:
  // The various states that a status bubble may be in.  Public for delegate
  // access (for testing).
  enum StatusBubbleState {
    kBubbleHidden,         // Fully hidden
    kBubbleShowingTimer,   // Waiting to fade in
    kBubbleShowingFadeIn,  // In a fade-in transition
    kBubbleShown,          // Fully visible
    kBubbleHidingTimer,    // Waiting to fade out
    kBubbleHidingFadeOut   // In a fade-out transition
  };

  StatusBubbleMac(NSWindow* parent, id delegate);
  virtual ~StatusBubbleMac();

  // StatusBubble implementation.
  virtual void SetStatus(const string16& status);
  virtual void SetURL(const GURL& url, const string16& languages);
  virtual void Hide();
  virtual void MouseMoved(const gfx::Point& location, bool left_content);
  virtual void UpdateDownloadShelfVisibility(bool visible);

  // Mac-specific method: Update the size and position of the status bubble to
  // match the parent window. Safe to call even when the status bubble does not
  // exist.
  void UpdateSizeAndPosition();

  // Mac-specific method: Change the parent window of the status bubble. Safe to
  // call even when the status bubble does not exist.
  void SwitchParentWindow(NSWindow* parent);

  // Delegate method called when a fade-in or fade-out transition has
  // completed.  This is public so that it may be visible to the CAAnimation
  // delegate, which is an Objective-C object.
  void AnimationDidStop(CAAnimation* animation, bool finished);

  // Expand the bubble to fit a URL too long for the standard bubble size.
  void ExpandBubble();

 private:
  friend class StatusBubbleMacTest;

  // Setter for state_.  Use this instead of writing to state_ directly so
  // that state changes can be observed by unit tests.
  void SetState(StatusBubbleState state);

  // Sets the bubble text for SetStatus and SetURL.
  void SetText(const string16& text, bool is_url);

  // Construct the window/widget if it does not already exist. (Safe to call if
  // it does.)
  void Create();

  // Attaches the status bubble window to its parent window. Safe to call even
  // when already attached.
  void Attach();

  // Detaches the status bubble window from its parent window.
  void Detach();

  // Is the status bubble attached to the browser window? It should be attached
  // when shown and during any fades, but should be detached when hidden.
  bool is_attached() { return [window_ parentWindow] != nil; }

  // Begins fading the status bubble window in or out depending on the value
  // of |show|.  This must be called from the appropriate fade state,
  // kBubbleShowingFadeIn or kBubbleHidingFadeOut, or from the appropriate
  // fully-shown/hidden state, kBubbleShown or kBubbleHidden.  This may be
  // called at any point during a fade-in or fade-out; it is even possible to
  // reverse a transition before it has completed.
  void Fade(bool show);

  // One-shot timer operations to manage the delays associated with the
  // kBubbleShowingTimer and kBubbleHidingTimer states.  StartTimer and
  // TimerFired must be called from one of these states.  StartTimer may be
  // called while the timer is still running; in that case, the timer will be
  // reset. CancelTimer may be called from any state.
  void StartTimer(int64 time_ms);
  void CancelTimer();
  void TimerFired();

  // Begin the process of showing or hiding the status bubble.  These may be
  // called from any state, and will take the appropriate action to initiate
  // any state changes that may be needed.
  void StartShowing();
  void StartHiding();

  // Cancel the expansion timer.
  void CancelExpandTimer();

  // The timer factory used for show and hide delay timers.
  ScopedRunnableMethodFactory<StatusBubbleMac> timer_factory_;

  // The timer factory used for the expansion delay timer.
  ScopedRunnableMethodFactory<StatusBubbleMac> expand_timer_factory_;

  // Calculate the appropriate frame for the status bubble window. If
  // |expanded_width|, use entire width of parent frame.
  NSRect CalculateWindowFrame(bool expanded_width);

  // The window we attach ourselves to.
  NSWindow* parent_;  // WEAK

  // The object that we query about our vertical offset for positioning.
  id delegate_;  // WEAK

  // The window we own.
  NSWindow* window_;

  // The status text we want to display when there are no URLs to display.
  NSString* status_text_;

  // The url we want to display when there is no status text to display.
  NSString* url_text_;

  // The status bubble's current state.  Do not write to this field directly;
  // use SetState().
  StatusBubbleState state_;

  // True if operations are to be performed immediately rather than waiting
  // for delays and transitions.  Normally false, this should only be set to
  // true for testing.
  bool immediate_;

  // True if the status bubble has been expanded. If the bubble is in the
  // expanded state and encounters a new URL, change size immediately,
  // with no hover delay.
  bool is_expanded_;

  // The original, non-elided URL.
  GURL url_;

  // Needs to be passed to ElideURL if the original URL string is wider than
  // the standard bubble width.
  string16 languages_;

  DISALLOW_COPY_AND_ASSIGN(StatusBubbleMac);
};

// Delegate interface
@interface NSObject(StatusBubbleDelegate)
// Called to query the delegate about the frame StatusBubble should position
// itself in. Frame is returned in the parent window coordinates.
- (NSRect)statusBubbleBaseFrame;

// Called from SetState to notify the delegate of state changes.
- (void)statusBubbleWillEnterState:(StatusBubbleMac::StatusBubbleState)state;
@end

#endif  // CHROME_BROWSER_UI_COCOA_STATUS_BUBBLE_MAC_H_