/*
  Copyright (c) 2000 Caldera Systems

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <qlabel.h>
#include <qlayout.h>
#include <qvbox.h>
#include <qpushbutton.h>
#include <qwidgetstack.h>
#include <qstatusbar.h>

#include <kapplication.h>
#include <kdialog.h>
#include <kglobal.h>
#include <kdebug.h>
#include <klocale.h>
#include <kmainwindow.h>
#include <kaction.h>
#include <kstdaction.h>
#include <kiconloader.h>
#include <kmessagebox.h>

#include "listview.h"
#include "toplevel.h"
#include "toplevel.moc"
#include "wizard.h"
#include "videocardpageimpl.h"
#include "quickhelp.h"
#include "keyboardpageimpl.h"
#include "mousepageimpl.h"
#include "monitorpageimpl.h"
#include "screenpageimpl.h"
#include "generalpageimpl.h"
#include "kxpage.h"
#include "kxfreeconfig.h"

TopLevel::TopLevel( KXFreeConfig *config, bool ownX, QWidget* parent, const char* name, WFlags flags)
    : KMainWindow(parent, name, flags | WType_TopLevel | WStyle_ContextHelp | WDestructiveClose),
      m_wizard(0),
      mConfig( config )
{
    setPlainCaption(i18n("XFree86 Configuration"));

    m_data = 0;
    m_pageHandler = 0;

    m_mainWidget = new QWidgetStack( this );
    setCentralWidget( m_mainWidget );

    // setup view
    QFrame *view = new QFrame( m_mainWidget );
    view->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);

    // setup layout
    QVBoxLayout* lay = new QVBoxLayout(view, KDialog::marginHint(), KDialog::marginHint());

    // setup header
    QHBox *header = new QHBox(view);
    header->setSpacing(10);

    // topic hbox
    QHBox *topic = new QHBox(header);

    // k label
    QLabel *k_label = new QLabel(topic);
    k_label->setText(i18n("<strong><large><large><large><large>K</large></large></large></large></strong>"));
    k_label->setAlignment(AlignBottom | AlignLeft);

    // x logo label
    QLabel *xlogo = new QLabel(topic);
    xlogo->setPixmap(KGlobal::iconLoader()->loadIcon("xlogo_small", KIcon::Desktop));
    xlogo->setAlignment(AlignBottom | AlignLeft);

    // config label
    QLabel *config_label = new QLabel(topic);
    config_label->setText(i18n("<strong><large><large><large><large>Config</large></large></large></large></strong>"));
    config_label->setAlignment(AlignBottom | AlignLeft);

    QLabel *subtopic = new QLabel(header);
    subtopic->setText(i18n("Advanced XFree86 Configuration"));
    subtopic->setAlignment(AlignBottom | AlignLeft);

    // spacer widget
    QWidget *spacer3 = new QWidget(topic);
    topic->setStretchFactor(spacer3, 10);

    // spacer widget
    QWidget *spacer4 = new QWidget(header);
    header->setStretchFactor(spacer4, 10);

    // setup items
    m_names.insert(iVideoCard, i18n("Video Card"));
    m_names.insert(iMonitor, i18n("Monitor"));
    m_names.insert(iKeyboard, i18n("Keyboard"));
    m_names.insert(iPointerDevice, i18n("Pointer Device"));
    m_names.insert(iScreen, i18n("Screen"));
    m_names.insert(iGeneral, i18n("General"));

    m_icons.insert(iVideoCard, "kcmpci");
    m_icons.insert(iMonitor, "kscreensaver");
    m_icons.insert(iKeyboard, "keyboard");
    m_icons.insert(iPointerDevice, "mouse");
    m_icons.insert(iScreen, "kwin");
    m_icons.insert(iGeneral, "kcontrol");


    // body hbox
    QHBox *body = new QHBox(view);

    // listview
    listview = new ListView(body);
    listview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
    body->setStretchFactor( listview, 30 );
    connect(listview, SIGNAL(itemSelected(ListViewItem*)), SLOT(itemSelected(ListViewItem*)));

    m_videoCardItem = listview->insertItem(iVideoCard, m_names[iVideoCard], m_icons[iVideoCard],
                                           i18n("Video card configuration. Select this option if you intend to add "
                                                "or remove a video card or to change the configuration of an existing one."));

    m_monitorItem = listview->insertItem(iMonitor, m_names[iMonitor], m_icons[iMonitor],
                                         i18n("Monitor configuration. Select this option if you intend to add "
                                              "or remove a monitor or to change the configuration of an existing one."));

    m_keyboardItem = listview->insertItem(iKeyboard, m_names[iKeyboard], m_icons[iKeyboard],
                                          i18n("Keyboard configuration. Select this option if you intend to add "
                                               "or remove a keyboard or to change the configuration of an existing one."));

    m_pointerItem = listview->insertItem(iPointerDevice, m_names[iPointerDevice], m_icons[iPointerDevice],
                                         i18n("Pointer device configuration. Select this option if you intend to add "
                                              "or remove a pointer device or to change the configuration of an existing one."));

    m_screenItem = listview->insertItem(iScreen, m_names[iScreen], m_icons[iScreen],
                                        i18n("Resolution and color depth configuration. Select this option if you intend to add "
                                             "or remove a resolution or to change the configuration of an existing one."));

    m_generalItem = listview->insertItem(iGeneral, m_names[iGeneral], m_icons[iGeneral],
                                         i18n("General settings. Select this option if you intend to modify "
                                              "general XF86 configuration options."));

    // help widget
    quickHelp = new QuickHelp(body);
    body->setStretchFactor( quickHelp, 10 );
    quickHelp->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
    quickHelp->setText(i18n("<large><p>Please select a configuration item from the list on the left and "
                            "follow the instructions in the quick help dialog on the right to setup "
                            "your X windowing system.</p>"
                            "<strong>Note that the settings you can configure in this application "
                            "are system wide settings</strong></large>"));

    // hline 1
    //QFrame *hline1 = new QFrame(view);
    //hline1->setFrameStyle(QFrame::HLine | QFrame::Sunken);

    // hline 2
    QFrame *hline2 = new QFrame(view);
    hline2->setFrameStyle(QFrame::HLine | QFrame::Sunken);

    // footer
    QHBox *footer = new QHBox(view);
    footer->setSpacing(10);

    m_statusBar = new QStatusBar( footer );
    m_statusBar->setSizeGripEnabled( false );

//    QWidget *spacer2 = new QWidget(footer);
    QPushButton *helpButton = new QPushButton(i18n("&Help"), footer);
    connect( helpButton, SIGNAL( clicked() ), this, SLOT( help() ) );
    if ( ownX )
	    helpButton->hide();

    m_quitButton = new QPushButton( footer );
    m_quitButton->setText( i18n("&Quit") );

    setupQuitButton( mConfig->configFile().isEmpty() );

//    footer->setStretchFactor(spacer2, 4);

    lay->addWidget(header);
    //lay->addWidget(hline1);
    lay->addWidget(body);
    lay->addWidget(hline2);
    lay->addWidget(footer);
    lay->setStretchFactor(listview, 10);

    m_mainWidget->addWidget( view, 1 );
    m_mainWidget->raiseWidget( view );

    m_introView = view;
}

TopLevel::~TopLevel()
{
}

void TopLevel::itemSelected(ListViewItem *item)
{
    kdDebug() << "Item " << item->id() << " selected!" << endl;

    if(!item) return;

    if ( m_data || m_pageHandler )
	    return;

    bool bVideoCard, bMonitor, bKeyboard, bPointerDevice, bScreen, bGeneral;
    bVideoCard = bMonitor = bKeyboard = bPointerDevice = bScreen = bGeneral = false;

    // figure out what pages are needed
    switch(item->id()) {
        case iVideoCard:
            bVideoCard = true;
            bMonitor = true;
            bScreen = true;
            break;
        case iMonitor:
            bMonitor = true;
            bScreen = true;
            break;
        case iKeyboard:
            bKeyboard = true;
            break;
        case iPointerDevice:
            bPointerDevice = true;
            break;
        case iScreen:
            bScreen = true;
            break;
        case iGeneral:
            bGeneral = true;
            break;
    }

    // setup wizard
    m_wizard = new Wizard( m_mainWidget );

    // video card page
    if (bVideoCard) {
        VideoCardPage *p = new VideoCardPage( m_wizard->viewport() );
        QPixmap icon = KGlobal::iconLoader()->loadIcon( item->icon(), KIcon::Desktop );
        p->setIcon(KGlobal::iconLoader()->loadIcon( m_icons[iVideoCard], KIcon::Desktop ));
        p->setCaption(m_names[iVideoCard]);
        m_wizard->appendPage(p);

        connect( p, SIGNAL( enableForward( bool ) ),
                 m_wizard, SLOT( setForwardEnabled( bool ) ) );
    }

    // monitor page
    if (bMonitor) {
        MonitorPage *p = new MonitorPage( m_wizard->viewport() );
        p->setIcon(KGlobal::iconLoader()->loadIcon( m_icons[iMonitor], KIcon::Desktop ));
        p->setCaption(m_names[iMonitor]);
        m_wizard->appendPage(p);

        connect( p, SIGNAL( enableForward( bool ) ),
                 m_wizard, SLOT( setForwardEnabled( bool ) ) );
        connect( p, SIGNAL( status( const QString & ) ),
                 m_wizard, SLOT( setStatus( const QString & ) ) );
    }

    // screen page
    if (bScreen) {
        ScreenPage *p = new ScreenPage( m_wizard->viewport() );
        p->setIcon(KGlobal::iconLoader()->loadIcon( m_icons[iScreen], KIcon::Desktop ));
        p->setCaption(m_names[iScreen]);
        m_wizard->appendPage(p);

        connect( p, SIGNAL( enableForward( bool ) ),
                 m_wizard, SLOT( setForwardEnabled( bool ) ) );
        connect( p, SIGNAL( status( const QString & ) ),
                 m_wizard, SLOT( setStatus( const QString & ) ) );
    }

    // keyboard page
    if (bKeyboard) {
        KeyboardPage *p = new KeyboardPage( m_wizard->viewport() );
        p->setIcon(KGlobal::iconLoader()->loadIcon( m_icons[iKeyboard], KIcon::Desktop ));
        p->setCaption(m_names[iKeyboard]);
        m_wizard->appendPage(p);

        connect( p, SIGNAL( enableForward( bool ) ),
                 m_wizard, SLOT( setForwardEnabled( bool ) ) );

        connect( m_wizard, SIGNAL( reject() ),
                 p, SLOT( revert() ) );
    }

    // pointer device page
    if (bPointerDevice) {
        MousePage *p = new MousePage( m_wizard->viewport() );
        p->setIcon(KGlobal::iconLoader()->loadIcon( m_icons[iPointerDevice], KIcon::Desktop ));
        p->setCaption(m_names[iPointerDevice]);
        m_wizard->appendPage(p);

        connect( p, SIGNAL( enableForward( bool ) ),
                 m_wizard, SLOT( setForwardEnabled( bool ) ) );
    }

    // general page
    if (bGeneral) {
        GeneralPage *p = new GeneralPage( m_wizard->viewport() );
        p->setIcon( KGlobal::iconLoader()->loadIcon( m_icons[iGeneral], KIcon::Desktop ) );
        p->setCaption( m_names[ iGeneral ] );
        m_wizard->appendPage( p );

        connect( p, SIGNAL( enableForward( bool ) ),
                 m_wizard, SLOT( setForwardEnabled( bool ) ) );
    }

    m_wizard->setForwardEnabled( false ); // initial setting

    m_data = KXData::global()->clone();

    m_pageHandler = new KXPageHandler;

    connect (m_pageHandler, SIGNAL( enableForward( bool ) ),
             m_wizard, SLOT( setForwardEnabled( bool ) ) );

    QPtrList<QWidget> pages = m_wizard->pages();
    QPtrListIterator<QWidget> it( pages );
    for (; it.current(); ++it )
    {
        KXPage *page = dynamic_cast<KXPage *>( it.current() );
        assert( page );
        page->setData( m_data );
        m_pageHandler->addPage( it.current() );
    }

    m_mainWidget->addWidget( m_wizard, 2 );
    m_mainWidget->raiseWidget( m_wizard );

    connect( m_wizard, SIGNAL( reject() ),
             this, SLOT( slotWizardRejected() ) );

    connect( m_wizard, SIGNAL( accept() ),
             this, SLOT( slotWizardAccepted() ) );
}

void TopLevel::slotWizardRejected()
{
    delete m_wizard;
    delete m_data;
    delete m_pageHandler;
    m_wizard = 0; // paranoia :)
    m_data = 0;
    m_pageHandler = 0;
    m_mainWidget->raiseWidget( m_introView );
}

void TopLevel::slotWizardAccepted()
{
//    kdDebug() << "TopLevel::slotWizardAccepted()" << endl;

    // save changes before deleting things

    QPtrList<QWidget> pages = m_wizard->pages();
    QPtrListIterator<QWidget> it( pages );
    for (; it.current(); ++it )
    {
        KXPage *page = dynamic_cast<KXPage *>( it.current() );
        if ( page )
            page->savePage();
        else
            kdDebug() << "grmbllll : " << it.current()->className() << " doesn't inherit from KXPage" << endl;
    }

    KXData::global()->copyFrom( m_data );

    // Config changed, save before quitting
    setupQuitButton( true );

    delete m_wizard;
    delete m_data;
    delete m_pageHandler;
    m_wizard = 0;
    m_data = 0;
    m_pageHandler = 0;
    updateItems();
    m_mainWidget->raiseWidget( m_introView );
}

void TopLevel::updateItems()
{
    m_introView->setUpdatesEnabled( false );

    m_videoCardItem->setComment( i18n( "Type: %1" ).arg( KXData::global()->currentVideoCards()[ 0 ].name() ) );

    KXMonitorData monitor = KXData::global()->currentMonitors()[ 0 ];
    if ( monitor.size() > 0 )
        m_monitorItem->setComment( i18n( "Name: %1 - %2, "
                                         "Size: %3" ).arg( monitor.vendor() ).arg( monitor.model() ).arg( monitor.size() ) );
    else
        m_monitorItem->setComment( i18n( "Name: %1 - %2" ).arg( monitor.vendor() ).arg( monitor.model() ) );

    KXKeyboardData keyboard = KXData::global()->currentKeyboards()[ 0 ];

    m_keyboardItem->setComment( i18n( "Model: %1, "
                                      "Layout: %2" ).arg( keyboard.model().name() ).arg( keyboard.layout().name() ) );

    KXPointerData pointer = KXData::global()->currentPointers()[ 0 ];

    if ( !pointer.port().isEmpty() )
        m_pointerItem->setComment( i18n( "Type: %1, "
                                         "Model: %2, "
                                         "Port: %3" ).arg( KXData::pointerClassToName( pointer.pointerModel().pointerClass() ) )
                                   .arg( pointer.pointerModel().name() )
                                   .arg( pointer.port() ) );
    else
        m_pointerItem->setComment( i18n( "Type: %1, "
                                         "Model: %2" ).arg( KXData::pointerClassToName( pointer.pointerModel().pointerClass() ) )
                                   .arg( pointer.pointerModel().name() ) );

    QString comment;

    // ### bounds checks!
    KXScreenData screen = KXData::global()->currentScreens()[ 0 ];

    if ( screen.displays().count() > 0 && screen.displays()[ 0 ].modes().count() > 0 )
    {
        KXDisplayData currentDisplay = screen.displays()[ 0 ];
        QString currentModeName = currentDisplay.modes()[ 0 ];

        // find matching KXModeData object
        KXModeData mode;
        bool foundMode = false;

        KXModeDataList modes = KXData::global()->currentModes();
        KXModeDataList::ConstIterator it = modes.begin();
        KXModeDataList::ConstIterator end = modes.end();
        for (; it != end; ++it )
        {
            if ( (*it).name() == currentModeName )
            {
                foundMode = true;
                mode = *it;
                break;
            }
        }

        if ( foundMode )
        {
            QSize virtualResolution = currentDisplay.virtualResolution();

            if ( virtualResolution.width() == 0 ||
                 virtualResolution.height() == 0 )
                comment = i18n( "Resolution: %1x%2/%3Hz, "
                                "Color Depth: %4" ).arg( mode.xResolution() ).arg( mode.yResolution() ).arg( mode.vRefresh() )
                          .arg( screen.defaultDepth() );
            else
                comment = i18n( "Resolution: %1x%2/%3Hz, "
                                "Color Depth: %4, "
                                "Virtual Resolution: %5x%6" ).arg( mode.xResolution() ).arg( mode.yResolution() ).arg( mode.vRefresh() )
                          .arg( screen.defaultDepth() ).arg( currentDisplay.virtualResolution().width() )
                          .arg( currentDisplay.virtualResolution().height() );
        }
    }

    m_screenItem->setComment( comment );

    m_generalItem->setComment( i18n( "You can configure general options here." ) );

    m_introView->setUpdatesEnabled( true );
}

void TopLevel::help()
{
    kapp->invokeHelp();
}

void TopLevel::saveAndQuit()
{
//    kdDebug() << "TopLevel::saveAndQuit()" << endl;

    if ( KMessageBox::questionYesNo( this,
                                     i18n( "KXConfig has created a new configuration. Do you want to save it?" ) ) == KMessageBox::Yes )
    {
        // all changes are in KXData -> now write out the new config
        if ( mConfig->saveConfig() )
        {
            m_statusBar->message( i18n( "New configuration successfully saved." ), 10*100 );

            KMessageBox::information( this, i18n( "The new configuration was successfully saved.\n"
                                                  "You will have to restart your session for the changes\n"
                                                  "to take effect!" ), QString::null,
                                      QString::fromLatin1( "ShowRestartSessionHint" ) );
        } else {
            kdDebug() << "Couldn't save config file." << endl;
        }
    }

    kapp->quit();
}

void TopLevel::setupQuitButton( bool saveAndQuit )
{

    disconnect( m_quitButton, SIGNAL( clicked() ), kapp, SLOT( quit() ) );
    disconnect( m_quitButton, SIGNAL( clicked() ), this, SLOT( saveAndQuit() ) );

    if ( saveAndQuit )
        connect( m_quitButton, SIGNAL(clicked()), this, SLOT(saveAndQuit()));
    else
        connect( m_quitButton, SIGNAL(clicked()), kapp, SLOT(quit()));

}
