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

#include "mixer.hpp"
#include "mstate/mixer/state.hpp"
#include "mstate/mixer/states_db.hpp"
#include "mwdg/inputs_setup.hpp"
#include "mwdg/mixer/sliders/mixer.hpp"
#include "mwdg/mixer/sliders/status_widget.hpp"
#include "mwdg/mixer/switches/mixer.hpp"
#include "mwdg/mixer_device_setup.hpp"
#include "qsnd/mixer/mixer.hpp"
#include "views/message_widget.hpp"
#include "views/mixer_setup.hpp"
#include <QKeyEvent>
#include <QVBoxLayout>
#include <iostream>

namespace Views
{

Mixer::Mixer ( Wdg::Style_Db * wdg_style_db_n,
               dpe::Image_Allocator * image_alloc_n,
               MState::Mixer::States_Db * mixer_states_db_n,
               QWidget * parent_n )
: Views::View_Base ( parent_n )
, _mixer_states_db ( mixer_states_db_n )
, _sliders_model ( this )
, _switches_model ( this )
{
  // Models
  _sliders_model.set_mixer_setup ( &_mixer_setup );
  _switches_model.set_mixer_setup ( &_mixer_setup );

  // Mixer object
  {
    _qsnd_mixer.reset ( new QSnd::Mixer::Mixer ( this ) );
    connect ( _qsnd_mixer.get (),
              &QSnd::Mixer::Mixer::sig_mixer_reload_request,
              this,
              &Mixer::sig_mixer_dev_reload_request );
  }

  // Mixer sliders
  {
    _mixer_sliders.reset (
        new MWdg::Mixer::Sliders::Mixer ( wdg_style_db_n, image_alloc_n ) );
    _mixer_sliders->set_mixer_setup ( &_mixer_setup );
    _mixer_sliders->set_proxies_model ( &_sliders_model );

    connect ( _mixer_sliders.get (),
              &MWdg::Mixer::Sliders::Mixer::sig_footer_label_selected,
              this,
              &Mixer::footer_label_selected );

    // Adjust layout margins
    if ( _mixer_sliders->layout () != nullptr ) {
      auto & margs = _splitter_margins.sliders;
      margs = _mixer_sliders->layout ()->contentsMargins ();
      // Clear all but bottom
      margs.setTop ( 0 );
      margs.setLeft ( 0 );
      margs.setRight ( 0 );
      _mixer_sliders->layout ()->setContentsMargins ( margs );
    }

    _mixer_sliders->hide ();
  }

  // Mixer switches
  {
    _mixer_switches.reset (
        new MWdg::Mixer::Switches::Mixer ( wdg_style_db_n, image_alloc_n ) );
    _mixer_switches->set_mixer_setup ( &_mixer_setup );
    _mixer_switches->set_proxies_model ( &_switches_model );

    // Adjust layout margins
    if ( _mixer_switches->layout () != nullptr ) {
      auto & margs = _splitter_margins.switches;
      margs = _mixer_switches->layout ()->contentsMargins ();
      // Clear all but top
      margs.setBottom ( 0 );
      margs.setLeft ( 0 );
      margs.setRight ( 0 );
      _mixer_switches->layout ()->setContentsMargins ( margs );
    }

    _mixer_switches->hide ();
  }

  // Vertical splitter
  {
    _splitter.reset ( new QSplitter );
    _splitter->hide ();
    _splitter->setOrientation ( Qt::Vertical );
    _splitter->addWidget ( _mixer_sliders.get () );
    _splitter->addWidget ( _mixer_switches.get () );
    _splitter->setCollapsible ( 0, false );
    _splitter->setCollapsible ( 1, false );
    _splitter->setStretchFactor ( 0, 1 );
    _splitter->setStretchFactor ( 1, 0 );
  }

  // Central widget
  lay_stack ()->addWidget ( _splitter.get () );
}

Mixer::~Mixer ()
{
  set_mixer_dev_setup ( nullptr );
  set_view_setup ( nullptr );
}

void
Mixer::set_mixer_dev_setup ( const MWdg::Mixer_Device_Setup * setup_n )
{
  // Clear the view
  clear_view ();

  if ( mixer_dev_setup () != nullptr ) {
    set_streams_available ( false );
    // Reset the models
    _sliders_model.clear ();
    _switches_model.clear ();
    // Reset the state and the mixer
    _mixer_state.reset ();
    _qsnd_mixer->close ();
    // Update stream availability
    update_stream_availability ();
  }

  Views::View_Base::set_mixer_dev_setup ( setup_n );

  if ( mixer_dev_setup () != nullptr ) {
    const auto & ctl_addr = mixer_dev_setup ()->control_address ();
    if ( ctl_addr.is_valid () ) {
      // Open the mixer
      if ( _qsnd_mixer->open ( ctl_addr.addr_str () ) ) {
        // Acquire the mixer state
        _mixer_state = _mixer_states_db->state ( ctl_addr );
        // Setup proxies models
        _sliders_model.setup ( _qsnd_mixer.get (), _mixer_state );
        _switches_model.setup ( _qsnd_mixer.get (), _mixer_state );
        _sliders_model.reload_proxies_sorted ();
        _switches_model.reload_proxies_sorted ();
        _sliders_model.reload_proxies_filtered ();
        _switches_model.reload_proxies_filtered ();
        // Update stream availability
        update_stream_availability ();
      }
    }
  }

  // Setup the view
  setup_view ();
}

void
Mixer::set_inputs_setup ( const MWdg::Inputs_Setup * setup_n )
{
  Views::View_Base::set_inputs_setup ( setup_n );
  _mixer_sliders->set_inputs_setup ( setup_n );
  _mixer_switches->set_inputs_setup ( setup_n );
}

void
Mixer::set_view_setup ( Views::View_Base_Setup * setup_n )
{
  _view_setup = dynamic_cast< Views::Mixer_Setup * > ( setup_n );
  if ( _view_setup != nullptr ) {
    _mixer_setup.show_slider_value_labels =
        _view_setup->show_slider_value_labels ();
    _mixer_setup.show_tool_tips = _view_setup->show_tool_tips ();

    _mixer_sliders->set_mixer_setup ( &_mixer_setup );
    _sliders_model.set_mixer_setup ( &_mixer_setup );

    _mixer_switches->set_mixer_setup ( &_mixer_setup );
    _switches_model.set_mixer_setup ( &_mixer_setup );
  }
}

void
Mixer::save_state ()
{
  // Store splitter state
  if ( _mixer_state && _visibility.splitter ) {
    _mixer_state->set_splitter_sizes ( _splitter->sizes () );
  }
}

void
Mixer::reload_proxies_models_sorted ()
{
  clear_view ();

  // Update sorted and filtered proxies lists
  _sliders_model.reload_proxies_sorted ();
  _switches_model.reload_proxies_sorted ();
  _sliders_model.reload_proxies_filtered ();
  _switches_model.reload_proxies_filtered ();
  // Update stream availability
  update_stream_availability ();

  setup_view ();
}

void
Mixer::reload_proxies_models_filtered ()
{
  clear_view ();

  // Update filtered proxies lists
  _sliders_model.reload_proxies_filtered ();
  _switches_model.reload_proxies_filtered ();
  // Update stream availability
  update_stream_availability ();

  setup_view ();
}

void
Mixer::clear_view ()
{
  // Save state
  save_state ();

  // Destroy status widget
  if ( _status_wdg != nullptr ) {
    delete _status_wdg;
    _status_wdg = nullptr;
  }

  // Hide widgets
  _splitter->hide ();
  _mixer_sliders->hide ();
  _mixer_switches->hide ();

  // Clear visibility
  _visibility.sliders = false;
  _visibility.switches = false;
  _visibility.splitter = false;
}

void
Mixer::setup_view ()
{
  std::size_t lay_stack_idx = 0;
  if ( _qsnd_mixer->is_open () ) {
    // Mixer is open.  Show the mixer widgets.
    lay_stack_idx = 1;
  } else {
    // Mixer is not open.  Show a message.
    if ( ( mixer_dev_setup () != nullptr ) &&
         mixer_dev_setup ()->control_address ().is_valid () ) {
      // Address is valid but mixer is not open. Show error.
      message_wdg ()->set_mixer_open_fail (
          mixer_dev_setup ()->control_address ().addr_str (),
          _qsnd_mixer->err_message (),
          _qsnd_mixer->err_func () );
    } else {
      // Emty mixer address
      message_wdg ()->set_no_device ();
    }
  }

  if ( lay_stack_idx == 1 ) {
    // Setup proxies models
    _sliders_model.reload_proxies_visible ();
    _switches_model.reload_proxies_visible ();

    // Update visiblity
    _visibility.sliders = !_sliders_model.proxies_visible ().empty ();
    _visibility.switches = !_switches_model.proxies_visible ().empty ();
    _visibility.splitter = ( _visibility.sliders && _visibility.switches );

    // Setup widgets
    if ( _visibility.splitter ) {
      // Set the in-splitter contents margins
      _mixer_sliders->layout ()->setContentsMargins (
          _splitter_margins.sliders );
      _mixer_switches->layout ()->setContentsMargins (
          _splitter_margins.switches );

      // Show widgets
      _mixer_sliders->show ();
      _mixer_switches->show ();
      if ( _mixer_state ) {
        _splitter->setSizes ( _mixer_state->splitter_sizes () );
      }
      _splitter->show ();
    } else {
      // Show sliders or switches
      QWidget * widget = nullptr;
      if ( _visibility.sliders ) {
        widget = _mixer_sliders.get ();
      } else if ( _visibility.switches ) {
        widget = _mixer_switches.get ();
      }
      if ( widget != nullptr ) {
        // Set single widget contents margins
        widget->layout ()->setContentsMargins ( 0, 0, 0, 0 );
        // Show widgets
        widget->show ();
        _splitter->show ();
      }
    }
  }

  lay_stack ()->setCurrentIndex ( lay_stack_idx );
}

void
Mixer::update_stream_availability ()
{
  // Sanitize show streams
  bool show_stream[ 2 ] = { false, false };
  if ( _mixer_state ) {
    show_stream[ 0 ] = _mixer_state->show_stream ( 0 );
    show_stream[ 1 ] = _mixer_state->show_stream ( 1 );

    // Acquire playback and capture controls count
    std::size_t num_elems[ 2 ] = { 0, 0 };
    for ( std::size_t ii = 0; ii < 2; ++ii ) {
      num_elems[ ii ] += _sliders_model.proxies_filtered_stats_stream ( ii );
      num_elems[ ii ] += _switches_model.proxies_filtered_stats_stream ( ii );
    }
    // Check if there are no playback or no capture controls
    if ( ( num_elems[ 0 ] == 0 ) || ( num_elems[ 1 ] == 0 ) ) {
      show_stream[ 0 ] = ( num_elems[ 0 ] > 0 );
      show_stream[ 1 ] = ( num_elems[ 1 ] > 0 );
    } else {
      // Both playback and capture counts are non zero
      if ( !show_stream[ 0 ] && !show_stream[ 1 ] ) {
        // Select playback
        show_stream[ 0 ] = true;
      }
    }

    // Apply changes
    _mixer_state->set_show_stream ( 0, show_stream[ 0 ] );
    _mixer_state->set_show_stream ( 1, show_stream[ 1 ] );

    set_streams_available ( ( num_elems[ 0 ] > 0 ) && ( num_elems[ 1 ] > 0 ) );
  } else {
    set_streams_available ( false );
  }

  // Send change signals
  Q_EMIT sig_show_playback_changed ( show_stream[ 0 ] );
  Q_EMIT sig_show_capture_changed ( show_stream[ 1 ] );
}

void
Mixer::set_streams_available ( bool flag_n )
{
  if ( _streams_available != flag_n ) {
    _streams_available = flag_n;
    Q_EMIT sig_streams_available ( flag_n );
  }
}

void
Mixer::show_playback ( bool flag_n )
{
  if ( !streams_available () ) {
    return;
  }
  if ( !_mixer_state ) {
    return;
  }
  if ( _mixer_state->show_stream ( 0 ) != flag_n ) {
    clear_view ();

    _mixer_state->set_show_stream ( 0, flag_n );
    Q_EMIT sig_show_playback_changed ( _mixer_state->show_stream ( 0 ) );

    // Toggle to show capture on demand
    if ( !_mixer_state->show_stream ( 0 ) &&
         !_mixer_state->show_stream ( 1 ) ) {
      _mixer_state->set_show_stream ( 1, true );
      Q_EMIT sig_show_capture_changed ( _mixer_state->show_stream ( 1 ) );
    }

    setup_view ();
  }
}

void
Mixer::show_capture ( bool flag_n )
{
  if ( !streams_available () ) {
    return;
  }
  if ( !_mixer_state ) {
    return;
  }
  if ( _mixer_state->show_stream ( 1 ) != flag_n ) {
    clear_view ();

    _mixer_state->set_show_stream ( 1, flag_n );
    Q_EMIT sig_show_capture_changed ( _mixer_state->show_stream ( 1 ) );

    // Toggle to show plyback on demand
    if ( !_mixer_state->show_stream ( 0 ) &&
         !_mixer_state->show_stream ( 1 ) ) {
      _mixer_state->set_show_stream ( 0, true );
      Q_EMIT sig_show_playback_changed ( _mixer_state->show_stream ( 0 ) );
    }

    setup_view ();
  }
}

void
Mixer::toggle_show_playback ()
{
  if ( !streams_available () ) {
    return;
  }
  if ( _mixer_state ) {
    show_playback ( !_mixer_state->show_stream ( 0 ) );
  }
}

void
Mixer::toggle_show_capture ()
{
  if ( !streams_available () ) {
    return;
  }
  if ( _mixer_state ) {
    show_capture ( !_mixer_state->show_stream ( 1 ) );
  }
}

void
Mixer::show_slider_value_widget ()
{
  if ( _status_wdg == nullptr ) {
    MWdg::Mixer::Sliders::Status_Widget * swdg (
        new MWdg::Mixer::Sliders::Status_Widget ( this ) );
    swdg->setAttribute ( Qt::WA_DeleteOnClose );
    swdg->set_sliders_pad ( _mixer_sliders->sliders_pad () );
    swdg->slider_focus_changed ();

    _status_wdg = swdg;
    _status_wdg->show ();
  }
}

void
Mixer::footer_label_selected ( unsigned int group_idx_n,
                               unsigned int column_idx_n )
{
  // std::cout << "Footer label selected " << group_idx_n << " " <<
  //  column_idx_n << "\n";
  bool keep_indices = true;
  if ( _status_wdg == nullptr ) {
    show_slider_value_widget ();
  } else {
    if ( ( group_idx_n == _status_group_idx ) &&
         ( column_idx_n == _status_column_idx ) ) {
      _status_group_idx = ~0;
      _status_column_idx = ~0;
      _status_wdg->close ();
      keep_indices = false;
    }
  }

  if ( keep_indices ) {
    _status_group_idx = group_idx_n;
    _status_column_idx = column_idx_n;
  }
}

void
Mixer::keyPressEvent ( QKeyEvent * event_n )
{
  bool handled = false;
  if ( inputs_setup () != nullptr ) {
    handled = true;
    const QKeySequence kseq = event_n->key ();
    if ( kseq == inputs_setup ()->ks_toggle_vis_stream[ 0 ] ) {
      toggle_show_playback ();
    } else if ( kseq == inputs_setup ()->ks_toggle_vis_stream[ 1 ] ) {
      toggle_show_capture ();
    } else {
      handled = false;
    }
  }
  if ( !handled ) {
    Views::View_Base::keyPressEvent ( event_n );
  }
}

void
Mixer::showEvent ( QShowEvent * event_n )
{
  View_Base::showEvent ( event_n );
  if ( _status_wdg != nullptr ) {
    _status_wdg->show ();
  }
}

void
Mixer::hideEvent ( QHideEvent * event_n )
{
  View_Base::hideEvent ( event_n );
  if ( _status_wdg != nullptr ) {
    _status_wdg->hide ();
  }
}

} // namespace Views
