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

#include "mixer_hctl.hpp"
#include "mwdg/hctl/mixer.hpp"
#include "mwdg/hctl/table_model.hpp"
#include "mwdg/hctl/tree_model.hpp"
#include "mwdg/mixer_device_setup.hpp"
#include "mwdg/mixer_styles.hpp"
#include "qsnd/hctl/elem.hpp"
#include "qsnd/hctl/elem_group.hpp"
#include "qsnd/hctl/info_db.hpp"
#include "qsnd/hctl/mixer.hpp"
#include "views/message_widget.hpp"
#include "views/mixer_hctl_setup.hpp"
#include "wdg/tree_view_kv.hpp"
#include <QCoreApplication>
#include <QHeaderView>
#include <iostream>

namespace Views
{

Mixer_HCTL::Mixer_HCTL ( Wdg::Style_Db * wdg_style_db_n,
                         dpe::Image_Allocator * image_alloc_n,
                         QWidget * parent_n )
: Views::View_Base ( parent_n )
, _wdg_style_db ( wdg_style_db_n )
{
  // Snd hctl mixer
  _snd_mixer = new QSnd::HCtl::Mixer ( this );
  connect ( _snd_mixer,
            &QSnd::HCtl::Mixer::sig_mixer_reload_request,
            this,
            &Mixer_HCTL::sig_mixer_dev_reload_request );

  _default_iface_type_idx =
      _snd_mixer->info_db ()->iface_type_idx ( SND_CTL_ELEM_IFACE_MIXER );

  // Mixer widget
  _mixer_hctl = new MWdg::HCtl::Mixer ( wdg_style_db_n, image_alloc_n );
  _mixer_hctl->set_ctl_info_db ( _snd_mixer->info_db () );

  // Tree model
  _tree_model = new MWdg::HCtl::Tree_Model ( this );
  update_tree_model_colors ();

  connect ( _wdg_style_db,
            &Wdg::Style_Db::sig_palette_changed,
            this,
            &Mixer_HCTL::update_tree_model_colors );

  // Tree view
  _tree_view = new Wdg::Tree_View_KV;
  _tree_view->setHeaderHidden ( true );
  _tree_view->setTextElideMode ( Qt::ElideMiddle );
  _tree_view->setModel ( _tree_model );

  connect ( _tree_view,
            SIGNAL ( activated ( const QModelIndex & ) ),
            this,
            SLOT ( tree_element_selected ( const QModelIndex & ) ) );

  // Tree view container widget
  {
    QVBoxLayout * lay_wdg_tree = new QVBoxLayout;
    lay_wdg_tree->addWidget ( _tree_view );
    _wdg_side.setLayout ( lay_wdg_tree );
  }

  // Table model
  _table_model = new MWdg::HCtl::Table_Model ( this );

  // Table view
  {
    _table_view = new QTableView;
    _table_view->setModel ( _table_model );
    {
      QHeaderView * vhv = _table_view->verticalHeader ();
      if ( vhv != nullptr ) {
        vhv->hide ();
      }
    }
    _table_view->setSelectionMode ( QAbstractItemView::SingleSelection );
    _table_view->setSelectionBehavior ( QAbstractItemView::SelectRows );

    connect ( _table_view,
              SIGNAL ( activated ( const QModelIndex & ) ),
              this,
              SLOT ( table_element_selected ( const QModelIndex & ) ) );
  }

  // Central widget with stacked layout
  {
    QVBoxLayout * lay_center ( new QVBoxLayout );
    _wdg_center.setLayout ( lay_center );

    _lay_center_stack = new QStackedLayout;
    lay_center->addLayout ( _lay_center_stack );

    _lay_center_stack->setContentsMargins ( 0, 0, 0, 0 );
    _lay_center_stack->addWidget ( _table_view );
    _lay_center_stack->addWidget ( _mixer_hctl );
  }

  // Horizontal splitter
  {
    _splitter.setOrientation ( Qt::Horizontal );
    _splitter.addWidget ( &_wdg_side );
    _splitter.addWidget ( &_wdg_center );

    _splitter.setCollapsible ( 0, false );
    _splitter.setCollapsible ( 1, false );
    _splitter.setStretchFactor ( 1, 8 );

    lay_stack ()->addWidget ( &_splitter );
  }

  // Adjust margins
  {
    if ( QLayout * lay = _wdg_side.layout (); lay != nullptr ) {
      QMargins mgs ( lay->contentsMargins () );
      // Clear all but right
      mgs.setLeft ( 0 );
      mgs.setTop ( 0 );
      mgs.setBottom ( 0 );
      lay->setContentsMargins ( mgs );
    }

    if ( QLayout * lay = _wdg_center.layout (); lay != nullptr ) {
      QMargins mgs ( lay->contentsMargins () );
      // Clear all but left
      mgs.setRight ( 0 );
      mgs.setTop ( 0 );
      mgs.setBottom ( 0 );
      lay->setContentsMargins ( mgs );
    }

    if ( QLayout * lay = _mixer_hctl->layout (); lay != nullptr ) {
      lay->setContentsMargins ( 0, 0, 0, 0 );
    }
  }
}

Mixer_HCTL::~Mixer_HCTL ()
{
  set_mixer_dev_setup ( nullptr );
  set_inputs_setup ( nullptr );
  set_view_setup ( nullptr );
}

void
Mixer_HCTL::set_mixer_dev_setup ( const MWdg::Mixer_Device_Setup * setup_n )
{
  if ( mixer_dev_setup () != nullptr ) {
    if ( _snd_mixer->is_open () ) {
      // Close mixer
      _mixer_hctl->set_snd_elem_group ( nullptr );
      _tree_model->set_snd_mixer ( nullptr );
      _table_model->set_snd_mixer ( nullptr );
      _snd_mixer->close ();
    }
  }

  Views::View_Base::set_mixer_dev_setup ( setup_n );

  if ( mixer_dev_setup () != nullptr ) {
    if ( mixer_dev_setup ()->control_address.is_valid () ) {
      // Open mixer
      _snd_mixer->open ( mixer_dev_setup ()->control_address.addr_str () );
      if ( _snd_mixer->is_open () ) {
        setup_view ();
        restore_tree_view_selection ();
      }
    }
  }
}

void
Mixer_HCTL::set_inputs_setup ( const MWdg::Inputs_Setup * setup_n )
{
  if ( inputs_setup () != 0 ) {
    _mixer_hctl->set_inputs_setup ( 0 );
  }

  Views::View_Base::set_inputs_setup ( setup_n );

  if ( inputs_setup () != 0 ) {
    _mixer_hctl->set_inputs_setup ( inputs_setup () );
  }
}

void
Mixer_HCTL::set_view_setup ( Views::View_Base_Setup * setup_n )
{
  if ( _view_setup != nullptr ) {
  }

  _view_setup = dynamic_cast< Views::Mixer_HCTL_Setup * > ( setup_n );

  if ( _view_setup != nullptr ) {
    // Restore splitter state
    _splitter.restoreState ( _view_setup->splitter_state );
    // Setup view
    setup_view ();
    restore_tree_view_selection ();
  }
}

void
Mixer_HCTL::save_state ()
{
  if ( _view_setup == nullptr ) {
    return;
  }

  _view_setup->splitter_state = _splitter.saveState ();
}

void
Mixer_HCTL::setup_view ()
{
  if ( ( mixer_dev_setup () == nullptr ) || ( _view_setup == nullptr ) ) {
    return;
  }

  int lay_stack_idx = 0;

  // Open mixer
  if ( mixer_dev_setup ()->control_address.is_valid () ) {
    if ( _snd_mixer->is_open () ) {
      _tree_model->set_snd_mixer ( _snd_mixer );
      _table_model->set_snd_mixer ( _snd_mixer );
      lay_stack_idx = 1;
    } else {
      message_wdg ()->set_mixer_open_fail (
          mixer_dev_setup ()->control_address.addr_str (),
          _snd_mixer->err_message (),
          _snd_mixer->err_func () );
    }
  } else {
    message_wdg ()->set_no_device ();
  }

  expand_tree_items ();
  adjust_table_columns ();

  lay_stack ()->setCurrentIndex ( lay_stack_idx );
}

void
Mixer_HCTL::restore_tree_view_selection ()
{
  if ( ( mixer_dev_setup () == nullptr ) || ( _view_setup == nullptr ) ) {
    return;
  }

  // Tree view item selection
  {
    QModelIndex idx (
        _tree_model->elem_desc_index ( _view_setup->iface_name,
                                       _view_setup->elem_grp_name,
                                       _view_setup->elem_grp_index ) );
    if ( !idx.isValid () ) {
      idx = _tree_model->index ( 0, 0 );
    }
    if ( idx.isValid () ) {
      _tree_view->setCurrentIndex ( idx );
    }
  }
}

void
Mixer_HCTL::expand_tree_items ()
{
  const QModelIndex base_idx ( _tree_model->invisibleRootItem ()->index () );

  _tree_view->expand ( base_idx );

  const std::size_t num_rows = _tree_model->rowCount ( base_idx );
  for ( std::size_t ii = 0; ii < num_rows; ++ii ) {
    QModelIndex mod_idx ( _tree_model->index ( ii, 0, base_idx ) );
    _tree_view->expand ( mod_idx );
  }
}

void
Mixer_HCTL::adjust_table_columns ()
{
  _table_view->resizeColumnsToContents ();
  _table_view->resizeRowsToContents ();
}

void
Mixer_HCTL::tree_element_selected ( const QModelIndex & idx_n )
{
  if ( !idx_n.isValid () ) {
    return;
  }

  QSnd::HCtl::Elem_Group * elem_grp = nullptr;
  unsigned int elem_grp_index = 0;
  unsigned int iface_type_index = 0;

  unsigned int found_type = _tree_model->index_data (
      idx_n, &elem_grp, &elem_grp_index, &iface_type_index );

  if ( found_type == 1 ) {
    // std::cout << "Element type selected\n";
    _table_model->set_iface_type_idx ( iface_type_index );
    _lay_center_stack->setCurrentIndex ( 0 );
    adjust_table_columns ();
  } else if ( found_type == 2 ) {
    // std::cout << "Element group selected\n";
    _mixer_hctl->set_snd_elem_group ( elem_grp, elem_grp_index );
    _lay_center_stack->setCurrentIndex ( 1 );
  } else if ( found_type == 3 ) {
    // std::cout << "Element selected\n";
    _mixer_hctl->set_snd_elem_group ( elem_grp, elem_grp_index );
    _lay_center_stack->setCurrentIndex ( 1 );
  }

  // Save selected state in the setup class
  if ( _view_setup != nullptr ) {
    if ( found_type > 0 ) {
      _view_setup->iface_name =
          _snd_mixer->info_db ()->iface_name ( iface_type_index );
      if ( found_type == 1 ) {
        _view_setup->elem_grp_name.clear ();
      } else {
        _view_setup->elem_grp_name = elem_grp->elem ( 0 )->elem_name ();
        _view_setup->elem_grp_index = elem_grp_index;
      }
    }
  }
}

void
Mixer_HCTL::table_element_selected ( const QModelIndex & idx_n )
{
  QSnd::HCtl::Elem * elem = _table_model->elem ( idx_n );
  if ( elem != nullptr ) {
    QModelIndex tree_idx ( _tree_model->elem_index ( elem ) );
    if ( tree_idx.isValid () ) {
      _tree_view->setCurrentIndex ( tree_idx );
    }
  }
}

void
Mixer_HCTL::update_tree_model_colors ()
{
  auto set_color = [ this ] ( unsigned int style_id_n,
                              unsigned int snd_dir_n ) {
    const QPalette & pal = _wdg_style_db->palette ( style_id_n );
    _tree_model->set_snd_dir_foreground ( snd_dir_n,
                                          pal.color ( QPalette::WindowText ) );
  };
  // Set playback color
  set_color ( MWdg::Mixer_Style::PLAYBACK, 0 );
  // Set capture color
  set_color ( MWdg::Mixer_Style::CAPTURE, 1 );
}

} // namespace Views
