// (C) 2005 Max Howell (max.howell@methylblue.com)
// See COPYING file for licensing information

#include <tdeapplication.h> // XineConfigDialog::ctor -> to get the iconloader
#include <kcombobox.h>
#include <kiconloader.h>  // XineConfigDialog::ctor
#include <klineedit.h>
#include <kseparator.h>
#include <kstdguiitem.h>
#include <tqcheckbox.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqscrollview.h>
#include <tqspinbox.h>
#include <tqtabwidget.h>
#include <tqtooltip.h>
#include <tqvbox.h>
#include <xine.h>

#include "../debug.h"
#include "xineConfig.h"


TQString i18n(const char *text);


KDialogBase *XineConfigDialog::s_instance = 0;


namespace Codeine
{
   void
   showXineConfigurationDialog( TQWidget *parent, xine_t *xine )
   {
      XineConfigDialog d( xine, parent );
      if( d.exec() == TQDialog::Accepted )
         d.saveSettings();
   }
}


class TabWidget : public TQTabWidget
{
public:
   TabWidget( TQWidget *parent ) : TQTabWidget( parent ) {}

   virtual TQSize sizeHint() const
   {
      // TQt gives a stupid default sizeHint for this widget
      return TQSize(
            reinterpret_cast<TQWidget*>(tabBar())->sizeHint().width() + 5,
            TQTabWidget::sizeHint().height() );
   }
};


///@class XineConfigDialog

XineConfigDialog::XineConfigDialog( xine_t *xine, TQWidget *parent )
      : KDialogBase( parent, "xine_config_dialog",
               true, //modal
               i18n("Configure xine"), User1 | Stretch | Ok | Cancel,
               Ok, //default button
               false, //draw separator
               KStdGuiItem::reset() )
      , m_xine( xine )
{
   DEBUG_BLOCK

   s_instance = this;
   const int METRIC = fontMetrics().width( 'x' );
   const int METRIC_3B2 = (3*METRIC)/2;

   TQVBox *box = new TQVBox( this );
   box->setSpacing( METRIC );
   setMainWidget( box );

   {
      TQHBox *hbox = new TQHBox( box );
      hbox->setSpacing( METRIC_3B2 );
      hbox->setMargin( METRIC_3B2 );
      TQPixmap info = kapp->iconLoader()->loadIcon( "messagebox_info", TDEIcon::NoGroup, TDEIcon::SizeMedium, TDEIcon::DefaultState, 0, true );
      TQLabel *label = new TQLabel( hbox );
      label->setPixmap( info );
      label->setSizePolicy( TQSizePolicy::Maximum, TQSizePolicy::Maximum );
      label = new TQLabel( i18n(
            "xine's defaults are usually sensible and should not require modification. "
            "However, full configurability is provided for your pleasure ;-)." ), hbox );
      label->setAlignment( TQLabel::WordBreak | TQLabel::AlignVCenter );
   }

   //FIXME after many hours I have discovered that this
   // widget somehow sets the minSize of this widget to 0,0
   // whenever you resize the widget. WTF?
   TabWidget *tabs = new TabWidget( box );


   class XineConfigEntryIterator {
      xine_t *m_xine;
      xine_cfg_entry_t m_entry;
      bool m_valid;
   public:
      XineConfigEntryIterator( xine_t *xine ) : m_xine( xine ) { m_valid = xine_config_get_first_entry( m_xine, &m_entry ); }
      inline XineConfigEntryIterator &operator++() { m_valid = xine_config_get_next_entry( m_xine, &m_entry ); return *this; }
      inline xine_cfg_entry_t *operator*() { return m_valid ? &m_entry : 0; }
   };


   TQGridLayout *grid = 0;
   TQString currentPage;
   TQScrollView *view = 0;
   parent = 0;

   for( XineConfigEntryIterator it( m_xine ); *it; ++it )
   {
      const TQString pageName = TQString::fromUtf8( (*it)->key ).section( '.', 0, 0 );

      if( (TQStringList() << "ui" << "effects" << "subtitles").contains( pageName ) )
         continue;

      if( pageName != currentPage ) {
         if( view )
            //NOTE won't be executed for last tab
            view->viewport()->setMinimumWidth( grid->sizeHint().width() ); // seems necessary

         TQString pageTitle = pageName;
         pageTitle[0] = pageTitle[0].upper();

         tabs->addTab( view = new TQScrollView, pageTitle );
         view->setResizePolicy( TQScrollView::AutoOneFit );
         view->setHScrollBarMode( TQScrollView::AlwaysOff );
         view->setFrameShape( TQFrame::NoFrame );
         view->addChild( parent = new TQWidget( view->viewport() ) );

         TQBoxLayout *layout = new TQVBoxLayout( parent, /*margin*/METRIC_3B2, /*spacing*/0 );

         parent = new TQFrame( parent );
         static_cast<TQFrame*>(parent)->setFrameStyle( TQFrame::Panel | TQFrame::Raised );
         static_cast<TQFrame*>(parent)->setLineWidth( 2 );
         grid = new TQGridLayout( parent, /*rows*/0, /*cols*/2, /*margin*/20, /*spacing*/int(METRIC*2.5) );
         grid->setColStretch( 0, 3 );
         grid->setColStretch( 1, 2 );

         layout->addWidget( parent, 0 );
         layout->addStretch( 1 );

         currentPage = pageName;
      }

      m_entrys.append( new XineConfigEntry( parent, grid, *it ) );
   }

   //finishing touches
   m_entrys.setAutoDelete( true );
   enableButton( Ok, false );
   enableButton( User1, false );

   Q_ASSERT( !isUnsavedSettings() );
}

void
XineConfigDialog::slotHelp()
{
   /// HACK called when a widget's input value changes

   const bool b = isUnsavedSettings();
   enableButton( Ok, b );
   enableButton( User1, b );
}

void
XineConfigDialog::slotUser1()
{
   for( TQPtrListIterator<XineConfigEntry> it( m_entrys ); *it != 0; ++it )
      (*it)->reset();

   slotHelp();
}

bool
XineConfigDialog::isUnsavedSettings() const
{
   for( TQPtrListIterator<XineConfigEntry> it( m_entrys ); *it != 0; ++it )
      if( (*it)->isChanged() )
         return true;

   return false;
}

#include <tqdir.h>
void
XineConfigDialog::saveSettings()
{
   for( XineConfigEntry *entry = m_entrys.first(); entry; entry = m_entrys.next() )
      if( entry->isChanged() )
         entry->save( m_xine );

   xine_config_save( m_xine, TQFile::encodeName( TQDir::homeDirPath() + "/.xine/config" ) );
}


///@class XineConfigEntry

XineConfigEntry::XineConfigEntry( TQWidget *parent, TQGridLayout *grid, xine_cfg_entry_t *entry )
      : m_widget( 0 )
      , m_key( entry->key )
      , m_string( entry->str_value )
      , m_number( entry->num_value )
{
   TQWidget *&w = m_widget;
   const char *signal = 0;
   const int row = grid->numRows();

   TQString description_text = TQString::fromUtf8( entry->description );
   description_text[0] = description_text[0].upper();

   switch( entry->type )
   {
   case XINE_CONFIG_TYPE_STRING: {
      w = new KLineEdit( m_string, parent );
      signal = SIGNAL(textChanged( const TQString& ));
      break;
   }
   case XINE_CONFIG_TYPE_ENUM: {
      w = new KComboBox( parent );
      for( int i = 0; entry->enum_values[i]; ++i )
         ((KComboBox*)w)->insertItem( TQString::fromUtf8( entry->enum_values[i] ) );
      ((KComboBox*)w)->setCurrentItem( m_number );
      signal = SIGNAL(activated( int ));
      break;
   }
   case XINE_CONFIG_TYPE_RANGE:
   case XINE_CONFIG_TYPE_NUM: {
      w = new TQSpinBox(
               TQMIN( m_number, entry->range_min ), // xine bug, sometimes the min and max ranges
               TQMAX( m_number, entry->range_max ), // are both 0 even though this is bullshit
               1, parent );
      ((TQSpinBox*)w)->setValue( m_number );
      signal = SIGNAL(valueChanged( int ));
      break;
   }
   case XINE_CONFIG_TYPE_BOOL: {
      w = new TQCheckBox( description_text, parent );
      ((TQCheckBox*)w)->setChecked( m_number );

      connect( w, SIGNAL(toggled( bool )), XineConfigDialog::instance(), SLOT(slotHelp()) );
      TQToolTip::add( w, "<qt>" + TQString::fromUtf8( entry->help ) );
      grid->addMultiCellWidget( w, row, row, 0, 1 );
      return; //no need for a description label
   }
   default:
      ;
   }

   connect( w, signal, XineConfigDialog::instance(), SLOT(slotHelp()) );

   TQLabel *description = new TQLabel( description_text + ':', parent );
   description->setAlignment( TQLabel::WordBreak | TQLabel::AlignVCenter );

   const TQString tip = "<qt>" + TQString::fromUtf8( entry->help );
   TQToolTip::add( w, tip );
   TQToolTip::add( description, tip );

//   grid->addWidget( description, row, 0, TQt::AlignVCenter );
   grid->addWidget( w, row, 1, TQt::AlignTop );
}

bool
XineConfigEntry::isChanged() const
{
   #define _( x ) static_cast<x*>(m_widget)

   switch( classType( m_widget->className() ) ) {
      case LineEdit: return _(KLineEdit)->text().utf8() != m_string;
      case ComboBox: return _(KComboBox)->currentItem() != m_number;
      case SpinBox:  return _(TQSpinBox)->value() != m_number;
      case CheckBox: return _(TQCheckBox)->isChecked() != m_number;
   }
   return false;
}

void
XineConfigEntry::reset()
{
   // this is because we only get called by the XineConfigDialog reset button
   // and we don't want to cause a check for Ok/Reset button enabled state for
   // every XineConfigEntry
   m_widget->blockSignals( true );

   switch( classType( m_widget->className() ) ) {
      case LineEdit: _(KLineEdit)->setText( m_string ); break;
      case ComboBox: _(KComboBox)->setCurrentItem( m_number ); break;
      case SpinBox:  _(TQSpinBox)->setValue( m_number ); break;
      case CheckBox: _(TQCheckBox)->setChecked( (bool)m_number ); break;
   }
   m_widget->blockSignals( false );
}

void
XineConfigEntry::save( xine_t *xine )
{
   xine_cfg_entry_t ent;

   if( xine_config_lookup_entry( xine, key(), &ent ) )
   {
      switch( classType( m_widget->className() ) ) {
         case LineEdit: m_string = _(KLineEdit)->text().utf8(); break;
         case ComboBox: m_number = _(KComboBox)->currentItem(); break;
         case SpinBox:  m_number = _(TQSpinBox)->value(); break;
         case CheckBox: m_number = _(TQCheckBox)->isChecked(); break;
      }

      ent.str_value = tqstrdup( m_string );
      ent.num_value = m_number;

      debug() << "Saving setting: " << key() << endl;
      xine_config_update_entry( xine, &ent );
   }
   else
      Debug::warning() << "Couldn't save: " << key() << endl;

   #undef _
}
