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

#pragma once

#include "qsnd/alsa.hpp"
#include <QObject>
#include <array>
#include <memory>
#include <vector>

// Forward declaration
namespace QSnd::HCtl
{
class Elem;
class Elem_Group;
class Info_Db;
} // namespace QSnd::HCtl
class QSocketNotifier;

namespace QSnd::HCtl
{

/// @brief HCtl Mixer
///
/// Qt binding for ALSA's hctl interface
class Mixer : public QObject
{
  Q_OBJECT

  public:
  // -- Construction

  Mixer ( QObject * parent_n = nullptr );

  ~Mixer ();

  // -- Accessors

  const Info_Db *
  info_db () const
  {
    return _info_db.get ();
  }

  snd_hctl_t *
  snd_hctl ()
  {
    return _snd_hctl;
  }

  // -- Init / close

  /// @return true on success, false on error
  bool
  open ( const QString & dev_name_n );

  void
  close ();

  bool
  is_open () const
  {
    return ( _snd_hctl != nullptr );
  }

  // -- Error strings / codes

  const QString &
  err_func () const
  {
    return _err_func;
  }

  const QString &
  err_message () const
  {
    return _err_message;
  }

  // -- Elements

  std::size_t
  num_elems () const
  {
    return _mixer_elems.size ();
  }

  const Elem *
  elem ( std::size_t idx_n ) const
  {
    return _mixer_elems[ idx_n ].get ();
  }

  Elem *
  elem ( std::size_t idx_n )
  {
    return _mixer_elems[ idx_n ].get ();
  }

  // -- Interface types

  unsigned int
  iface_type_count ( std::size_t type_idx_n ) const
  {
    return _iface_type_count[ type_idx_n ];
  }

  unsigned int
  iface_types_avail () const
  {
    return _iface_num_types;
  }

  unsigned int
  iface_avail_type ( std::size_t type_idx_n ) const
  {
    return _iface_avail_types[ type_idx_n ];
  }

  // -- Mixer reload

  Q_SIGNAL
  void
  sig_mixer_reload_request ();

  protected:
  // -- Event processing

  bool
  event ( QEvent * event_n ) override;

  private:
  // -- Utility
  friend class QSnd::HCtl::Elem;

  void
  create_mixer_elems ();

  bool
  create_socket_notifiers ();

  void
  set_socked_notifiers_enabled ( bool flag_n );

  void
  request_reload_later ();

  void
  signalize_all_changes_later ();

  void
  signalize_all_changes ();

  Q_SLOT
  void
  socket_event ( int socket_id );

  // -- Alsa callback

  static int
  alsa_callback_hctl ( snd_hctl_t * snd_hctl_n,
                       unsigned int mask_n,
                       snd_hctl_elem_t * elem_n );

  private:
  // -- Attributes
  std::unique_ptr< QSnd::HCtl::Info_Db > _info_db;
  snd_hctl_t * _snd_hctl = nullptr;

  std::vector< std::unique_ptr< Elem > > _mixer_elems;
  std::vector< pollfd > _pollfds;
  std::vector< std::unique_ptr< QSocketNotifier > > _socket_notifiers;

  unsigned int _iface_num_types = 0;
  std::array< unsigned int, 7 > _iface_avail_types;
  std::array< unsigned int, 7 > _iface_type_count;

  bool _signalize_changes_requested = false;
  bool _reload_requested = false;

  QString _err_func;
  QString _err_message;
};

} // namespace QSnd::HCtl
