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

Copyright (C) 2007 Rivo Laks <rivolaks@hot.ee>
Copyright (C) 2008 Lucas Murray <lmurray@undefinedfire.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/>.
*********************************************************************/

#ifndef KWIN_PRESENTWINDOWS_H
#define KWIN_PRESENTWINDOWS_H

#include "presentwindows_proxy.h"

#include <kwineffects.h>
#include <kshortcut.h>

namespace KWin
{

/**
 * Expose-like effect which shows all windows on current desktop side-by-side,
 *  letting the user select active window.
 **/
class PresentWindowsEffect
    : public QObject, public Effect
    {
    Q_OBJECT
    private:
        // Structures
        struct WindowData
            {
            bool visible;
            bool deleted;
            bool referenced;
            double opacity;
            double highlight;
            int slot;
            int slot_distance;
            EffectFrame* textFrame;
            EffectFrame* iconFrame;
            };
        typedef QHash<EffectWindow*, WindowData> DataHash;
        struct GridSize
            {
            int columns;
            int rows;
            };

    public:
        PresentWindowsEffect();
        virtual ~PresentWindowsEffect();

        virtual void reconfigure( ReconfigureFlags );
        virtual void* proxy();

        // Screen painting
        virtual void prePaintScreen( ScreenPrePaintData &data, int time );
        virtual void paintScreen( int mask, QRegion region, ScreenPaintData &data );
        virtual void postPaintScreen();

        // Window painting
        virtual void prePaintWindow( EffectWindow *w, WindowPrePaintData &data, int time );
        virtual void paintWindow( EffectWindow *w, int mask, QRegion region, WindowPaintData &data );

        // User interaction
        virtual void windowAdded( EffectWindow *w );
        virtual void windowClosed( EffectWindow *w );
        virtual void windowDeleted( EffectWindow *w );
        virtual void windowGeometryShapeChanged( EffectWindow* w, const QRect& old );
        virtual bool borderActivated( ElectricBorder border );
        virtual void windowInputMouseEvent( Window w, QEvent *e );
        virtual void grabbedKeyboardEvent( QKeyEvent *e );

        // Tab box
        virtual void tabBoxAdded( int mode );
        virtual void tabBoxClosed();
        virtual void tabBoxUpdated();
        virtual void tabBoxKeyEvent( QKeyEvent* event );

        // atoms
        virtual void propertyNotify( EffectWindow* w, long atom );

        enum { LayoutNatural, LayoutRegularGrid, LayoutFlexibleGrid }; // Layout modes
        enum PresentWindowsMode
            {
            ModeAllDesktops, // Shows windows of all desktops
            ModeCurrentDesktop, // Shows windows on current desktop
            ModeSelectedDesktop, // Shows windows of selected desktop via property (m_desktop)
            ModeWindowGroup, // Shows windows selected via property
            ModeWindowClass // Shows all windows of same class as selected class
            };
        enum WindowMouseAction
            {
            WindowNoAction = 0, // Nothing
            WindowActivateAction = 1, // Activates the window and deactivates the effect
            WindowExitAction = 2, // Deactivates the effect without activating new window
            WindowToCurrentDesktopAction = 3, // Brings window to current desktop
            WindowToAllDesktopsAction = 4, // Brings window to all desktops
            WindowMinimizeAction = 5, // Minimize the window
            WindowCloseAction = 6 // Closes the window
            };
        enum DesktopMouseAction
            {
            DesktopNoAction = 0, // nothing
            DesktopActivateAction = 1, // Activates the window and deactivates the effect
            DesktopExitAction = 2, // Deactivates the effect without activating new window
            DesktopShowDesktopAction = 3 // Minimizes all windows
            };

    public slots:
        void setActive( bool active, bool closingTab = false ); // HACK: closingTab shouldn't be needed
        void toggleActive()  { m_mode = ModeCurrentDesktop; setActive( !m_activated ); }
        void toggleActiveAllDesktops()  { m_mode = ModeAllDesktops; setActive( !m_activated ); }
        void toggleActiveClass();

        // slots for global shortcut changed
        // needed to toggle the effect
        void globalShortcutChanged( const QKeySequence& seq );
        void globalShortcutChangedAll( const QKeySequence& seq );
        void globalShortcutChangedClass( const QKeySequence& seq );

    protected:
        // Window rearranging
        void rearrangeWindows();
        void calculateWindowTransformations( EffectWindowList windowlist, int screen,
            WindowMotionManager& motionManager, bool external = false );
        void calculateWindowTransformationsClosest( EffectWindowList windowlist, int screen,
            WindowMotionManager& motionManager );
        void calculateWindowTransformationsKompose( EffectWindowList windowlist, int screen,
            WindowMotionManager& motionManager );
        void calculateWindowTransformationsNatural( EffectWindowList windowlist, int screen,
            WindowMotionManager& motionManager );

        // Helper functions for window rearranging
        inline double aspectRatio( EffectWindow *w )
            { return w->width() / double( w->height() ); }
        inline int widthForHeight( EffectWindow *w, int height )
            { return int(( height / double( w->height() )) * w->width() ); }
        inline int heightForWidth( EffectWindow *w, int width )
            { return int(( width / double( w->width() )) * w->height() ); }
        void assignSlots( EffectWindowList windowlist, const QRect &area, int columns, int rows );
        void getBestAssignments( EffectWindowList windowlist );
        bool isOverlappingAny( EffectWindow *w, const QHash<EffectWindow*, QRect> &targets, const QRegion &border );

        // Filter box
        void updateFilterFrame();

        // Helper functions
        bool isSelectableWindow( EffectWindow *w );
        bool isVisibleWindow( EffectWindow *w );
        void setHighlightedWindow( EffectWindow *w );
        EffectWindow* relativeWindow( EffectWindow *w, int xdiff, int ydiff, bool wrap ) const;
        EffectWindow* findFirstWindow() const;

        // Helper functions for mouse actions
        void mouseActionWindow( WindowMouseAction& action );
        void mouseActionDesktop( DesktopMouseAction& action );

    private:
        PresentWindowsEffectProxy m_proxy;
        friend class PresentWindowsEffectProxy;

        // User configuration settings
        QList<ElectricBorder> m_borderActivate;
        QList<ElectricBorder> m_borderActivateAll;
        int m_layoutMode;
        bool m_showCaptions;
        bool m_showIcons;
        bool m_tabBoxAllowed;
        bool m_tabBoxAlternativeAllowed;
        int m_accuracy;
        bool m_fillGaps;
        double m_fadeDuration;
        bool m_showPanel;

        // Activation
        bool m_activated;
        bool m_ignoreMinimized;
        double m_decalOpacity;
        Window m_input;
        bool m_hasKeyboardGrab;
        bool m_tabBoxEnabled;
        PresentWindowsMode m_mode;
        int m_desktop;
        EffectWindowList m_selectedWindows;
        EffectWindow *m_managerWindow;
        QString m_class;

        // Window data
        WindowMotionManager m_motionManager;
        DataHash m_windowData;
        EffectWindow *m_highlightedWindow;

        // Grid layout info
        QList<GridSize> m_gridSizes;

        // Filter box
        EffectFrame* m_filterFrame;
        QString m_windowFilter;

        // Shortcut - needed to toggle the effect
        KShortcut shortcut;
        KShortcut shortcutAll;
        KShortcut shortcutClass;

        // Atoms
        // Present windows for all windows of given desktop
        // -1 for all desktops
        long m_atomDesktop;
        // Present windows for group of window ids
        long m_atomWindows;

        // Mouse Actions
        WindowMouseAction m_leftButtonWindow;
        WindowMouseAction m_middleButtonWindow;
        WindowMouseAction m_rightButtonWindow;
        DesktopMouseAction m_leftButtonDesktop;
        DesktopMouseAction m_middleButtonDesktop;
        DesktopMouseAction m_rightButtonDesktop;
    };

} // namespace

#endif