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

#include "mixers_model.hpp"
#include "qsnd/cards_model.hpp"
#include <iostream>

namespace QSnd
{

Mixers_Model::Mixers_Model ( QSnd::Cards_Db * cards_db_n, QObject * parent_n )
: QAbstractListModel ( parent_n )
, _cards_model ( cards_db_n, this )
{
  _default_info.acquire_info ( "default" );

  connect ( &_cards_model,
            &QAbstractItemModel::rowsAboutToBeInserted,
            this,
            &Mixers_Model::cards_insert_begin );
  connect ( &_cards_model,
            &QAbstractItemModel::rowsInserted,
            this,
            &Mixers_Model::cards_insert_done );

  connect ( &_cards_model,
            &QAbstractItemModel::rowsAboutToBeRemoved,
            this,
            &Mixers_Model::cards_remove_begin );
  connect ( &_cards_model,
            &QAbstractItemModel::rowsRemoved,
            this,
            &Mixers_Model::cards_remove_done );
}

Mixers_Model::~Mixers_Model () = default;

bool
Mixers_Model::card_id_is_unique ( const QString & card_id_n ) const
{
  std::size_t count = 0;
  for ( std::size_t row = 0; row < _cards_model.num_cards (); ++row ) {
    const auto & handle = _cards_model.card_info ( row );
    if ( handle->id () == card_id_n ) {
      ++count;
      if ( count > 1 ) {
        // Card id is not unique
        return false;
      }
    }
  }
  // Card id is unique
  return true;
}

int
Mixers_Model::count () const
{
  return _cards_model.count () + 4;
}

QModelIndex
Mixers_Model::control_index ( const QSnd::Ctl_Address & ctl_addr_n ) const
{
  // Default mixer
  if ( ctl_addr_n.addr_str () == QLatin1StringView ( "default" ) ) {
    return index ( 0, 0 );
  }
  // Card
  if ( ( ctl_addr_n.ctl_name () == QLatin1StringView ( "hw" ) ) &&
       ( ctl_addr_n.num_args () == 1 ) ) {
    const auto & num_arg = ctl_addr_n.arg ( 0 );
    if ( num_arg.name ().isEmpty () ||
         ( num_arg.name () == QLatin1StringView ( "CARD" ) ) ) {
      bool ok = false;
      int card_index = num_arg.value ().toInt ( &ok );
      if ( ok ) {
        // Numeric card index
        for ( std::size_t row = 0; row < _cards_model.num_cards (); ++row ) {
          const auto & handle = _cards_model.card_info ( row );
          if ( handle->index () == card_index ) {
            return index ( row + cards_offset, 0 );
          }
        }
      } else {
        // Non numeric card index.  Check the id string.
        for ( std::size_t row = 0; row < _cards_model.num_cards (); ++row ) {
          const auto & handle = _cards_model.card_info ( row );
          if ( handle->id () == num_arg.value () ) {
            return index ( row + cards_offset, 0 );
          }
        }
      }
    }
  }
  // User defined
  return index ( count () - 1, 0 );
}

Mixers_Model::Entry
Mixers_Model::entry ( const QModelIndex & index_n ) const
{
  int lcount = count ();
  if ( ( !index_n.isValid () ) || ( index_n.parent ().isValid () ) ||
       ( index_n.column () != 0 ) || ( index_n.row () < 0 ) ||
       ( index_n.row () >= lcount ) ) {
    return ENTRY_INVALID;
  }
  if ( index_n.row () == 0 ) {
    return ENTRY_DEFAULT;
  }
  if ( index_n.row () == 1 ) {
    return ENTRY_SEPARATOR;
  }
  if ( ( index_n.row () - cards_offset ) < _cards_model.count () ) {
    return ENTRY_CARD;
  }
  if ( index_n.row () == ( lcount - 2 ) ) {
    return ENTRY_SEPARATOR;
  }
  if ( index_n.row () == ( lcount - 1 ) ) {
    return ENTRY_USER_DEFINED;
  }
  return ENTRY_INVALID;
}

std::shared_ptr< const QSnd::Card_Info >
Mixers_Model::card_info ( const QModelIndex & index_n ) const
{
  Entry ent = entry ( index_n );
  if ( ent == ENTRY_CARD ) {
    return _cards_model.card_info ( index_n.row () - cards_offset );
  }
  return {};
}

void
Mixers_Model::reload ()
{
  reload_begin ();
  reload_finish ();
}

void
Mixers_Model::reload_begin ()
{
  beginResetModel ();
}

void
Mixers_Model::reload_finish ()
{
  _default_info.acquire_info ( QStringLiteral ( "default" ) );
  endResetModel ();

  Q_EMIT countChanged ();
}

void
Mixers_Model::cards_insert_begin ( const QModelIndex & parent_n,
                                   int start_n,
                                   int end_n )
{
  if ( parent_n.isValid () ) {
    return;
  }
  beginInsertRows (
      QModelIndex (), start_n + cards_offset, end_n + cards_offset );
}

void
Mixers_Model::cards_insert_done ( const QModelIndex & parent_n,
                                  int start_n [[maybe_unused]],
                                  int end_n [[maybe_unused]] )
{
  if ( parent_n.isValid () ) {
    return;
  }
  endInsertRows ();
}

void
Mixers_Model::cards_remove_begin ( const QModelIndex & parent_n,
                                   int start_n,
                                   int end_n )
{
  if ( parent_n.isValid () ) {
    return;
  }
  beginRemoveRows (
      QModelIndex (), start_n + cards_offset, end_n + cards_offset );
}

void
Mixers_Model::cards_remove_done ( const QModelIndex & parent_n,
                                  int start_n [[maybe_unused]],
                                  int end_n [[maybe_unused]] )
{
  if ( parent_n.isValid () ) {
    return;
  }
  endRemoveRows ();
}

QHash< int, QByteArray >
Mixers_Model::roleNames () const
{
  auto res = QAbstractListModel::roleNames ();
  res.insert ( ROLE_INDEX, "index" );
  res.insert ( ROLE_ID, "id" );
  res.insert ( ROLE_DRIVER, "driver" );
  res.insert ( ROLE_NAME, "name" );
  res.insert ( ROLE_LONG_NAME, "longName" );
  res.insert ( ROLE_MIXER_NAME, "mixerName" );
  res.insert ( ROLE_COMPONENTS, "components" );
  return res;
}

int
Mixers_Model::rowCount ( const QModelIndex & parent_n ) const
{
  if ( parent_n.isValid () ) {
    return 0;
  }
  return count ();
}

Qt::ItemFlags
Mixers_Model::flags ( const QModelIndex & index_n ) const
{
  Entry ent = entry ( index_n );
  switch ( ent ) {
  case ENTRY_INVALID:
  case ENTRY_SEPARATOR:
    return Qt::NoItemFlags;
  case ENTRY_DEFAULT:
  case ENTRY_CARD:
  case ENTRY_USER_DEFINED:
    return ( Qt::ItemIsSelectable | Qt::ItemIsEnabled );
  }
  return Qt::NoItemFlags;
}

QVariant
Mixers_Model::data ( const QModelIndex & index_n, int role_n ) const
{
  Entry ent = entry ( index_n );
  const QSnd::Card_Info * handle = nullptr;
  switch ( ent ) {
  case ENTRY_INVALID:
  case ENTRY_SEPARATOR:
    return QVariant ();
  case ENTRY_DEFAULT:
    handle = &_default_info;
    break;
  case ENTRY_CARD:
    handle = _cards_model.card_info ( index_n.row () - cards_offset ).get ();
    break;
  case ENTRY_USER_DEFINED:
    handle = &_user_defined_info;
    break;
  }

  if ( handle != nullptr ) {
    switch ( role_n ) {
    case Qt::DisplayRole:
      switch ( ent ) {
      case ENTRY_INVALID:
      case ENTRY_SEPARATOR:
        return QVariant ();
      case ENTRY_DEFAULT:
        return QVariant ( tr ( "Default" ) );
      case ENTRY_CARD:
        return QVariant ( handle->name () );
      case ENTRY_USER_DEFINED:
        return QVariant ( tr ( "User defined" ) );
      }
      break;
    case ROLE_INDEX:
      return QVariant ( handle->index () );
    case ROLE_ID:
      return QVariant ( handle->id () );
    case ROLE_DRIVER:
      return QVariant ( handle->driver () );
    case ROLE_NAME:
      return QVariant ( handle->name () );
    case ROLE_LONG_NAME:
      return QVariant ( handle->long_name () );
    case ROLE_MIXER_NAME:
      return QVariant ( handle->mixer_name () );
    case ROLE_COMPONENTS:
      return QVariant ( handle->components () );
    default:
      break;
    }
  }
  return QVariant ();
}

} // namespace QSnd
