/*
 * widgets/Widget.h:
 *
 * Copyright (C) 2000 John Watson, jwatson@tempusmud.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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 */

#ifndef __Widget_h__
#define __Widget_h__

#include <SDL/SDL.h>

#ifdef WIN32
#include <windows.h>
#endif

#include <GL/gl.h>

#include "Scrollable.h"
#include "LookAndFeel.h"
#include "EventListeners.h"
#include "Draggable.h"

#include "../util/Vector.h"
#include "../util/List.h"

class WidgetEvent;
class WidgetEventListener;

class Widget : 
        public Scrollable, 
        public KeyListener, 
        public MouseButtonListener, 
        public MouseMotionListener,
        public DNDGestureListener
{

        Vector< class WidgetEventListener*>   _widgetEventListeners;
        Vector< class KeyListener * >         _keyListeners;
        Vector< class MouseMotionListener * > _mouseMotionListeners;
        Vector< class MouseButtonListener * > _mouseButtonListeners;

        class Viewport * _viewport;

        class Widget * _parent;

        List<class Widget *> _children;

        static class Widget * _inputControllingWidget;
        
        //
        // texture-space coordinates of the 4 corners,
        // starting from bottom left, moving ccw
        //

        float _textureCoords[4][2];
        class TextureObject * _textureObject;
 
        bool _needsUIUpdate;

        class Widget * _lastWidget;
        bool _event_loopback;
        bool _isFocused;

        bool _isOpaque;

        int _extent[2];

        bool _ignoreEvents;

        const GLfloat * _bgColor;
        const GLfloat * _fgColor;

        bool _updateOk;

protected:

        static class WidgetManager * _wm;

        static class LookAndFeel * _lookAndFeel;

        bool _drawBorder;
        bool _drawMargin;
        
        bool _needsToPaint;

        int _size[2];

        char *_name;

        int _pos[2];
        int _absolutePos[2];
        
        bool _isEnabled;

        GLfloat _borderColor[4];
        GLfloat _marginColor[4];

        /**
         * set the widget that has pointer/keyboard control
         */
        static void setInputControllingWidget( class Widget * w );

        /**
         * null the widget that has pointer/keyboard control, iff it is equal to widget
         */

        static void unsetInputControllingWidget( class Widget * w );

        void addAbsolutePos();

        /**
         * this _must_ be called first from the constructors
         */

        void initWidget( int width, int height );

        inline void setNeedsToPaintBase( bool flag ) {
                _needsToPaint = flag;
        }

        void notifyWidgetEventListeners( class WidgetEvent * evt );
        void notifyActionListeners( class Widget * source, const char * command );
        void notifyViewportListeners( class Viewport * source, int old_x, int old_y );
        void notifyResizeListeners( class Widget * w, int old_x, int old_y );

public:

        inline void setEnabled( bool enabled ) { _isEnabled = enabled; }
        inline bool isEnabled() const { return _isEnabled; }

        inline bool isUpdateOk() { return _updateOk; }
        inline void setUpdateOk( bool flag ) { _updateOk = flag; }

        static void setWidgetManager( class WidgetManager * wm ) { _wm = wm; }

        virtual void updateWidget() {}
        
        inline class TextureObject * getBGTexture() { return _textureObject; }
        void setBGTexture( const char * texname, GLenum type );
        void setBGTextureCoords( float origin_x, float origin_y, float extent_x, float extent_y );
        void setBGTexture( class TextureObject * obj ) { _textureObject = obj; }

        /**
         * returns the viewport which is an immediate viewport of this widget
         */
        const class Viewport * getViewport() const { return _viewport; }

        /**
         * descends the widget tree in search of the first applicable viewport.
         * clients may compare the absolute coordinates of the viewport's view
         * widget, the view positions of the viewport, and the size of the viewport
         * to determine visibility of a distant sub-widget
         */
        inline const class Viewport * findViewport() const;

        const class Viewport * findViewportClipRectangle( int clip[4] ) const;
        
        void setViewport( class Viewport * v );
        
        void resize( int x, int y );

        /** 
         * the listener must be removed before or during destruction,
         * to prevent dangling pointers
         */
        void addWidgetEventListener( class WidgetEventListener * listener );
        void removeWidgetEventListener( class WidgetEventListener * listener );

        /** 
         * the listener must be removed before or during destruction,
         * to prevent dangling pointers
         */
        void addKeyListener(    class KeyListener * listener );
        void removeKeyListener( class KeyListener * listener );

        /** 
         * the listener must be removed before or during destruction,
         * to prevent dangling pointers
         */
        void addMouseButtonListener(    class MouseButtonListener * listener );
        void removeMouseButtonListener( class MouseButtonListener * listener );

        /** 
         * the listener must be removed before or during destruction,
         * to prevent dangling pointers
         */
        void addMouseMotionListener(    class MouseMotionListener * listener );
        void removeMouseMotionListener( class MouseMotionListener * listener );

        static class TextureFont * _font;
        
        /*
        enum WidgetColor {
                COLOR_BG = 0,
                COLOR_FG = 1,
                COLOR_BORDER = 2
        };
        */
        inline static const class LookAndFeel * getLookAndFeel() { return _lookAndFeel; }

        inline const class Widget * getParent() const { return _parent; }

        inline void setIgnoreEvents( bool flag ) { _ignoreEvents = flag; }
        inline bool ignoreEvents() const { return _ignoreEvents; }

        /**
         * paint
         */
        virtual void paint();
        
         /**
          * get the widget that has pointer/keyboard control
          */
        inline static const class Widget * getInputControllingWidget() { 
                return _inputControllingWidget;
        }
        
        /**
         * Constructor
         */

        Widget( int width, int height );

        virtual ~Widget();
        
        void add( class Widget * w, int x, int y );

        void addCenter( class Widget * w, int y ) {
                add( w, ( _size[0] - w->_size[0] ) / 2, y );
        }

        bool removeWidget( class Widget * w );

        virtual void display();
        
        inline const bool isFocused() const { return _isFocused; }
        inline const bool isOpaque() const { return _isOpaque; }
        
        inline void setFocused( bool focus ) { _isFocused = focus; }

        const int * getSize() const { return _size; }
        inline int getWidth() const { return _size[0]; }
        inline int getHeight() const { return _size[1]; }

        inline const int getX() const { return _pos[0]; }
        inline const int getY() const { return _pos[1]; }

        inline const int * getPos() const { return _pos; }
        inline const int * getAbsolutePos() const { return _absolutePos; }
        
        inline int getRight() const { return _pos[0] + _size[0]; }

        inline void setOpaque( bool opaque ) { 
                _isOpaque = opaque;
        }

        inline void setDrawBorder( bool flag ) { 
                _drawBorder = flag;
        }
        inline void setDrawMargin( bool flag ) { 
                _drawMargin = flag;
        }
        
        inline void setBGColor4fv( const float * color ) {
                _bgColor = color;
        }

        inline void setFGColor4fv( const float * color ) {
                _fgColor = color;
        }

        inline const GLfloat * getBGColor4fv() const { return ( _bgColor ? _bgColor : 
                                                                _lookAndFeel->getSecondaryColor( COLOR_THREE ) ); }

        inline const GLfloat * getFGColor4fv() const { return ( _fgColor ? _fgColor : 
                                                                _lookAndFeel->getPrimaryColor( COLOR_ONE ) ); }

        inline bool isPointInside( int x, int y ) const {
                
                if ( x >= _pos[0] && x < _extent[0] &&
                     y >= _pos[1] && y < _extent[1] ) {
                        return true;
                }
                
                return false;
        }

        inline bool isPointInside( int pos[2] ) const {
                return isPointInside( pos[0], pos[1] );
        }
        
        // class KeyListener
        virtual bool keyPressed(    SDL_KeyboardEvent * evt, int x, int y );
        virtual bool keyReleased(   SDL_KeyboardEvent * evt );
        // class MouseButtonListener
        virtual bool mousePressed(  SDL_MouseButtonEvent * evt );
        virtual bool mouseReleased( SDL_MouseButtonEvent * evt );
        // class MouseMotionListener {
        virtual bool mouseMoved(    SDL_MouseMotionEvent * evt );
        virtual bool mouseEntered(  SDL_MouseMotionEvent * evt );
        virtual bool mouseExited(   SDL_MouseMotionEvent * evt );
        // class DNDGestureListener
        virtual bool isDNDDropOk(      DNDGestureEvent * evt );
        virtual class Draggable * performDNDPickup( DNDGestureEvent * evt );
        virtual bool performDNDDrop(   DNDGestureEvent * evt, class Draggable ** d );

        friend ostream& operator<<( ostream &os, const class Widget& w );

        void setPosition( int x, int y );

};

#endif



