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

#include "main_window.hpp"
#include "mwdg/stream_view_selection.hpp"
#include "mwdg/stream_view_selection_actions.hpp"
#include "qastools_config.hpp"
#include "qsnd/cards_db.hpp"
#include "views/device_selection_bar.hpp"
#include "views/device_settings_dialog.hpp"
#include "views/info_dialog.hpp"
#include "views/mixer.hpp"
#include "views/view_utility.hpp"
#include "wdg/ds/painter/slider_bevelled.hpp"
#include "wdg/ds/painter/switch_circle.hpp"
#include "wdg/ds/painter/switch_svg.hpp"
#include "wdg/ds/widget_types.hpp"
#include <QCloseEvent>
#include <QFile>
#include <QIcon>
#include <QLabel>
#include <QMenuBar>
#include <QSplitter>
#include <QVBoxLayout>
#include <iostream>

Main_Window::Main_Window ( QSnd::Cards_Db * cards_db_n,
                           MState::Mixer::States_Db * mixer_states_db_n,
                           QWidget * parent_n,
                           Qt::WindowFlags flags_n )
: QMainWindow ( parent_n, flags_n )
, _mixer_style_db ( this )
, _image_alloc ( this )
, _cards_db ( cards_db_n )
{
  setWindowTitle ( PROGRAM_TITLE );
  setObjectName ( PROGRAM_TITLE );
  setContextMenuPolicy ( Qt::NoContextMenu );

  _str_fscreen_enable = tr ( "&Fullscreen mode" );
  _str_fscreen_disable = tr ( "Exit &fullscreen mode" );
  _icon_fscreen_enable = QIcon::fromTheme ( "view-fullscreen" );
  _icon_fscreen_disable = QIcon::fromTheme ( "view-restore" );

  // Image allocator setup
  // Slider painter bevelled
  {
    auto painter = std::make_shared< Wdg::DS::Painter::Slider_Bevelled > ();
    _image_alloc.install_painter ( painter );
  }

  // Switch painter circle
  {
    auto painter = std::make_shared< Wdg::DS::Painter::Switch_Circle > ();
    painter->set_group_variant ( Wdg::DS::SV_CIRCLE );
    _image_alloc.install_painter ( painter );
  }
  // Switch painter SVG
  {
    auto painter = std::make_shared< Wdg::DS::Painter::Switch_SVG > ();
    painter->set_group_variant ( Wdg::DS::SV_SVG_JOINED );
    painter->set_base_dir ( INSTALL_DIR_WIDGETS_GRAPHICS );
    painter->set_file_prefix_bg ( "sw_joined_bg_" );
    painter->set_file_prefix_handle ( "sw_joined_handle_" );
    if ( painter->ready () ) {
      _image_alloc.install_painter ( painter );
    }
  }

  // Init menus
  // Central mixer
  {
    _mixer = std::make_unique< Views::Mixer > (
        &_mixer_style_db, &_image_alloc, mixer_states_db_n );
    connect ( _mixer.get (),
              &Views::Mixer::sig_mixer_dev_reload_request,
              this,
              &Main_Window::reload_mixer_device,
              Qt::QueuedConnection );
  }

  // Device selection
  {
    _dev_select = std::make_unique< Views::Device_Selection_Bar > ( _cards_db );

    // QueuedConnection to update the GUI before loading the mixer
    connect ( _dev_select.get (),
              &Views::Device_Selection_View::sig_control_selected,
              this,
              &Main_Window::select_ctl_from_side_iface,
              Qt::QueuedConnection );

    connect ( _dev_select.get (),
              &Views::Device_Selection_View::sig_close,
              this,
              &Main_Window::toggle_device_selection );

    connect ( _dev_select.get (),
              &Views::Device_Selection_Bar::sig_device_settings,
              this,
              &Main_Window::toggle_device_settings_dialog );
  }

  // Connect stream view selection
  {
    connect ( _mixer.get (),
              &Views::Mixer::sig_streams_available,
              _dev_select->stream_selection (),
              &MWdg::Stream_View_Selection::set_available );

    connect ( _mixer.get (),
              &Views::Mixer::sig_show_playback_changed,
              _dev_select->stream_selection (),
              &MWdg::Stream_View_Selection::set_playback_silent );

    connect ( _mixer.get (),
              &Views::Mixer::sig_show_capture_changed,
              _dev_select->stream_selection (),
              &MWdg::Stream_View_Selection::set_capture_silent );

    connect ( _dev_select->stream_selection (),
              &MWdg::Stream_View_Selection::sig_playback,
              _mixer.get (),
              &Views::Mixer::show_playback );

    connect ( _dev_select->stream_selection (),
              &MWdg::Stream_View_Selection::sig_capture,
              _mixer.get (),
              &Views::Mixer::show_capture );
  }

  // Central splitter
  {
    _splitter = std::make_unique< QSplitter > ();
    _splitter->addWidget ( _mixer.get () );
    _splitter->addWidget ( _dev_select.get () );
    _splitter->setStretchFactor ( 0, 1 );
    _splitter->setStretchFactor ( 1, 0 );
    _splitter->setCollapsible ( 0, false );
    _splitter->setCollapsible ( 1, false );
    connect ( _splitter.get (),
              &QSplitter::splitterMoved,
              this,
              &Main_Window::on_splitter_moved );
    setCentralWidget ( _splitter.get () );
  }

  // Action: Exit / Quit
  QAction * act_quit = new QAction ( tr ( "&Quit" ), this );
  act_quit->setShortcut ( QKeySequence ( QKeySequence::Quit ) );
  act_quit->setIcon ( QIcon::fromTheme ( "application-exit" ) );
  connect ( act_quit, &QAction::triggered, this, &Main_Window::sig_quit );

  // Action: Settings
  QAction * act_settings = new QAction ( tr ( "&Settings" ), this );
  act_settings->setShortcut ( QKeySequence ( tr ( "Ctrl+s" ) ) );
  act_settings->setIcon ( QIcon::fromTheme ( "preferences-system" ) );
  connect ( act_settings,
            &QAction::triggered,
            this,
            &Main_Window::sig_show_settings );

  // Action: Refresh
  QAction * act_refresh = new QAction ( tr ( "&Refresh" ), this );
  act_refresh->setShortcut ( QKeySequence ( QKeySequence::Refresh ) );
  act_refresh->setIcon ( QIcon::fromTheme ( "view-refresh" ) );
  connect (
      act_refresh, &QAction::triggered, this, &Main_Window::refresh_views );

  // Action: Device selection
  _act_show_dev_select.reset ( new QAction ( this ) );
  _act_show_dev_select->setText ( tr ( "Show &device selection" ) );
  _act_show_dev_select->setCheckable ( true );
  connect ( _act_show_dev_select.get (),
            &QAction::toggled,
            this,
            &Main_Window::show_device_selection );

  // Action: Fullscreen
  _act_fullscreen.reset ( new QAction ( this ) );
  _act_fullscreen->setShortcut ( QKeySequence ( Qt::Key_F11 ) );
  _act_fullscreen->setCheckable ( true );
  connect ( _act_fullscreen.get (),
            &QAction::toggled,
            this,
            &Main_Window::set_fullscreen );

  // Actions: Stream view
  _acts_stream_view.reset ( new MWdg::Stream_View_Selection_Actions ( this ) );
  connect ( _mixer.get (),
            &Views::Mixer::sig_streams_available,
            _acts_stream_view.get (),
            &MWdg::Stream_View_Selection_Actions::set_available );
  connect ( _mixer.get (),
            &Views::Mixer::sig_show_playback_changed,
            _acts_stream_view.get (),
            &MWdg::Stream_View_Selection_Actions::set_playback_silent );
  connect ( _mixer.get (),
            &Views::Mixer::sig_show_capture_changed,
            _acts_stream_view.get (),
            &MWdg::Stream_View_Selection_Actions::set_capture_silent );
  connect ( _acts_stream_view.get (),
            &MWdg::Stream_View_Selection_Actions::sig_playback,
            _mixer.get (),
            &Views::Mixer::show_playback );
  connect ( _acts_stream_view.get (),
            &MWdg::Stream_View_Selection_Actions::sig_capture,
            _mixer.get (),
            &Views::Mixer::show_capture );

  // Action: About
  QAction * act_about = Views::Info_Dialog::create_action_about ( this );
  connect (
      act_about, &QAction::triggered, this, &Main_Window::sig_show_about );

  QAction * act_about_qt = Views::Info_Dialog::create_action_about_qt ( this );
  connect ( act_about_qt,
            &QAction::triggered,
            this,
            &Main_Window::sig_show_about_qt );

  // Menus
  {
    QMenu * cmenu = menuBar ()->addMenu ( tr ( "&File" ) );
    cmenu->addAction ( act_settings );
    cmenu->addSeparator ();
    cmenu->addAction ( act_quit );
  }
  {
    QMenu * cmenu = menuBar ()->addMenu ( tr ( "&View" ) );
    cmenu->addAction ( _act_show_dev_select.get () );
    cmenu->addSeparator ();
    cmenu->addAction ( _acts_stream_view->act_playback () );
    cmenu->addAction ( _acts_stream_view->act_capture () );
    cmenu->addSeparator ();
    cmenu->addAction ( _act_fullscreen.get () );
    cmenu->addSeparator ();
    cmenu->addAction ( act_refresh );
  }
  {
    QMenu * cmenu = menuBar ()->addMenu ( tr ( "&Help" ) );
    cmenu->addAction ( act_about );
    cmenu->addAction ( act_about_qt );
  }

  update_fullscreen_action ();
}

Main_Window::~Main_Window () = default;

QSize
Main_Window::sizeHint () const
{
  QSize res ( QMainWindow::sizeHint () );
  Views::win_default_size ( this, res );
  return res;
}

void
Main_Window::set_window_setup ( Main_Window_Setup * setup_n )
{
  if ( _win_setup != nullptr ) {
    _mixer->set_mixer_dev_setup ( nullptr );
    _mixer->set_inputs_setup ( nullptr );
    _mixer->set_view_setup ( nullptr );
    _dev_select->set_inputs_setup ( nullptr );
    _dev_select->set_view_setup ( nullptr );
    _acts_stream_view->set_inputs_setup ( nullptr );
  }

  _win_setup = setup_n;

  if ( _win_setup != nullptr ) {
    // Restore mixer window state
    restoreState ( _win_setup->window_state );
    if ( !restoreGeometry ( _win_setup->window_geometry ) ) {
      Views::resize_to_default ( this );
    }

    _splitter->restoreState ( _win_setup->splitter_state );

    // Actions
    _act_show_dev_select->setShortcut (
        _win_setup->dev_select.kseq_toggle_vis );
    _act_show_dev_select->setChecked ( _win_setup->show_dev_select );
    _acts_stream_view->set_inputs_setup ( &_win_setup->inputs );

    // Pass setup tree to child classes
    _dev_select->set_view_setup ( &_win_setup->dev_select );
    _dev_select->set_inputs_setup ( &_win_setup->inputs );
    _dev_select->silent_select_ctl ( _win_setup->mixer_dev.control_address );
    _dev_select->setVisible ( _win_setup->show_dev_select );

    _mixer->set_view_setup ( &_win_setup->mixer );
    _mixer->set_inputs_setup ( &_win_setup->inputs );
    _mixer->set_mixer_dev_setup ( &_win_setup->mixer_dev );
  }
}

void
Main_Window::update_fullscreen_action ()
{
  QString * txt;
  QIcon * icon;
  bool checked;

  if ( isFullScreen () ) {
    txt = &_str_fscreen_disable;
    icon = &_icon_fscreen_disable;
    checked = true;
  } else {
    txt = &_str_fscreen_enable;
    icon = &_icon_fscreen_enable;
    checked = false;
  }

  _act_fullscreen->setText ( *txt );
  _act_fullscreen->setIcon ( *icon );
  _act_fullscreen->setChecked ( checked );
}

void
Main_Window::select_ctl ( const QSnd::Ctl_Address & ctl_n )
{
  // std::cout << "Main_Window::select_ctl " << ctl_n.toLocal8Bit ().data ()
  //           << "\n";

  // Update device selection bar
  _dev_select->silent_select_ctl ( ctl_n );

  if ( _win_setup != nullptr ) {
    // Remove
    _mixer->set_mixer_dev_setup ( nullptr );
    // Change
    _win_setup->mixer_dev.control_address = ctl_n;
    // Reinstall
    _mixer->set_mixer_dev_setup ( &_win_setup->mixer_dev );
  }

  Q_EMIT sig_control_changed ();
}

void
Main_Window::select_ctl_from_side_iface ()
{
  // std::cout << "Main_Window::select_ctl_from_side_iface " << "\n";
  select_ctl ( _dev_select->selected_ctl () );
}

void
Main_Window::reload_mixer_device ()
{
  // std::cout << "Main_Window::reload_mixer_device" << "\n";
  _mixer->set_mixer_dev_setup ( nullptr );
  _mixer->set_mixer_dev_setup ( &_win_setup->mixer_dev );
}

void
Main_Window::reload_mixer_inputs ()
{
  // std::cout << "Main_Window::reload_mixer_inputs" << "\n";
  _mixer->set_inputs_setup ( nullptr );
  _dev_select->set_inputs_setup ( nullptr );
  _acts_stream_view->set_inputs_setup ( nullptr );

  if ( _win_setup != nullptr ) {
    _acts_stream_view->set_inputs_setup ( &_win_setup->inputs );
    _dev_select->set_inputs_setup ( &_win_setup->inputs );
    _mixer->set_inputs_setup ( &_win_setup->inputs );
  }
}

void
Main_Window::refresh_views ()
{
  _cards_db->reload ();
  _dev_select->reload_database ();
  reload_mixer_device ();
}

void
Main_Window::reload_mixer_view ()
{
  // std::cout << "Main_Window::reload_mixer_view" << "\n";
  _mixer->set_view_setup ( nullptr );
  _mixer->set_view_setup ( &_win_setup->mixer );
}

void
Main_Window::set_fullscreen ( bool flag_n )
{
  if ( flag_n != isFullScreen () ) {
    if ( flag_n ) {
      showFullScreen ();
    } else {
      showNormal ();
    }
    update_fullscreen_action ();
  }
}

void
Main_Window::show_device_selection ( bool flag_n )
{
  if ( _win_setup != nullptr ) {
    if ( _win_setup->show_dev_select != flag_n ) {
      _win_setup->show_dev_select = flag_n;
    }
  }
  if ( flag_n ) {
    _dev_select->show ();
    if ( _win_setup != nullptr ) {
      _splitter->restoreState ( _win_setup->splitter_state );
    }
  } else {
    _dev_select->hide ();
  }
}

void
Main_Window::toggle_device_selection ()
{
  _act_show_dev_select->setChecked ( !_act_show_dev_select->isChecked () );
}

void
Main_Window::toggle_device_settings_dialog ()
{
  // Create a new dialog on demand
  if ( _dialog_device_settings.isNull () ) {
    auto dialog = new Views::Device_Settings_Dialog ( &_mixer_style_db, this );
    connect ( dialog,
              &Views::Device_Settings_Dialog::sig_sorting_changed,
              _mixer.get (),
              &Views::Mixer::reload_proxies_models_sorted );
    connect ( dialog,
              &Views::Device_Settings_Dialog::sig_visibility_changed,
              _mixer.get (),
              &Views::Mixer::reload_proxies_models_filtered );
    dialog->set_setup ( &_win_setup->device_settings_dialog );
    dialog->set_models ( _mixer->sliders_model (), _mixer->switches_model () );
    _dialog_device_settings = dialog;
    _dialog_device_settings->setAttribute ( Qt::WA_DeleteOnClose );
    _dialog_device_settings->show ();
  } else {
    _dialog_device_settings->close ();
  }
}

void
Main_Window::save_state ()
{
  if ( _win_setup != nullptr ) {
    _win_setup->window_state = saveState ();
    _win_setup->window_geometry = saveGeometry ();
    if ( _dev_select->isVisible () ) {
      _win_setup->splitter_state = _splitter->saveState ();
    }
  }
}

void
Main_Window::on_splitter_moved ()
{
  if ( _win_setup != nullptr ) {
    if ( _dev_select->isVisible () ) {
      _win_setup->splitter_state = _splitter->saveState ();
    }
  }
}

void
Main_Window::changeEvent ( QEvent * event_n )
{
  QMainWindow::changeEvent ( event_n );

  switch ( event_n->type () ) {
  case QEvent::StyleChange:
  case QEvent::PaletteChange:
    // Use the main window palette as the default palette
    _mixer_style_db.update_palettes ( palette () );
    break;
  case QEvent::WindowStateChange:
    update_fullscreen_action ();
    break;
  default:
    break;
  }
}

void
Main_Window::keyPressEvent ( QKeyEvent * event_n )
{
  QMainWindow::keyPressEvent ( event_n );
  if ( _win_setup != nullptr ) {
    const QKeySequence kseq = event_n->key ();
    if ( kseq == _win_setup->dev_select.kseq_toggle_vis ) {
      toggle_device_selection ();
    }
  }
}

void
Main_Window::closeEvent ( QCloseEvent * event_n )
{
  // Make sure the dialog receives a close event
  if ( !_dialog_device_settings.isNull () ) {
    _dialog_device_settings->close ();
  }
  QMainWindow::closeEvent ( event_n );
}
