/* 
 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
   */

/**
 * This file contains a collection of small graphical elements used to display heartbeats
 * values over time and other visual sugar.
 * In general these widgets are all thread-safe and can be manipulated from any thread
 * (particularly useful for animations and other timed updates).
 */
#ifndef _WIDGETS_H_
#define _WIDGETS_H_

#include <list>

#include "glib.h"
#include "cairo/cairo.h"

#include "mforms/base.h"
#include "mforms/drawbox.h" 
#include "mforms/box.h"
#include "mforms/label.h"
#include "mforms/table.h"
#include "base/threaded_timer.h"
#include "base/geometry.h"

#ifndef DOXYGEN_SHOULD_SKIP_THIS
namespace mforms {

  typedef std::list<double> ThresholdList;
  
  class MFORMS_EXPORT BaseWidget : public DrawBox
  {
  public:
    BaseWidget();
    ~BaseWidget();

    void enable_auto_scale(bool enable);
    double get_upper_range() const;
    void set_value_range(double low, double high);
    void set_thresholds(ThresholdList lower_thresholds, ThresholdList upper_thresholds);
    void set_description(const std::string& description);
#ifndef SWIG
    virtual void repaint(cairo_t *cr, int areax, int areay, int areaw, int areah);
    virtual void get_layout_size(int* w, int* h);
    
    virtual void step() {};
#endif
  protected:
    cairo_surface_t* _background;
    bool _auto_scale;
    int _layout_width;
    int _layout_height;

    void lock();
    void unlock();
    double normalize(double input);
    virtual void prepare_background() {};
    virtual void destroy_background();
    MySQL::Geometry::Rect get_diagram_area();
    virtual void range_updated(double scale, double offset) {};
    virtual bool layout(cairo_t *cr);
    void auto_scale(double value);
    bool compute_scale(double min, double max);
    virtual void get_minmax_values(double* min, double* max);
  private:
    GStaticMutex _lock;
    double _lower_limit;
    double _upper_limit;
    ThresholdList _lower_thresholds;
    ThresholdList _upper_thresholds;
    std::string _description;
    double _description_height;
    
    cairo_t* _layout_context;
    cairo_surface_t* _layout_surface;
    void create_context_for_layout();
  };
  
  class MFORMS_EXPORT WidgetSeparator : public DrawBox
  {
  public:
    virtual void repaint(cairo_t *cr, int areax, int areay, int areaw, int areah);
  };
  
  /**
   * A widget box holds any number of widgets together with a title and descriptions for each of them.
   */
  class MFORMS_EXPORT WidgetContainer : public Box
  {
  public:
    WidgetContainer(const std::string& title);
    ~WidgetContainer();

    void add_widget(BaseWidget* widget, bool expand);
    void set_title(const std::string& title);
  private:
    Label _title;
    Box _content;
  };

  // Number of values to use for the heartbeat.
  #define HEARTBEAT_DATA_SIZE 80
  
  class MFORMS_EXPORT HeartbeatWidget : public BaseWidget
  {
  public:
    HeartbeatWidget();
    ~HeartbeatWidget();
#ifndef SWIG
    virtual void repaint(cairo_t *cr, int areax, int areay, int areaw, int areah);
    virtual void step();
#endif
    void set_value(double value);
  protected:
    virtual void prepare_background();
    virtual void range_updated(double scale, double offset);
    virtual void get_minmax_values(double* min, double* max);
  private:
    int _pivot;
    double _luminance[HEARTBEAT_DATA_SIZE];
    double _deflection[HEARTBEAT_DATA_SIZE];
  };
  
  /**
   * The ServerInfoWidget shows some server informations (name, IP address etc.) as well as
   * the server's running state.
   */
  class MFORMS_EXPORT ServerInfoWidget : public BaseWidget
  {
  public:
    ServerInfoWidget();
    ~ServerInfoWidget();
    
#ifndef SWIG
    virtual void repaint(cairo_t *cr, int areax, int areay, int areaw, int areah);
#endif
    void set_server_info(const std::string& instance, const std::string& ip_address, const std::string& server);
    void set_server_status(int status);
  protected:
    virtual bool layout(cairo_t *cr);
  private:
    int _status; // -1 for unknown, 0 for not-running, 1 for running.

    cairo_surface_t* _image_unknown;
    cairo_surface_t* _image_running;
    cairo_surface_t* _image_stopped;
    std::string _instance;
    std::string _ip_address;
    std::string _server;

    double _content_top;
    double _var_text_left;   // The left position of the variable text part (which is left aligned).
    double _instance_left;   // Left position of the "Name" string (which is right aligned, like the 
                             // other fixed text parts).
    double _ip_left;
    double _server_left;
    double _status_left;
    double _text_y_advance;  // The distance from one text line to the next.
  };
  
  /**
   * The bar graph widget is a widget which shows a single value similar to a gauge reading.
   */
  class MFORMS_EXPORT BarGraphWidget : public BaseWidget
  {
  public:
    BarGraphWidget();
    ~BarGraphWidget();
    
#ifndef SWIG
    virtual void repaint(cairo_t *cr, int areax, int areay, int areaw, int areah);
#endif
    void set_value(double value);
  protected:
    virtual void prepare_background();
    virtual void destroy_background();
    void create_value_gradient();
    virtual void range_updated(double scale, double offset);
    virtual void get_minmax_values(double* min, double* max);
  private:
    double _value;
    cairo_pattern_t* _value_gradient;
    cairo_surface_t* _grid;
  };

  /**
   * The line diagram widget shows a series of values in the order they came in, scrolling
   * the display area slowly to fade out the oldest values making so room for new entries.
   */
  #define LINE_SERIES_DATA_SIZE 500
  
  class MFORMS_EXPORT LineDiagramWidget : public BaseWidget
  {
  public:
    LineDiagramWidget();
    ~LineDiagramWidget();
    
#ifndef SWIG
    virtual void repaint(cairo_t *cr, int areax, int areay, int areaw, int areah);
    virtual void step();
#endif
    void set_value(double value);
  protected:
    virtual void prepare_background();
    virtual void destroy_background();
    virtual void range_updated(double scale, double offset);
    virtual void get_minmax_values(double* min, double* max);
  private:
    double _next_value;
    double _deflection[LINE_SERIES_DATA_SIZE];
    double _timestamp[LINE_SERIES_DATA_SIZE];
    int _time_in_view;                           // Seconds to show in the widget. 
    cairo_pattern_t* _value_gradient;
    GTimer* _clock;                              // Used to generate timestamps for incoming values.
    cairo_surface_t* _grid;
    double _content_alpha;                       // Used to blend out the content for special feedback.
    double _warning_alpha;                       // Alpha for warning feedback.
    double _last_shift;                          // The animation timer is faster than our shift needs to be.
                                                 // Hence determine the distance since the last shift before we do the next.
    double _sleep_start;                         // 0 if not sleeping, otherwise the timestamp of when
                                                 // sleeping started.
    enum {Awake, GoSleeping, Sleeping, Awaking} _sleep_mode;
    
    cairo_text_extents_t _warning_extents;
    
    void show_feedback(cairo_t* cr, const MySQL::Geometry::Rect& bounds);
    void begin_sleeping(double timestamp);
    void end_sleeping(double timestamp);
    bool feedback_step();
  };
  
}

#endif // !DOXYGEN_SHOULD_SKIP_THIS

#endif // _WIDGETS_H_
