/********************************************************************
 KWin - the KDE window manager
 This file is part of the KDE project.

Copyright (C) 2007 Philip Falkner <philip.falkner@gmail.com>

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, see <http://www.gnu.org/licenses/>.
*********************************************************************/

#include "fade.h"

#include <kconfiggroup.h>

namespace KWin
{

KWIN_EFFECT( fade, FadeEffect )

FadeEffect::FadeEffect()
    {
    reconfigure( ReconfigureAll );
    }

void FadeEffect::reconfigure( ReconfigureFlags )
    {
    KConfigGroup conf = effects->effectConfig( "Fade" );
    fadeInTime = animationTime( conf, "FadeInTime", 150 );
    fadeOutTime = animationTime( conf, "FadeOutTime", 150 );
    fadeWindows = conf.readEntry( "FadeWindows", true );

    // Add all existing windows to the window list
    // TODO: Enabling desktop effects should trigger windowAdded() on all windows
    windows.clear();
    if( !fadeWindows )
        return;
    foreach( EffectWindow *w, effects->stackingOrder() )
        if( w && isFadeWindow( w )) // TODO: Apparently w can == NULL here?
            windows[ w ] = WindowInfo();
    }

void FadeEffect::prePaintScreen( ScreenPrePaintData& data, int time )
    {
    if( !windows.isEmpty())
        {
        fadeInStep = time / double( fadeInTime );
        fadeOutStep = time / double( fadeOutTime );
        }
    effects->prePaintScreen( data, time );
    }

void FadeEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time )
    {
    if( windows.contains( w ))
        {
        windows[ w ].fadeInStep += fadeInStep;
        windows[ w ].fadeOutStep += fadeOutStep;
        if( windows[ w ].opacity < 1.0 )
            data.setTranslucent();
        if( windows[ w ].deleted )
            {
            if( windows[ w ].opacity <= 0.0 )
                {
                windows.remove( w );
                w->unrefWindow();
                }
            else
                w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DELETE );
            }
        }
    effects->prePaintWindow( w, data, time );
    if( windows.contains( w ) && !w->isPaintingEnabled() && !effects->activeFullScreenEffect() )
        { // if the window isn't to be painted, then let's make sure
          // to track its progress
        if( windows[ w ].fadeInStep < 1.0
            || windows[ w ].fadeOutStep < 1.0 )
            { // but only if the total change is less than the
              // maximum possible change
            w->addRepaintFull();
            }
        }
    }

void FadeEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
    {
    if( windows.contains( w ))
        {
        if( windows[ w ].deleted
            || windows[ w ].opacity != data.opacity
            || windows[ w ].saturation != data.saturation
            || windows[ w ].brightness != data.brightness )
            {
            WindowPaintData new_data = data;

            if( windows[ w ].deleted )
                new_data.opacity = 0.0;

            if( new_data.opacity > windows[ w ].opacity )
                {
                windows[ w ].opacity = qMin( new_data.opacity,
                    windows[ w ].opacity + windows[ w ].fadeInStep );
                }
            else if( new_data.opacity < windows[ w ].opacity )
                {
                windows[ w ].opacity = qMax( new_data.opacity,
                    windows[ w ].opacity - windows[ w ].fadeOutStep );
                }

            if( new_data.saturation > windows[ w ].saturation )
                {
                windows[ w ].saturation = qMin( new_data.saturation,
                    windows[ w ].saturation + windows[ w ].fadeInStep );
                }
            else if( new_data.saturation < windows[ w ].saturation )
                {
                windows[ w ].saturation = qMax( new_data.saturation,
                    windows[ w ].saturation - windows[ w ].fadeOutStep );
                }

            if( new_data.brightness > windows[ w ].brightness )
                {
                windows[ w ].brightness = qMin( new_data.brightness,
                    windows[ w ].brightness + windows[ w ].fadeInStep );
                }
            else if( new_data.brightness < windows[ w ].brightness )
                {
                windows[ w ].brightness = qMax( new_data.brightness,
                    windows[ w ].brightness - windows[ w ].fadeOutStep );
                }

            windows[ w ].opacity = qBound( 0.0, windows[ w ].opacity, 1.0 );
            windows[ w ].saturation = qBound( 0.0, windows[ w ].saturation, 1.0 );
            windows[ w ].brightness = qBound( 0.0, windows[ w ].brightness, 1.0 );
            windows[ w ].fadeInStep = 0.0;
            windows[ w ].fadeOutStep = 0.0;

            new_data.opacity = windows[ w ].opacity;
            new_data.saturation = windows[ w ].saturation;
            new_data.brightness = windows[ w ].brightness;
            effects->paintWindow( w, mask, region, new_data );
            if( windows[ w ].opacity != data.opacity
                || windows[ w ].saturation != data.saturation
                || windows[ w ].brightness != data.brightness )
                w->addRepaintFull();
            return;
            }
        windows[ w ].fadeInStep = 0.0;
        windows[ w ].fadeOutStep = 0.0;
        }
    effects->paintWindow( w, mask, region, data );
    }

void FadeEffect::windowOpacityChanged( EffectWindow* w, double old_opacity )
    {
    if( !windows.contains( w ))
        windows[ w ].opacity = old_opacity;
    if( windows[ w ].opacity == 1.0 )
        windows[ w ].opacity -= 0.1 / fadeOutTime;
    w->addRepaintFull();
    }

void FadeEffect::windowAdded( EffectWindow* w )
    {
    if( !fadeWindows || !isFadeWindow( w ))
        return;
    windows[ w ].opacity = 0.0;
    w->addRepaintFull();
    }

void FadeEffect::windowClosed( EffectWindow* w )
    {
    if( !fadeWindows || !isFadeWindow( w ))
        return;
    if( !windows.contains( w ))
        windows[ w ].opacity = w->opacity();
    if( windows[ w ].opacity == 1.0 )
        windows[ w ].opacity -= 0.1 / fadeOutTime;
    windows[ w ].deleted = true;
    w->refWindow();
    w->addRepaintFull();
    }

void FadeEffect::windowDeleted( EffectWindow* w )
    {
    windows.remove( w );
    }

bool FadeEffect::isFadeWindow( EffectWindow* w )
    {
    if( w->windowClass() == "ksplashx ksplashx"
        || w->windowClass() == "ksplashsimple ksplashsimple" )
        { // see login effect
        return false;
        }
    return ( !w->isDesktop() && !w->isUtility() );
    }

} // namespace
