/// QasTools: Desktop toolset for the Linux sound system ALSA.
/// \copyright See COPYING file.

#pragma once

#include "dpe/image_set_meta.hpp"
#include "wdg/cubic_curve.hpp"
#include "wdg/ds/imaging.hpp"
#include "wdg/ds/slider_meta_bg.hpp"
#include "wdg/uint_mapper.hpp"
#include <QBrush>
#include <QElapsedTimer>
#include <QPen>
#include <QTimer>
#include <QWidget>
#include <memory>

// Forward declaration
namespace dpe
{
class Image_Allocator;
} // namespace dpe
namespace Wdg
{
class Style_Db;
}

namespace Wdg::DS
{

/// @brief Dynamic sized slider
///
/// Background images:
/// 0 - non_active area
/// 1 - active area
///
/// Frame images:
/// 0 - focus
/// 1 - weak focus
///
/// Handle images:
/// 0 - idle
/// 1 - focus
/// 2 - idle + hover
/// 3 - focus + hover
/// 4 - isDown()
///
/// Marker images:
/// 0 - Current value marker
/// 1 - Value hint marker
///
class Slider : public QWidget
{
  Q_OBJECT

  public:
  // -- Construction

  Slider ( const Wdg::Style_Db * style_db_n,
           dpe::Image_Allocator * image_alloc_n,
           QWidget * parent_n = nullptr );

  ~Slider ();

  // -- Size hints

  QSize
  sizeHint () const override;

  QSize
  minimumSizeHint () const override;

  // -- Value index

  unsigned long
  current_index () const
  {
    return _current_index;
  }

  unsigned long
  maximum_index () const
  {
    return _maximum_index;
  }

  bool
  slider_down () const
  {
    return _slider_down;
  }

  // -- Image allocator

  dpe::Image_Allocator *
  image_alloc () const
  {
    return _dsi.image_alloc ();
  }

  // -- Style database

  const Wdg::Style_Db *
  style_db () const
  {
    return _style_db;
  }

  // -- Style id

  unsigned int
  style_id () const
  {
    return _meta_bg.style_id;
  }

  void
  set_style_id ( unsigned int id_n );

  // -- Additional config

  Wdg::DS::Slider_Meta_Bg &
  meta_bg ()
  {
    return _meta_bg;
  }

  // -- Mouse wheel degrees

  void
  set_wheel_degrees ( unsigned int degrees_n );

  unsigned int
  wheel_degrees () const
  {
    return _wheel_degrees;
  }

  // -- Public slots

  Q_SLOT
  void
  set_current_index ( unsigned long idx_n );

  Q_SLOT
  void
  adjust_current_index ( long idx_delta_n );

  Q_SLOT
  void
  set_maximum_index ( unsigned long idx_n );

  // -- Signals

  Q_SIGNAL
  void
  sig_current_index_changed ( unsigned long idx_n );

  Q_SIGNAL
  void
  sig_maximum_index_changed ( unsigned long idx_n );

  protected:
  // -- Protected methods

  void
  set_slider_down ( bool down_n );

  virtual void
  current_index_changed ();

  virtual void
  maximum_index_changed ();

  // Mapping: Pixel <=> Value

  unsigned long
  map_handle_pos_to_index ( int handle_pos_n );

  unsigned int
  map_index_to_handle_pos ( unsigned long idx_n );

  unsigned int
  map_tick_pos_to_handle_pos ( int tick_pos_n );

  unsigned int
  map_handle_pos_to_tick_pos ( int handle_pos_n );

  unsigned long
  map_tick_pos_to_index ( int tick_pos_n );

  unsigned int
  map_index_to_tick_pos ( unsigned long idx_n );

  void
  update_index_mappers ();

  // Cursor mode

  bool
  update_cursor_mode ();

  // Handle position

  /// @return true if the handle position was changed
  bool
  set_handle_pos ( unsigned int pos_n );

  void
  update_value_from_handle_pos ();

  void
  finish_handle_manipulation ();

  // Change events

  void
  changeEvent ( QEvent * event_n ) override;

  // Focus events

  void
  focusInEvent ( QFocusEvent * event_n ) override;

  void
  focusOutEvent ( QFocusEvent * event_n ) override;

  // Enter events

  void
  enterEvent ( QEnterEvent * event_n ) override;

  void
  leaveEvent ( QEvent * event_n ) override;

  // Mouse events

  void
  mousePressEvent ( QMouseEvent * event_n ) override;

  void
  mouseReleaseEvent ( QMouseEvent * event_n ) override;

  void
  mouseMoveEvent ( QMouseEvent * event_n ) override;

  void
  wheelEvent ( QWheelEvent * event_n ) override;

  // Keyboard events

  void
  keyPressEvent ( QKeyEvent * event_n ) override;

  void
  keyReleaseEvent ( QKeyEvent * event_n ) override;

  // Window events

  void
  resizeEvent ( QResizeEvent * event_n ) override;

  void
  paintEvent ( QPaintEvent * event_n ) override;

  /// @brief Fetches new pixmaps from the image buffers
  void
  update_pixmaps ();

  void
  update_palette_and_colors ();

  private:
  // -- Private slots

  Q_SLOT
  void
  style_palette_changed ( unsigned int style_id_n );

  Q_SLOT
  void
  anim_tick ();

  private:
  // -- Private methods

  void
  anim_stop ();

  void
  anim_snap_start ();

  bool
  anim_snap_tick ( unsigned int msec_n );

  private:
  // -- Attributes
  static const unsigned int num_images_bg = 2;
  static const unsigned int num_images_marker = 2;
  static const unsigned int num_images_frame = 2;
  static const unsigned int num_images_handle = 5;

  Wdg::DS::Imaging _dsi;

  unsigned long _current_index;
  unsigned long _maximum_index;

  unsigned int _wheel_degrees;

  long _step_single;
  long _step_page;

  QRect _handle_rect;
  QPen _snap_pen;
  QBrush _snap_brush;

  unsigned int _handle_pos;
  unsigned int _handle_pos_index;
  unsigned int _handle_pos_hint;
  unsigned int _handle_pos_max;
  int _marker_offset[ 2 ];
  int _tick_min;
  int _tick_max;
  QPoint _mouse_last;

  std::unique_ptr< UInt_Mapper > _map_hp_idx;
  std::unique_ptr< UInt_Mapper > _map_idx_hp;

  bool _slider_down;
  bool _update_pixmaps_pending;
  bool _cursor_select_allowed;
  bool _cursor_over_handle;
  bool _cursor_value_hinting;
  bool _anim_run_snap;

  // Animation
  QTimer _anim_timer;

  QElapsedTimer _anim_snap_time;
  unsigned int _anim_snap_msec_max;
  Wdg::Cubic_Curve _cubic_curve;

  // Graphics creation
  QSize _marker_size;

  // Pixmap buffers
  Wdg::DS::Slider_Meta_Bg _meta_bg;
  dpe::Image_Set_Meta _meta_marker;
  dpe::Image_Set_Meta _meta_frame;
  dpe::Image_Set_Meta _meta_handle;
  const Wdg::Style_Db * _style_db = nullptr;
};

} // namespace Wdg::DS
