/* This file is part of KNemo
   Copyright (C) 2004, 2006 Percy Leonhardt <percy@eris23.de>

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

   KNemo 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 Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include <tqtimer.h>
#include <tqpixmap.h>

#include <twin.h>
#include <kdebug.h>
#include <tdelocale.h>
#include <twinmodule.h>
#include <kiconloader.h>

#include "interface.h"
#include "signalplotter.h"
#include "interfacestatistics.h"
#include "interfacestatusdialog.h"
#include "interfacestatisticsdialog.h"

Interface::Interface( TQString ifname,
                      const GeneralData& generalData,
                      const PlotterSettings& plotterSettings )
    : TQObject(),
      mType( UNKNOWN_TYPE ),
      mState( UNKNOWN_STATE ),
      mOutgoingPos( 0 ),
      mIncomingPos( 0 ),
      mName( ifname ),
      mPlotterTimer( 0 ),
      mIcon( this ),
      mStatistics( 0 ),
      mStatusDialog( 0 ),
      mStatisticsDialog(  0 ),
      mPlotter( 0 ),
      mVisibleBeams( NONE ),
      mGeneralData( generalData ),
      mPlotterSettings( plotterSettings )
{
    connect( &mMonitor, TQ_SIGNAL( statusChanged( int ) ),
             &mIcon, TQ_SLOT( updateStatus( int ) ) );
    connect( &mMonitor, TQ_SIGNAL( available( int ) ),
             &mIcon, TQ_SLOT( updateTrayStatus( int ) ) );
    connect( &mMonitor, TQ_SIGNAL( notAvailable( int ) ),
             &mIcon, TQ_SLOT( updateTrayStatus( int ) ) );
    connect( &mMonitor, TQ_SIGNAL( notExisting( int ) ),
             &mIcon, TQ_SLOT( updateTrayStatus( int ) ) );
    connect( &mMonitor, TQ_SIGNAL( available( int ) ),
             this, TQ_SLOT( setStartTime( int ) ) );
    connect( &mMonitor, TQ_SIGNAL( statusChanged( int ) ),
             this, TQ_SLOT( resetData( int ) ) );
    connect( &mIcon, TQ_SIGNAL( statisticsSelected() ),
             this, TQ_SLOT( showStatisticsDialog() ) );
}

Interface::~Interface()
{
    if ( mStatusDialog != 0 )
    {
        delete mStatusDialog;
    }
    if ( mPlotter != 0 )
    {
        delete mPlotter;
    }
    if ( mPlotterTimer != 0 )
    {
        mPlotterTimer->stop();
        delete mPlotterTimer;
    }
    if ( mStatistics != 0 )
    {
        // this will also delete a possibly open statistics dialog
        stopStatistics();
    }
}

void Interface::configChanged()
{
    // UNKNOWN_STATE to avoid notification
    mIcon.updateTrayStatus( UNKNOWN_STATE );
    // handle changed iconset by user
    mIcon.updateStatus( mState );
    mIcon.updateToolTip();
    mIcon.updateMenu();

    if ( mPlotter != 0L )
    {
        configurePlotter();
    }

    if ( mStatistics != 0 )
    {
        mStatistics->configChanged();
    }

    if ( mSettings.activateStatistics && mStatistics == 0 )
    {
        // user turned on statistics
        startStatistics();
    }
    else if ( !mSettings.activateStatistics && mStatistics != 0 )
    {
        // user turned off statistics
        stopStatistics();
    }

    if ( mStatusDialog )
    {
        mStatusDialog->setStatisticsGroupEnabled( mSettings.activateStatistics );
    }
}

void Interface::activateMonitor()
{
    mMonitor.checkStatus( this );
}

void Interface::setStartTime( int )
{
    mStartTime.setDate( TQDate::currentDate() );
    mStartTime.setTime( TQTime::currentTime() );
}

void Interface::showStatusDialog()
{
    // Toggle the status dialog.
    // First click will show the status dialog, second will hide it.
    if ( mStatusDialog == 0L )
    {
        mStatusDialog = new InterfaceStatusDialog( this );
        connect( &mMonitor, TQ_SIGNAL( available( int ) ),
                 mStatusDialog, TQ_SLOT( enableNetworkGroups( int ) ) );
        connect( &mMonitor, TQ_SIGNAL( notAvailable( int ) ),
                 mStatusDialog, TQ_SLOT( disableNetworkGroups( int ) ) );
        connect( &mMonitor, TQ_SIGNAL( notExisting( int ) ),
                 mStatusDialog, TQ_SLOT( disableNetworkGroups( int ) ) );
        if ( mStatistics != 0 )
        {
            connect( mStatistics, TQ_SIGNAL( currentEntryChanged() ),
                     mStatusDialog, TQ_SLOT( statisticsChanged() ) );
            mStatusDialog->statisticsChanged();
        }
        activateOrHide( mStatusDialog, true );
    }
    else
    {
        // Toggle the status dialog.
        activateOrHide( mStatusDialog );
    }
}

void Interface::showSignalPlotter( bool wasMiddleButton )
{
    // No plotter, create it.
    if ( mPlotter == 0L )
    {
        mPlotter = new SignalPlotter( 0L, mName.local8Bit() );
        mPlotter->setIcon( SmallIcon( "knemo" ) );
        mPlotter->setCaption( mName + " " + i18n( "Traffic" ) );
        mPlotter->setTitle( mName );
        configurePlotter();
        activateOrHide( mPlotter, true );

        mPlotterTimer = new TQTimer();
        connect( mPlotterTimer, TQ_SIGNAL( timeout() ),
                 this, TQ_SLOT( updatePlotter() ) );
        mPlotterTimer->start( 1000 );
    }
    else
    {
        if ( wasMiddleButton )
        {
            // Toggle the signal plotter.
            activateOrHide( mPlotter );
        }
        else
        {
            // Called from the context menu, show the dialog.
            activateOrHide( mPlotter, true );
        }
    }
}

void Interface::showStatisticsDialog()
{
    if ( mStatisticsDialog == 0 )
    {
        mStatisticsDialog = new InterfaceStatisticsDialog( this );
        if ( mStatistics == 0 )
        {
            // should never happen but you never know...
            startStatistics();
        }
        connect( mStatistics, TQ_SIGNAL( dayStatisticsChanged() ),
                 mStatisticsDialog, TQ_SLOT( updateDays() ) );
        connect( mStatistics, TQ_SIGNAL( monthStatisticsChanged() ),
                 mStatisticsDialog, TQ_SLOT( updateMonths() ) );
        connect( mStatistics, TQ_SIGNAL( yearStatisticsChanged() ),
                 mStatisticsDialog, TQ_SLOT( updateYears() ) );
        connect( mStatistics, TQ_SIGNAL( currentEntryChanged() ),
                 mStatisticsDialog, TQ_SLOT( updateCurrentEntry() ) );
        connect( mStatisticsDialog, TQ_SIGNAL( clearDailyStatisticsClicked() ),
                 mStatistics, TQ_SLOT( clearDayStatistics() ) );
        connect( mStatisticsDialog, TQ_SIGNAL( clearMonthlyStatisticsClicked() ),
                 mStatistics, TQ_SLOT( clearMonthStatistics() ) );
        connect( mStatisticsDialog, TQ_SIGNAL( clearYearlyStatisticsClicked() ),
                 mStatistics, TQ_SLOT( clearYearStatistics() ) );

        mStatisticsDialog->updateDays();
        mStatisticsDialog->updateMonths();
        mStatisticsDialog->updateYears();
    }
    mStatisticsDialog->show();
}

void Interface::resetData( int state )
{
    // For PPP interfaces we will reset all data to zero when the
    // interface gets disconnected. If the driver also resets its data
    // (like PPP seems to do) we will start from zero for every new
    // connection.
    if ( mType == PPP &&
         ( state == NOT_AVAILABLE ||
           state == NOT_EXISTING ) )
    {
        mData.prevTxBytes = mData.txBytes = 0;
        mData.prevRxBytes = mData.rxBytes = 0;
        mData.prevTxPackets = mData.txPackets = 0;
        mData.prevRxPackets = mData.rxPackets = 0;
    }
}

void Interface::updatePlotter()
{
    if ( mPlotter )
    {
        double outgoingBytes = mData.outgoingBytes / 1024.0 / (double) mGeneralData.pollInterval;
        double incomingBytes = mData.incomingBytes / 1024.0 / (double) mGeneralData.pollInterval;

        TQValueList<double> trafficList;
        switch ( mVisibleBeams )
        {
        case BOTH:
            if ( mIncomingPos == 1 )
            {
                trafficList.append( outgoingBytes );
                trafficList.append( incomingBytes );
            }
            else
            {
                trafficList.append( incomingBytes );
                trafficList.append( outgoingBytes );
            }
            mPlotter->addSample( trafficList );
            break;
        case INCOMING_TRAFFIC:
            trafficList.append( incomingBytes );
            mPlotter->addSample( trafficList );
            break;
        case OUTGOING_TRAFFIC:
            trafficList.append( outgoingBytes );
            mPlotter->addSample( trafficList );
            break;
        case NONE:
            break;
        }
    }
}

void Interface::configurePlotter()
{
    mPlotter->setFontSize( mPlotterSettings.fontSize );
    if ( !mPlotterSettings.automaticDetection )
    {
        mPlotter->setMinValue( mPlotterSettings.minimumValue );
        mPlotter->setMaxValue( mPlotterSettings.maximumValue );
    }
    mPlotter->setHorizontalScale( mPlotterSettings.pixel );
    mPlotter->setHorizontalLinesCount( mPlotterSettings.count );
    mPlotter->setVerticalLinesDistance( mPlotterSettings.distance );
    mPlotter->setShowLabels( mPlotterSettings.labels );
    mPlotter->setShowTopBar( mPlotterSettings.topBar );
    mPlotter->setShowVerticalLines( mPlotterSettings.verticalLines );
    mPlotter->setShowHorizontalLines( mPlotterSettings.horizontalLines );
    mPlotter->setUseAutoRange( mPlotterSettings.automaticDetection );
    mPlotter->setVerticalLinesScroll( mPlotterSettings.verticalLinesScroll );
    mPlotter->setVerticalLinesColor( mPlotterSettings.colorVLines );
    mPlotter->setHorizontalLinesColor( mPlotterSettings.colorHLines );
    mPlotter->setBackgroundColor( mPlotterSettings.colorBackground );

    // add or remove beams according to user settings
    VisibleBeams nextVisibleBeams = NONE;
    if ( mPlotterSettings.showOutgoing )
        nextVisibleBeams = (VisibleBeams) ( nextVisibleBeams | OUTGOING_TRAFFIC );
    if ( mPlotterSettings.showIncoming )
        nextVisibleBeams = (VisibleBeams) ( nextVisibleBeams | INCOMING_TRAFFIC );

    TQValueList<TQColor>& colors = mPlotter->beamColors();
    switch( mVisibleBeams )
    {
    case NONE:
        if ( nextVisibleBeams == BOTH )
        {
            mOutgoingPos = 0;
            mPlotter->addBeam( mPlotterSettings.colorOutgoing );
            mIncomingPos = 1;
            mPlotter->addBeam( mPlotterSettings.colorIncoming );
        }
        else if ( nextVisibleBeams == OUTGOING_TRAFFIC )
        {
            mOutgoingPos = 0;
            mPlotter->addBeam( mPlotterSettings.colorOutgoing );
        }
        else if ( nextVisibleBeams == INCOMING_TRAFFIC )
        {
            mIncomingPos = 0;
            mPlotter->addBeam( mPlotterSettings.colorIncoming );
        }
        break;
    case INCOMING_TRAFFIC:
        if ( nextVisibleBeams == BOTH )
        {
            mOutgoingPos = 1;
            mPlotter->addBeam( mPlotterSettings.colorOutgoing );
        }
        else if ( nextVisibleBeams == OUTGOING_TRAFFIC )
        {
            mPlotter->removeBeam( mIncomingPos );
            mOutgoingPos = 0;
            mPlotter->addBeam( mPlotterSettings.colorOutgoing );
        }
        else if ( nextVisibleBeams == INCOMING_TRAFFIC )
        {
            colors[mIncomingPos] = ( mPlotterSettings.colorIncoming );
        }
        else if ( nextVisibleBeams == NONE )
        {
            mPlotter->removeBeam( mIncomingPos );
        }
        break;
    case OUTGOING_TRAFFIC:
        if ( nextVisibleBeams == BOTH )
        {
            mIncomingPos = 1;
            mPlotter->addBeam( mPlotterSettings.colorIncoming );
        }
        else if ( nextVisibleBeams == INCOMING_TRAFFIC )
        {
            mPlotter->removeBeam( mOutgoingPos );
            mIncomingPos = 0;
            mPlotter->addBeam( mPlotterSettings.colorIncoming );
        }
        else if ( nextVisibleBeams == OUTGOING_TRAFFIC )
        {
            colors[mOutgoingPos] = ( mPlotterSettings.colorOutgoing );
        }
        else if ( nextVisibleBeams == NONE )
        {
            mPlotter->removeBeam( mOutgoingPos );
        }
        break;
    case BOTH:
        if ( nextVisibleBeams == BOTH )
        {
            colors[mIncomingPos] = ( mPlotterSettings.colorIncoming );
            colors[mOutgoingPos] = ( mPlotterSettings.colorOutgoing );
        }
        else if ( nextVisibleBeams == OUTGOING_TRAFFIC )
        {
            mOutgoingPos = 0;
            mPlotter->removeBeam( mIncomingPos );
        }
        else if ( nextVisibleBeams == INCOMING_TRAFFIC )
        {
            mIncomingPos = 0;
            mPlotter->removeBeam( mOutgoingPos );
        }
        else if ( nextVisibleBeams == NONE )
        {
            mPlotter->removeBeam( 0 );
            mPlotter->removeBeam( 0 );
        }
        break;
    }
    mVisibleBeams = nextVisibleBeams;
    mPlotter->repaint();
}

void Interface::startStatistics()
{
    mStatistics = new InterfaceStatistics( this );
    connect( &mMonitor, TQ_SIGNAL( incomingData( unsigned long ) ),
             mStatistics, TQ_SLOT( addIncomingData( unsigned long ) ) );
    connect( &mMonitor, TQ_SIGNAL( outgoingData( unsigned long ) ),
             mStatistics, TQ_SLOT( addOutgoingData( unsigned long ) ) );
    if ( mStatusDialog != 0 )
    {
        connect( mStatistics, TQ_SIGNAL( currentEntryChanged() ),
                 mStatusDialog, TQ_SLOT( statisticsChanged() ) );
        mStatusDialog->statisticsChanged();
    }

    mStatistics->loadStatistics();
}

void Interface::stopStatistics()
{
    if ( mStatisticsDialog != 0 )
    {
        // this will close an open statistics dialog
        delete mStatisticsDialog;
        mStatisticsDialog = 0;
    }

    mStatistics->saveStatistics();

    delete mStatistics;
    mStatistics = 0;
}

// taken from ksystemtray.cpp
void Interface::activateOrHide( TQWidget* widget, bool onlyActivate )
{
    if ( !widget )
	return;

    KWin::WindowInfo info1 = KWin::windowInfo( widget->winId(), NET::XAWMState | NET::WMState );
    // mapped = visible (but possibly obscured)
    bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized();
    //    - not mapped -> show, raise, focus
    //    - mapped
    //        - obscured -> raise, focus
    //        - not obscured -> hide
    if( !mapped )
    {
        KWin::setOnDesktop( widget->winId(), KWin::currentDesktop() );
        widget->show();
        widget->raise();
	KWin::activateWindow( widget->winId() );
    }
    else
    {
        KWinModule module;
        for( TQValueList< WId >::ConstIterator it = module.stackingOrder().fromLast();
             it != module.stackingOrder().end() && (*it) != widget->winId();
             --it )
        {
            KWin::WindowInfo info2 = KWin::windowInfo( *it, (unsigned long)
                NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType );
            if( info2.mappingState() != NET::Visible )
                continue; // not visible on current desktop -> ignore
            if( !info2.geometry().intersects( widget->geometry()))
                continue; // not obscuring the window -> ignore
            if( !info1.hasState( NET::KeepAbove ) && info2.hasState( NET::KeepAbove ))
                continue; // obscured by window kept above -> ignore
            NET::WindowType type = info2.windowType( NET::NormalMask | NET::DesktopMask
                | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
                | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask );
            if( type == NET::Dock || type == NET::TopMenu )
                continue; // obscured by dock or topmenu -> ignore
            widget->raise();
            KWin::activateWindow( widget->winId());
            return;
        }
        if ( !onlyActivate )
        {
            widget->hide();
        }
    }
}

#include "interface.moc"
