#include "mixerwindow.h"
#include "preferencesdlg.h"
#include "devicewidget.h"
#include "../model/pulsemodel.h"

#include <tqlayout.h>
#include <tqlabel.h>
#include <tqscrollview.h>
#include <tqwidgetstack.h>
#include <tqtoolbutton.h>
#include <tqtooltip.h>
#include <tqevent.h>
#include <ktabwidget.h>
#include <tdelocale.h>
#include <kiconloader.h>
#include <tdeglobal.h>
#include <tdestandarddirs.h>
#include <tdeaction.h>
#include <tdeactioncollection.h>
#include <tdeglobalaccel.h>
#include <kkeydialog.h>
#include <tdemenubar.h>
#include <tdepopupmenu.h>
#include <tdeaboutapplication.h>
#include <tdeaboutdata.h>
#include <tdeapplication.h>
#include <twin.h>
#include "tmixtray.h"
#include "devicespage.h"
#include <tdeconfig.h>
#include <tdemessagebox.h>
#include <kstdguiitem.h>

// ---- tab helper -------------------------------------------------------------

static MixerWindow::Tab makeTab( KTabWidget *tabs, const TQString &label )
{
    MixerWindow::Tab t;

    TQScrollView *sv = new TQScrollView( tabs );
    sv->setFrameStyle( TQFrame::NoFrame );
    sv->setHScrollBarMode( TQScrollView::Auto );
    sv->setVScrollBarMode( TQScrollView::AlwaysOff );
    sv->setResizePolicy( TQScrollView::AutoOneFit );

    t.page = new TQWidget( sv->viewport() );
    sv->addChild( t.page, 0, 0 );

    t.layout = new TQHBoxLayout( t.page, 6, 4 );
    t.layout->addStretch( 1 );
    t.scroll = sv;
    t.count = 0;

    tabs->addTab( sv, label );
    return t;
}

MixerWindow::MixerWindow( PulseModel *model, TQWidget *parent )
    : TDEMainWindow(parent), m_model(model), m_defaultDevice(0), m_globalAccel(0),
      m_prefsDlg(0), m_devicesBtn(0), m_devicesPage(0), m_devicesInTabs(false),
      m_quitting(false), m_totalDevCount(0), m_waitLabel(0)
{
    setPlainCaption( i18n("Volume Control") );
    setIcon( TDEGlobal::iconLoader()->loadIcon( "kmix", TDEIcon::Desktop, 48 ) );

    // ---- menu bar -----------------------------------------------------------
    TDEPopupMenu *fileMenu = new TDEPopupMenu( this );
    fileMenu->insertItem( SmallIcon("application-exit"), i18n("&Quit"), this, TQ_SLOT(quit()) );

    TDEPopupMenu *settingsMenu = new TDEPopupMenu( this );
    settingsMenu->insertItem( SmallIcon("configure"), i18n("&Configure TMix..."),
                              this, TQ_SLOT(showPreferences()) );
    settingsMenu->insertItem( SmallIcon("configure"), i18n("Configure &Global Shortcuts..."),
                              this, TQ_SLOT(slotConfigureGlobalShortcuts()) );

    TDEPopupMenu *helpMenu = new TDEPopupMenu( this );
    helpMenu->insertItem( SmallIcon("help"), i18n("&About TMix"), this, TQ_SLOT(showAbout()) );

    menuBar()->insertItem( i18n("&File"), fileMenu );
    menuBar()->insertItem( i18n("&Settings"), settingsMenu );
    menuBar()->insertItem( i18n("&Help"), helpMenu );

    // ---- system tray --------------------------------------------------------
    m_tray = new TmixTray( this );
    m_tray->setModel( model );
    connect( model, TQ_SIGNAL(defaultOutputChanged(AudioDevice*)),
             this,  TQ_SLOT(onDefaultOutputChanged(AudioDevice*)) );
    onDefaultOutputChanged( model->defaultOutput() );

    TDEConfig *cfg = TDEGlobal::config();
    cfg->setGroup("Tray");
    if ( cfg->readBoolEntry("DockInTray", true) )
        m_tray->show();

    // ---- global hotkeys -----------------------------------------------------
    m_globalAccel = new TDEGlobalAccel( this, "TMix" );
    m_globalAccel->insert( "Increase volume", i18n("Increase Volume"), TQString(),
        TDEShortcut(), TDEShortcut(), this, TQ_SLOT(slotVolumeUp()) );
    m_globalAccel->insert( "Decrease volume", i18n("Decrease Volume"), TQString(),
        TDEShortcut(), TDEShortcut(), this, TQ_SLOT(slotVolumeDown()) );
    m_globalAccel->insert( "Toggle mute", i18n("Toggle Mute"), TQString(),
        TDEShortcut(), TDEShortcut(), this, TQ_SLOT(slotToggleMute()) );
    m_globalAccel->insert( "Toggle input mute", i18n("Toggle Mute of Microphone"), TQString(),
        TDEShortcut(), TDEShortcut(), this, TQ_SLOT(slotToggleInputMute()) );
    m_globalAccel->readSettings();
    m_globalAccel->updateConnections();

    // ---- central widget -----------------------------------------------------
    TQWidget *central = new TQWidget( this );
    setCentralWidget( central );
    setMinimumSize( 300, 200 );

    TQVBoxLayout *top = new TQVBoxLayout( central, 0, 0 );

    // Outer stack: page 0 = mixer, page 1 = devices (no-tab mode), page 2 = waiting
    m_outerStack = new TQWidgetStack( central );
    top->addWidget( m_outerStack, 1 );

    TQWidget *mixerPage = new TQWidget( m_outerStack );
    TQVBoxLayout *mixerLayout = new TQVBoxLayout( mixerPage, 0, 0 );
    m_outerStack->addWidget( mixerPage, 0 );

    // Page 2: shown when PA is not yet connected / no devices present
    TQWidget *waitPage = new TQWidget( m_outerStack );
    TQVBoxLayout *waitLayout = new TQVBoxLayout( waitPage, 0, 0 );
    m_waitLabel = new TQLabel( i18n("Waiting for PulseAudio to start..."), waitPage );
    m_waitLabel->setAlignment( TQt::AlignCenter );
    waitLayout->addWidget( m_waitLabel );
    m_outerStack->addWidget( waitPage, 2 );

    m_outerStack->raiseWidget( 2 );  // start on waiting page; switch to 0 on first device

    m_stack = new TQWidgetStack( mixerPage );
    mixerLayout->addWidget( m_stack );

    // Page 0: tabbed view
    m_tabs = new KTabWidget( m_stack );
    m_stack->addWidget( m_tabs, 0 );

    m_output    = makeTab( m_tabs, i18n("Output") );
    m_input     = makeTab( m_tabs, i18n("Input") );
    m_playback  = makeTab( m_tabs, i18n("Playback") );
    m_recording = makeTab( m_tabs, i18n("Recording") );

    // Page 1: all-in-one flat view (scrollable)
    {
        TQScrollView *sv = new TQScrollView( m_stack );
        sv->setFrameStyle( TQFrame::NoFrame );
        sv->setHScrollBarMode( TQScrollView::Auto );
        sv->setVScrollBarMode( TQScrollView::AlwaysOff );
        sv->setResizePolicy( TQScrollView::AutoOneFit );
        m_allPage = sv;

        m_allStrip = new TQWidget( sv->viewport() );
        sv->addChild( m_allStrip, 0, 0 );

        m_allLayout = new TQHBoxLayout( m_allStrip, 2, 4 );
        m_allLayout->addStretch( 1 );

        m_allCount = 0;
        m_stack->addWidget( sv, 1 );
    }

    // Floating toggle button — child of central widget so TQWidgetStack page
    // switches don't hide it; positioned over m_outerStack's bottom-right corner.
    m_devicesBtn = new TQToolButton( central );
    m_devicesBtn->setIconSet( SmallIconSet("configure") );
    m_devicesBtn->resize( 24, 24 );
    TQToolTip::add( m_devicesBtn, i18n("Device Configuration") );
    connect( m_devicesBtn, TQ_SIGNAL(clicked()), this, TQ_SLOT(toggleDevices()) );
    central->installEventFilter( this );

    // DevicesPage — placement depends on tab/no-tab mode (set below)
    m_devicesPage = new DevicesPage( model, this );

    {
        TDEConfig *c = TDEGlobal::config();
        c->setGroup("View");
        bool noTabs = c->readBoolEntry("NoTabs", false);
        m_stack->raiseWidget( noTabs ? 1 : 0 );
        // Place DevicesPage in the right home for the initial mode
        if ( noTabs ) {
            m_devicesPage->reparent( m_outerStack, 0, TQPoint(), false );
            m_outerStack->addWidget( m_devicesPage, 1 );
            m_devicesInTabs = false;
        } else {
            m_devicesPage->reparent( m_tabs, 0, TQPoint(), false );
            m_tabs->addTab( m_devicesPage, i18n("Devices") );
            m_devicesInTabs = true;
        }
    }

    setAutoSaveSettings( "MainWindow" );

    // Position button after layout is set; hide if in tab mode (has its own tab)
    repositionDevicesBtn();
    m_devicesBtn->setShown( !m_devicesInTabs );

    connect( model, TQ_SIGNAL(deviceAdded(AudioDevice*)),   this, TQ_SLOT(onDeviceAdded(AudioDevice*)) );
    connect( model, TQ_SIGNAL(deviceRemoved(AudioDevice*)), this, TQ_SLOT(onDeviceRemoved(AudioDevice*)) );
    connect( tqApp, TQ_SIGNAL(aboutToQuit()), this, TQ_SLOT(onAppAboutToQuit()) );

    // Populate with any devices already known at construction time.
    AudioDevice::Category cats[] = {
        AudioDevice::Output, AudioDevice::Input,
        AudioDevice::Playback, AudioDevice::Recording
    };
    for ( int i = 0; i < 4; i++ ) {
        TQPtrList<AudioDevice> devs = model->devices( cats[i] );
        for ( TQPtrListIterator<AudioDevice> it(devs); *it; ++it )
            onDeviceAdded( *it );
    }
}

bool MixerWindow::queryClose()
{
    if ( m_quitting || tdeApp->sessionSaving() )
        return true;
    TDEConfig *cfg = TDEGlobal::config();
    cfg->setGroup("Tray");
    if ( cfg->readBoolEntry("DockInTray", true) ) {
        saveMainWindowSettings( TDEGlobal::config(), "MainWindow" );
        TDEConfig *c = TDEGlobal::config();
        c->setGroup("MainWindow");
        c->writeEntry("XPos", x());
        c->writeEntry("YPos", y());
        c->sync();
        hide();
        return false;
    }
    // Not in systray — show confirm if needed, then let TDE close the window
    // normally (returning true). TDE quits the app when the last main window closes.
    TDEConfig *gcfg = TDEGlobal::config();
    gcfg->setGroup("General");
    if ( gcfg->readBoolEntry("ConfirmQuit", false) ) {
        int r = KMessageBox::questionYesNo(
            this,
            i18n("Are you sure you want to quit TMix?"),
            i18n("Quit TMix"),
            KStdGuiItem::quit(),
            KStdGuiItem::cancel() );
        if ( r != KMessageBox::Yes )
            return false;
    }
    m_quitting = true;
    return true;
}


void MixerWindow::bringToFront()
{
    show();
    KWin::forceActiveWindow( winId() );
}

void MixerWindow::showDevices()
{
    if ( m_devicesInTabs ) {
        m_outerStack->raiseWidget( 0 );
        m_tabs->showPage( m_devicesPage );
    } else {
        m_outerStack->raiseWidget( 1 );
        updateDevicesButton();
        repositionDevicesBtn();
        m_devicesBtn->raise();
    }
    bringToFront();
}

void MixerWindow::showAbout()
{
    TDEAboutData about(
        "tmix", I18N_NOOP("TMix"), "0.1",
        I18N_NOOP("Volume control for TDE / PulseAudio"),
        TDEAboutData::License_GPL,
        "(c) 2026", 0, 0, 0 );
    TDEAboutApplication dlg( &about, this );
    dlg.exec();
}

MixerWindow::Tab &MixerWindow::tabForCategory( AudioDevice::Category cat )
{
    switch ( cat ) {
        case AudioDevice::Output:    return m_output;
        case AudioDevice::Input:     return m_input;
        case AudioDevice::Playback:  return m_playback;
        case AudioDevice::Recording: return m_recording;
    }
    return m_output;
}

static void updateSeparators( TQHBoxLayout *layout )
{
    TQLayoutIterator it = layout->iterator();
    DeviceWidget *last = 0;
    while ( TQLayoutItem *item = it.current() ) {
        DeviceWidget *dw = dynamic_cast<DeviceWidget*>( item->widget() );
        if ( dw ) {
            if ( last ) last->setSeparatorVisible( true );
            last = dw;
        }
        ++it;
    }
    if ( last ) last->setSeparatorVisible( false );
}

void MixerWindow::onDeviceAdded( AudioDevice *dev )
{
    if ( m_totalDevCount == 0 )
        m_outerStack->raiseWidget( 0 );
    m_totalDevCount++;

    if ( m_stack->id( m_stack->visibleWidget() ) == 1 ) {
        TQWidget *w = dev->createWidget( m_allStrip );
        m_allLayout->insertWidget( m_allCount, w );
        m_allCount++;
        w->show();
        m_widgets.insert( dev, w );
        updateSeparators( m_allLayout );
    } else {
        Tab &t = tabForCategory( dev->category() );
        TQWidget *w = dev->createWidget( t.page );
        t.layout->insertWidget( t.count, w );
        t.count++;
        w->show();
        m_widgets.insert( dev, w );
        updateSeparators( t.layout );
    }
}


void MixerWindow::onDefaultOutputChanged( AudioDevice *dev )
{
    m_defaultDevice = dev;
    m_tray->setDevice( dev );
}

void MixerWindow::slotVolumeUp()
{
    if ( !m_defaultDevice ) return;
    TDEConfig *cfg = TDEGlobal::config();
    cfg->setGroup("Tray");
    int step = cfg->readNumEntry("ScrollStep", 5);
    int v = m_defaultDevice->volume() + step;
    m_defaultDevice->setVolume( v > 100 ? 100 : v );
}

void MixerWindow::slotVolumeDown()
{
    if ( !m_defaultDevice ) return;
    TDEConfig *cfg = TDEGlobal::config();
    cfg->setGroup("Tray");
    int step = cfg->readNumEntry("ScrollStep", 5);
    int v = m_defaultDevice->volume() - step;
    m_defaultDevice->setVolume( v < 0 ? 0 : v );
}

void MixerWindow::slotToggleMute()
{
    if ( !m_defaultDevice ) return;
    m_defaultDevice->setMuted( !m_defaultDevice->muted() );
}

void MixerWindow::slotToggleInputMute()
{
    AudioDevice *dev = m_model->defaultInput();
    if ( !dev ) return;
    dev->setMuted( !dev->muted() );
}

void MixerWindow::slotConfigureGlobalShortcuts()
{
    KKeyDialog::configure( m_globalAccel, 0, false );
    m_globalAccel->writeSettings();
    m_globalAccel->updateConnections();
}

void MixerWindow::onDeviceRemoved( AudioDevice *dev )
{
    TQWidget *w = m_widgets[dev];
    if ( !w ) return;
    if ( m_stack->id( m_stack->visibleWidget() ) == 1 ) {
        m_allLayout->remove( w );
        m_allCount--;
        m_widgets.remove( dev );
        delete w;
        updateSeparators( m_allLayout );
    } else {
        Tab &t = tabForCategory( dev->category() );
        t.layout->remove( w );
        t.count--;
        m_widgets.remove( dev );
        delete w;
        updateSeparators( t.layout );
    }
    m_totalDevCount--;
    if ( m_totalDevCount == 0 )
        m_outerStack->raiseWidget( 2 );
}


void MixerWindow::quit()
{
    TDEConfig *cfg = TDEGlobal::config();
    cfg->setGroup("General");
    if ( cfg->readBoolEntry("ConfirmQuit", false) ) {
        int r = KMessageBox::questionYesNo(
            this,
            i18n("Are you sure you want to quit TMix?"),
            i18n("Quit TMix"),
            KStdGuiItem::quit(),
            KStdGuiItem::cancel() );
        if ( r != KMessageBox::Yes )
            return;
    }
    m_quitting = true;
    tqApp->quit();
}

void MixerWindow::onAppAboutToQuit()
{
    m_quitting = true;
}

void MixerWindow::showPreferences()
{
    if ( !m_prefsDlg ) {
        m_prefsDlg = new PreferencesDlg( this );
        connect( m_prefsDlg, TQ_SIGNAL(settingsChanged()), this, TQ_SLOT(applySettings()) );
    }
    m_prefsDlg->load();
    m_prefsDlg->show();
    m_prefsDlg->raise();
    m_prefsDlg->setActiveWindow();
}

void MixerWindow::rebuildView()
{
    // Tear down all existing device widgets
    for ( TQMap<AudioDevice*, TQWidget*>::iterator it = m_widgets.begin(); it != m_widgets.end(); ++it )
        delete it.data();
    m_widgets.clear();
    m_output.count = m_input.count = m_playback.count = m_recording.count = 0;
    m_allCount = 0;

    TDEConfig *cfg = TDEGlobal::config();
    cfg->setGroup("View");
    bool noTabs = cfg->readBoolEntry("NoTabs", false);
    m_stack->raiseWidget( noTabs ? 1 : 0 );

    if ( noTabs && m_devicesInTabs ) {
        // Move DevicesPage from m_tabs → m_outerStack
        m_tabs->removePage( m_devicesPage );
        m_devicesPage->reparent( m_outerStack, 0, TQPoint(), false );
        m_outerStack->addWidget( m_devicesPage, 1 );
        m_outerStack->raiseWidget( 0 );
        updateDevicesButton();
        m_devicesInTabs = false;
        m_devicesBtn->show();
        repositionDevicesBtn();
    } else if ( !noTabs && !m_devicesInTabs ) {
        // Move DevicesPage from m_outerStack → m_tabs
        m_outerStack->removeWidget( m_devicesPage );
        m_devicesPage->reparent( m_tabs, 0, TQPoint(), false );
        m_tabs->addTab( m_devicesPage, i18n("Devices") );
        m_outerStack->raiseWidget( 0 );
        m_devicesInTabs = true;
        m_devicesBtn->hide();
    }

    // Repopulate into whichever view is now active
    AudioDevice::Category cats[] = {
        AudioDevice::Output, AudioDevice::Input,
        AudioDevice::Playback, AudioDevice::Recording
    };
    for ( int i = 0; i < 4; i++ ) {
        TQPtrList<AudioDevice> devs = m_model->devices( cats[i] );
        for ( TQPtrListIterator<AudioDevice> jt(devs); *jt; ++jt )
            onDeviceAdded( *jt );
    }
}


void MixerWindow::applySettings()
{
    TDEConfig *cfg = TDEGlobal::config();

    cfg->setGroup("Tray");
    if ( cfg->readBoolEntry("DockInTray", true) )
        m_tray->show();
    else
        m_tray->hide();

    cfg->setGroup("View");
    bool noTabs = cfg->readBoolEntry("NoTabs", false);
    if ( noTabs != ( m_stack->id( m_stack->visibleWidget() ) == 1 ) ) {
        rebuildView();
    } else {
        // Tabs mode: apply per-tab visibility
        m_tabs->setTabEnabled( m_output.scroll,    cfg->readBoolEntry("ShowOutput",    true) );
        m_tabs->setTabEnabled( m_input.scroll,     cfg->readBoolEntry("ShowInput",     true) );
        m_tabs->setTabEnabled( m_playback.scroll,  cfg->readBoolEntry("ShowPlayback",  true) );
        m_tabs->setTabEnabled( m_recording.scroll, cfg->readBoolEntry("ShowRecording", true) );
    }
    emit settingsChanged();
}

void MixerWindow::toggleDevices()
{
    int next = ( m_outerStack->id( m_outerStack->visibleWidget() ) == 0 ) ? 1 : 0;
    m_outerStack->raiseWidget( next );
    updateDevicesButton();
    repositionDevicesBtn();
    m_devicesBtn->raise();
}

void MixerWindow::updateDevicesButton()
{
    if ( m_outerStack->id( m_outerStack->visibleWidget() ) == 0 ) {
        m_devicesBtn->setIconSet( SmallIconSet("configure") );
        TQToolTip::add( m_devicesBtn, i18n("Device Configuration") );
    } else {
        m_devicesBtn->setIconSet( SmallIconSet("kmix") );
        TQToolTip::add( m_devicesBtn, i18n("Mixer") );
    }
}

void MixerWindow::repositionDevicesBtn()
{
    int bw = m_devicesBtn->width()  ? m_devicesBtn->width()  : 24;
    int bh = m_devicesBtn->height() ? m_devicesBtn->height() : 24;
    // Position in bottom-right of m_outerStack, mapped to centralWidget coords
    TQPoint origin = m_outerStack->mapTo( centralWidget(), TQPoint(0, 0) );
    int x = origin.x() + m_outerStack->width()  - bw - 4;
    int y = origin.y() + m_outerStack->height() - bh - 4;
    m_devicesBtn->move( x, y );
    m_devicesBtn->raise();
}

bool MixerWindow::eventFilter( TQObject *obj, TQEvent *e )
{
    if ( obj == centralWidget() && e->type() == TQEvent::Resize )
        repositionDevicesBtn();
    return false;
}

void MixerWindow::showEvent( TQShowEvent *e )
{
    TDEMainWindow::showEvent( e );
    repositionDevicesBtn();

    TDEConfig *c = TDEGlobal::config();
    c->setGroup("MainWindow");
    if ( c->hasKey("XPos") && c->hasKey("YPos") )
        move( c->readNumEntry("XPos"), c->readNumEntry("YPos") );

    m_model->addLevelViewer();
}

void MixerWindow::hideEvent( TQHideEvent *e )
{
    TDEMainWindow::hideEvent( e );
    m_model->removeLevelViewer();
}

#include "mixerwindow.moc"
