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

#include "proxies_model.hpp"
#include "mstate/mixer/controls.hpp"
#include "mstate/mixer/state.hpp"
#include "mwdg/mixer/switches/proxy/column.hpp"
#include "mwdg/mixer/switches/proxy/enum.hpp"
#include "mwdg/mixer/switches/proxy/group.hpp"
#include "mwdg/mixer/switches/proxy/switch.hpp"
#include "mwdg/mixer_styles.hpp"
#include "qsnd/mixer/elem.hpp"
#include "qsnd/mixer/mixer.hpp"
#include <QCoreApplication>
#include <QEvent>
#include <algorithm>
#include <iostream>

namespace MWdg::Mixer::Switches
{

Proxies_Model::Proxies_Model ( QObject * parent_n )
: MWdg::Mixer::Proxies_Model ( parent_n )
{
  _tool_tip.l10n_enum[ 0 ] = tr ( "Playback selection" );
  _tool_tip.l10n_enum[ 1 ] = tr ( "Capture selection" );
  _tool_tip.l10n_switch[ 0 ] = tr ( "Playback switch" );
  _tool_tip.l10n_switch[ 1 ] = tr ( "Capture switch" );
}

Proxies_Model::~Proxies_Model () = default;

void
Proxies_Model::create_proxies_groups ( Proxies_Groups_Owner & groups_n )
{
  for ( std::size_t ii = 0; ii < _qsnd_mixer->num_elems (); ++ii ) {
    QSnd::Mixer::Elem * qsme = _qsnd_mixer->elem ( ii );

    // Types means either enum or switch
    for ( std::size_t itype = 0; itype < 2; ++itype ) {
      for ( std::size_t snd_dir = 0; snd_dir < 2; ++snd_dir ) {
        if ( qsme->has_volume ( snd_dir ) ) {
          continue;
        }

        bool cr_enum = ( itype == 0 );
        bool cr_switch = ( itype == 1 );

        if ( ( cr_enum && qsme->has_enum ( snd_dir ) ) ||
             ( cr_switch && qsme->has_switch ( snd_dir ) ) ) {

          // Create group
          std::unique_ptr< MWdg::Mixer::Proxy::Group > pgrp (
              new Proxy::Group ( this ) );

          pgrp->set_qsnd_mixer_elem ( qsme );
          pgrp->set_snd_dir ( snd_dir );
          pgrp->set_group_id ( create_group_id ( qsme, snd_dir ) );
          pgrp->set_group_name ( qsme->display_name () );
          pgrp->set_style_id ( MWdg::Mixer_Style::PLAYBACK + snd_dir );
          if ( show_tool_tips () ) {
            pgrp->set_tool_tip ( create_group_tool_tip ( qsme, snd_dir ) );
          }

          if ( should_be_separated ( pgrp.get () ) ) {
            setup_proxies_group_separate ( pgrp.get () );
          } else {
            setup_proxies_group_joined ( pgrp.get () );
          }

          if ( pgrp->num_columns () > 0 ) {
            // Keep group
            groups_n.push_back ( std::move ( pgrp ) );
          }
        }
      }
    }
  }
}

void
Proxies_Model::setup_proxies_group_joined ( MWdg::Mixer::Proxy::Group * pgrp_n )
{
  // Store separation state
  _mixer_state->switches ().set_proxies_group_separated ( pgrp_n->group_id (),
                                                          false );

  const QSnd::Mixer::Elem * qsme = pgrp_n->qsnd_mixer_elem ();
  const std::size_t snd_dir = pgrp_n->snd_dir ();

  pgrp_n->clear_columns ();
  pgrp_n->set_is_joined ( true );

  std::size_t num_channels = 0;
  if ( qsme->has_switch ( snd_dir ) ) {
    num_channels = qsme->num_switch_channels ( snd_dir );
  } else if ( qsme->has_enum ( snd_dir ) ) {
    num_channels = qsme->num_enum_channels ( snd_dir );
  }

  if ( num_channels > 0 ) {
    create_proxies_column ( pgrp_n, 0 );
  }

  pgrp_n->update_mixer_values ();
}

void
Proxies_Model::setup_proxies_group_separate (
    MWdg::Mixer::Proxy::Group * pgrp_n )
{
  // Store separation state
  _mixer_state->switches ().set_proxies_group_separated ( pgrp_n->group_id (),
                                                          true );

  const QSnd::Mixer::Elem * qsme = pgrp_n->qsnd_mixer_elem ();
  const std::size_t snd_dir = pgrp_n->snd_dir ();

  pgrp_n->clear_columns ();
  pgrp_n->set_is_joined ( false );

  std::size_t num_channels = 0;
  if ( qsme->has_switch ( snd_dir ) ) {
    num_channels = qsme->num_switch_channels ( snd_dir );
  } else if ( qsme->has_enum ( snd_dir ) ) {
    num_channels = qsme->num_enum_channels ( snd_dir );
  }

  for ( std::size_t ii = 0; ii < num_channels; ++ii ) {
    create_proxies_column ( pgrp_n, ii );
  }

  pgrp_n->update_mixer_values ();
}

void
Proxies_Model::create_proxies_column ( MWdg::Mixer::Proxy::Group * pgrp_n,
                                       int channel_idx_n )
{
  QSnd::Mixer::Elem * qsme = pgrp_n->qsnd_mixer_elem ();
  const std::size_t snd_dir = pgrp_n->snd_dir ();

  QString iname;
  if ( pgrp_n->is_joined () ) {
    iname = pgrp_n->group_name ();
  } else {
    iname = tr ( "%1 (%2)" );
    iname = iname.arg ( QCoreApplication::translate (
        "ALSA::Channel_Name", qsme->channel_name ( snd_dir, channel_idx_n ) ) );
    iname = iname.arg ( qsme->channel ( snd_dir, channel_idx_n ) );
  }

  QString ttip;
  if ( show_tool_tips () ) {
    ttip += QLatin1StringView ( "<div><b>" );
    ttip += pgrp_n->group_name ();
    ttip += QLatin1StringView ( "</b></div>\n" );

    ttip += QLatin1StringView ( "<div>" );
    if ( qsme->has_enum ( snd_dir ) ) {
      ttip += _tool_tip.l10n_enum[ snd_dir ];
    } else {
      ttip += _tool_tip.l10n_switch[ snd_dir ];
    }
    ttip += QLatin1StringView ( "</div>\n" );

    if ( !pgrp_n->is_joined () ) {
      ttip += QLatin1StringView ( "<div>" );
      ttip += iname;
      ttip += QLatin1StringView ( "</div>" );
    }
  }

  auto pcol = std::make_unique< MWdg::Mixer::Switches::Proxy::Column > ();

  if ( qsme->has_enum ( snd_dir ) ) {
    Proxy::Enum * msp = new Proxy::Enum;

    msp->set_qsnd_mixer_elem ( qsme );
    msp->set_snd_dir ( snd_dir );
    msp->set_is_joined ( pgrp_n->is_joined () );
    msp->set_channel_idx ( channel_idx_n );
    msp->set_enum_num_items ( qsme->enum_item_names ().size () );

    msp->set_item_name ( iname );
    msp->set_tool_tip ( ttip );
    msp->set_style_id ( pgrp_n->style_id () );

    pcol->set_enum_proxy ( msp );
  } else if ( qsme->has_switch ( snd_dir ) ) {
    Proxy::Switch * msp = new Proxy::Switch;

    msp->set_qsnd_mixer_elem ( qsme );
    msp->set_snd_dir ( snd_dir );
    msp->set_is_joined ( pgrp_n->is_joined () );
    msp->set_channel_idx ( channel_idx_n );

    msp->set_item_name ( iname );
    msp->set_tool_tip ( ttip );
    msp->set_style_id ( pgrp_n->style_id () );

    pcol->set_switch_proxy ( msp );
  } else {
    pcol.reset ();
  }

  if ( pcol ) {
    pgrp_n->append_column ( pcol.release () );
  }
}

MState::Mixer::Controls *
Proxies_Model::get_mixer_state_controls () const
{
  if ( mixer_state () ) {
    return &mixer_state ()->switches ();
  }
  return nullptr;
}

void
Proxies_Model::join_proxies_group ( MWdg::Mixer::Proxy::Group * pgrp_n )
{
  if ( !pgrp_n->is_joined () ) {
    // Level switches values
    {
      QSnd::Mixer::Elem * qsme = pgrp_n->qsnd_mixer_elem ();
      qsme->level_switches ( pgrp_n->snd_dir () );
    }
    setup_proxies_group_joined ( pgrp_n );
  }
}

void
Proxies_Model::separate_proxies_group ( MWdg::Mixer::Proxy::Group * pgrp_n )
{
  if ( pgrp_n->is_joined () && pgrp_n->can_be_separated () ) {
    setup_proxies_group_separate ( pgrp_n );
  }
}

} // namespace MWdg::Mixer::Switches
