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

#include "main_window.hpp"
#include "qastools_config.hpp"
#include "views/device_selection_view.hpp"
#include "views/info_dialog.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 <QApplication>
#include <QDockWidget>
#include <QEvent>
#include <QMenu>
#include <QMenuBar>
#include <QSettings>
#include <QVBoxLayout>
#include <memory>

Main_Window::Main_Window ( QWidget * parent_n, Qt::WindowFlags flags_n )
: QMainWindow ( parent_n, flags_n )
, _mixer_style_db ( this )
, _image_alloc ( this )
, _cards_db ( this )
{
  setWindowTitle ( PROGRAM_TITLE );
  setObjectName ( PROGRAM_TITLE );
  setContextMenuPolicy ( Qt::NoContextMenu );

  // Strings and icons
  _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" );

  // 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 widgets
  // Device selection
  {
    _dev_select = new Views::Device_Selection_View ( &_cards_db );
    _dev_select->hide ();

    connect ( _dev_select,
              &Views::Device_Selection_View::sig_control_selected,
              this,
              &Main_Window::select_ctl_from_side_iface );

    connect ( _dev_select,
              &Views::Device_Selection_View::sig_close,
              this,
              &Main_Window::toggle_device_selection );
  }
  // Central mixer
  {
    _mixer_hctl = new Views::Mixer_HCTL ( &_mixer_style_db, &_image_alloc );
    connect ( _mixer_hctl,
              &Views::Mixer_HCTL::sig_mixer_dev_reload_request,
              this,
              &Main_Window::reload_mixer_device,
              Qt::QueuedConnection );
  }
  // Central splitter
  {
    _splitter = std::make_unique< QSplitter > ();
    _splitter->addWidget ( _mixer_hctl );
    _splitter->addWidget ( _dev_select );
    _splitter->setStretchFactor ( 0, 1 );
    _splitter->setStretchFactor ( 1, 0 );
    _splitter->setCollapsible ( 0, false );
    _splitter->setCollapsible ( 1, false );
    setCentralWidget ( _splitter.get () );
  }

  // Init menu bar
  {
    // Action: 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::close );

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

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

    // 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 );

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

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

    // Menu: File
    {
      QMenu * cmenu = menuBar ()->addMenu ( tr ( "&File" ) );
      cmenu->addAction ( act_quit );
    }

    // Menu: View
    {
      QMenu * cmenu = menuBar ()->addMenu ( tr ( "&View" ) );
      cmenu->addAction ( _act_show_dev_select );
      cmenu->addAction ( _act_fullscreen );
      cmenu->addSeparator ();
      cmenu->addAction ( act_refresh );
    }

    // Menu: Help
    {
      QMenu * menu = menuBar ()->addMenu ( tr ( "&Help" ) );
      menu->addAction ( act_about );
      menu->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::restore_state ()
{
  QSettings settings;

  QByteArray mwin_state;
  QByteArray mwin_geom;
  QByteArray mwin_splitter;

  // Main window state
  {
    settings.beginGroup ( "main_window" );
    {
      mwin_state =
          settings.value ( "window_state", QByteArray () ).toByteArray ();

      mwin_geom =
          settings.value ( "window_geometry", QByteArray () ).toByteArray ();

      mwin_splitter =
          settings.value ( "splitter_state", QByteArray () ).toByteArray ();
    }

    _setup.show_dev_select =
        settings.value ( "show_device_selection", _setup.show_dev_select )
            .toBool ();
    settings.endGroup ();
  }

  // General
  {
    _setup.mixer_dev.control_address =
        settings
            .value ( "current_device",
                     _setup.mixer_dev.control_address.addr_str () )
            .toString ();

    _setup.inputs.wheel_degrees =
        settings.value ( "wheel_degrees", _setup.inputs.wheel_degrees )
            .toUInt ();
  }

  // HCtl mixer
  {
    settings.beginGroup ( "element_mixer" );

    _setup.hctl.splitter_state =
        settings.value ( "splitter_state", _setup.hctl.splitter_state )
            .toByteArray ();

    _setup.hctl.iface_name =
        settings.value ( "iface_name", _setup.hctl.iface_name ).toString ();

    _setup.hctl.elem_grp_name =
        settings.value ( "elem_grp_name", _setup.hctl.elem_grp_name )
            .toString ();

    _setup.hctl.elem_grp_index =
        settings.value ( "elem_grp_index", _setup.hctl.elem_grp_index )
            .toUInt ();

    settings.endGroup ();
  }

  // Device selection
  {
    settings.beginGroup ( "device_selection" );

    _setup.dev_select.user_device =
        settings.value ( "user_device", _setup.dev_select.user_device )
            .toString ();

    settings.endGroup ();
  }

  // Sanitize values

  if ( !_setup.mixer_dev.control_address.is_valid () ) {
    _setup.mixer_dev.control_address = QStringLiteral ( "default" );
  }

  if ( _setup.inputs.wheel_degrees == 0 ) {
    _setup.inputs.wheel_degrees = 720;
  }

  _setup.inputs.update_translation ();

  // Apply values
  restoreState ( mwin_state );
  if ( !restoreGeometry ( mwin_geom ) ) {
    Views::resize_to_default ( this );
  }
  _splitter->restoreState ( mwin_splitter );
  update_fullscreen_action ();
  show_device_selection ( _setup.show_dev_select );
  _dev_select->set_view_setup ( &_setup.dev_select );
  _dev_select->silent_select_ctl ( _setup.mixer_dev.control_address );
  _mixer_hctl->set_inputs_setup ( &_setup.inputs );
  _mixer_hctl->set_mixer_dev_setup ( &_setup.mixer_dev );
  _mixer_hctl->set_view_setup ( &_setup.hctl );
}

void
Main_Window::save_state ()
{
  _mixer_hctl->save_state ();

  QSettings settings;

  // Main window state
  {
    settings.beginGroup ( "main_window" );
    settings.setValue ( "window_state", saveState () );
    settings.setValue ( "window_geometry", saveGeometry () );
    settings.setValue ( "splitter_state", _splitter->saveState () );
    settings.setValue ( "show_device_selection", _setup.show_dev_select );
    settings.endGroup ();
  }

  settings.setValue ( "current_device",
                      _setup.mixer_dev.control_address.addr_str () );
  settings.setValue ( "wheel_degrees", _setup.inputs.wheel_degrees );

  // CTL mixer
  {
    settings.beginGroup ( "element_mixer" );
    settings.setValue ( "splitter_state", _setup.hctl.splitter_state );
    settings.setValue ( "iface_name", _setup.hctl.iface_name );
    settings.setValue ( "elem_grp_name", _setup.hctl.elem_grp_name );
    settings.setValue ( "elem_grp_index", _setup.hctl.elem_grp_index );
    settings.endGroup ();
  }

  // Device selection
  {
    settings.beginGroup ( "device_selection" );
    settings.setValue ( "user_device", _setup.dev_select.user_device );
    settings.endGroup ();
  }
}

void
Main_Window::select_snd_ctl ( const QSnd::Ctl_Address & ctl_n )
{
  _mixer_hctl->set_mixer_dev_setup ( nullptr );
  _setup.mixer_dev.control_address = ctl_n;
  _mixer_hctl->set_mixer_dev_setup ( &_setup.mixer_dev );
}

void
Main_Window::reload_mixer_device ()
{
  _mixer_hctl->set_mixer_dev_setup ( nullptr );
  _mixer_hctl->set_mixer_dev_setup ( &_setup.mixer_dev );
}

void
Main_Window::refresh ()
{
  // std::cout << "Refresh" << "\n";
  _cards_db.reload ();
  _dev_select->reload_database ();
  reload_mixer_device ();
}

void
Main_Window::select_ctl_from_side_iface ()
{
  select_snd_ctl ( _dev_select->selected_ctl () );
}

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 ( _setup.show_dev_select != flag_n ) {
    _setup.show_dev_select = flag_n;
  }
  _act_show_dev_select->setChecked ( flag_n );
  _dev_select->setVisible ( flag_n );
}

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

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::show_dialog_about ()
{
  if ( _info_dialog == nullptr ) {
    Views::Info_Dialog * dlg = new Views::Info_Dialog ( this );
    dlg->setAttribute ( Qt::WA_DeleteOnClose );
    _info_dialog = dlg;
  }
  _info_dialog->show ();
}

void
Main_Window::show_dialog_about_qt ()
{
  QApplication::aboutQt ();
}

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::closeEvent ( QCloseEvent * event_n )
{
  save_state ();
  QMainWindow::closeEvent ( event_n );
}
