// Copyright (C) 2005  Davis E. King (davis@dlib.net), Keita Mochizuki
// License: Boost Software License   See LICENSE.txt for the full license.
#ifndef DLIB_DRAWABLe_
#define DLIB_DRAWABLe_
#include <memory>
#include "drawable_abstract.h"
#include "../gui_core.h"
#include "../set.h"
#include "../binary_search_tree.h"
#include "../algs.h"
#include "../pixel.h"
#include "fonts.h"
#include "../matrix.h"
#include "canvas_drawing.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    // class drawable_window  
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    class drawable;
    class drawable_window : public base_window
    {
        /*!
            INITIAL VALUE
                - lastx == -1
                - lasty == -1
                - event_id == 1
            CONVENTION
                - bg_color == background_color()
                - widgets == this binary search tree contains every drawable that is in
                  this window.  It is a mapping of each drawable's z-order to a pointer
                  to said drawable.
                - widget_set == a set that contains all the widgets in this window and
                  want to receive events.
                - mouse_move == this is a set of drawables that are in this window and 
                  want to receive the mouse movement events.
                - mouse_wheel == this is a set of drawables that are in this window and 
                  want to receive the mouse wheel events.
                - mouse_click == this is a set of drawables that are in this window and 
                  want to receive the mouse click events.
                - window_resized == this is a set of drawables that are in this window and 
                  want to receive the window_resized event.
                - keyboard == this is a set of drawables that are in this window and 
                  want to receive keyboard events.
                - focus == this is a set of drawables that are in this window and 
                  want to receive focus events.
                - window_moved == this is a set of drawables that are in this window and 
                  want to receive window move events.
                - lastx == the x coordinate that we last saw the mouse at or -1 if the 
                  mouse is outside this window.
                - lasty == the y coordinate that we last saw the mouse at or -1 if the 
                  mouse is outside this window.
                - event_id == a number we use to tag events so we don't end up sending
                  an event to a drawable more than once.  This could happen if one of the
                  event handlers does something to reset the enumerator while we are
                  dispatching events (e.g. creating a new widget).
        !*/
    public:
        drawable_window(
            bool resizable = true,
            bool undecorated = false
        ) : 
            base_window(resizable,undecorated),
            bg_color(rgb_pixel(212,208,200)),
            lastx(-1),
            lasty(-1),
            event_id(1)
        {}
        void set_background_color (
            unsigned long red,
            unsigned long green,
            unsigned long blue
        );
        rgb_pixel background_color (
        ) const;
        virtual inline ~drawable_window()=0;
    private:
        void paint (
            const canvas& c
        );
    protected:
        void on_window_resized(
        );
        void on_window_moved(
        );
               
        void on_mouse_down (
            unsigned long btn,
            unsigned long state,
            long x,
            long y,
            bool is_double_click
        );
        void on_mouse_up (
            unsigned long btn,
            unsigned long state,
            long x,
            long y
        );
        void on_mouse_move (
            unsigned long state,
            long x,
            long y
        );
        void on_mouse_leave (
        );
        void on_mouse_enter (
        );
        void on_wheel_up (
            unsigned long state
        );
        void on_wheel_down (
            unsigned long state
        );
        
        void on_focus_gained (
        );
        void on_focus_lost (
        );
        void on_keydown (
            unsigned long key,
            bool is_printable,
            unsigned long state
        );
        void on_string_put (
            const std::wstring &str
        );
        void on_user_event (
            void* p,
            int i
        );
    private:
        
        friend class drawable;
        rgb_pixel bg_color;
        typedef set<drawable*>::kernel_1a_c set_of_drawables;
        binary_search_tree<long,set_of_drawables>::kernel_1a_c widgets;
        set_of_drawables widget_set;
        set_of_drawables mouse_move;
        set_of_drawables mouse_wheel;
        set_of_drawables mouse_click;
        set_of_drawables window_resized;
        set_of_drawables keyboard;
        set_of_drawables focus;
        set_of_drawables window_moved;
        set_of_drawables string_put;
        long lastx, lasty;
        unsigned long event_id;
        // restricted functions
        drawable_window(drawable_window&);        // copy constructor
        drawable_window& operator=(drawable_window&);    // assignment operator
    };
    drawable_window::~drawable_window(){ close_window();}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    // class drawable  
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    enum 
    {
        MOUSE_MOVE = 1,
        MOUSE_CLICK = 2,
        MOUSE_WHEEL = 4,
        WINDOW_RESIZED = 8,
        KEYBOARD_EVENTS = 16,
        FOCUS_EVENTS = 32,
        WINDOW_MOVED = 64,
        STRING_PUT = 128
    };
    class drawable 
    {
        /*!
            INITIAL VALUE 
                - enabled_events == false
                - event_id == 0
            CONVENTION
                - events == a bitset specifying what events this drawable is to receive.
                - z_order_value == z_order()
                - if (this drawable has been added to the parent window's sets and
                  binary search tree) then
                    - enabled_events == true
                - else
                    - enabled_events == false
                - event_id == the id of the last event we got from our parent window
        !*/
    public:
        friend class drawable_window;
        drawable (
            drawable_window& w,
            unsigned long events_ = 0
        ) :
            m(w.wm),
            parent(w),
            hidden(false),
            enabled(true),
            lastx(w.lastx),
            lasty(w.lasty),
            mfont(default_font::get_font()),
            z_order_value(0),
            events(events_),
            enabled_events(false),
            event_id(0)
        {}
        virtual ~drawable (
        );
        long z_order (
        ) const
        {
            m.lock();
            long temp = z_order_value;
            m.unlock();
            return temp;
        }
        virtual void set_z_order (
            long order
        );
        const rectangle get_rect (
        ) const 
        {
            auto_mutex M(m);
            return rect;
        }
        long bottom (
        ) const 
        { 
            auto_mutex M(m); 
            return rect.bottom(); 
        }
        long top (
        ) const 
        { 
            auto_mutex M(m); 
            return rect.top(); 
        }
        long left (
        ) const 
        { 
            auto_mutex M(m); 
            return rect.left(); 
        }
        long right (
        ) const 
        { 
            auto_mutex M(m); 
            return rect.right(); 
        }
        long width (
        ) const 
        { 
            auto_mutex M(m); 
            return rect.width(); 
        }
        long height (
        ) const 
        { 
            auto_mutex M(m); 
            return rect.height(); 
        }
        bool is_enabled (
        ) const
        {
            auto_mutex M(m);
            return enabled;
        }
        virtual void enable (
        ) 
        {
            auto_mutex M(m);
            enabled = true;
            parent.invalidate_rectangle(rect);
        }
        virtual void disable (
        ) 
        {
            auto_mutex M(m);
            enabled = false;
            parent.invalidate_rectangle(rect);
        }
        virtual void set_main_font (
            const std::shared_ptr<font>& f
        )
        {
            auto_mutex M(m);
            mfont = f;
            parent.invalidate_rectangle(rect);
        }
        const std::shared_ptr<font> main_font (
        ) const
        {
            auto_mutex M(m);
            return mfont;
        }
        bool is_hidden (
        ) const
        {
            auto_mutex M(m);
            return hidden;
        }
        virtual void set_pos (
            long x,
            long y
        )
        {
            m.lock();       
            rectangle old(rect);            
            const unsigned long width = rect.width();
            const unsigned long height = rect.height();
            rect.set_top(y);
            rect.set_left(x);
            rect.set_right(static_cast<long>(x+width)-1);
            rect.set_bottom(static_cast<long>(y+height)-1);
            
            parent.invalidate_rectangle(rect+old);
            m.unlock();
        }
        virtual void show (
        )
        {
            m.lock();
            hidden = false;
            parent.invalidate_rectangle(rect);
            m.unlock();
        }
        virtual void hide (
        )
        {
            m.lock();
            hidden = true;
            parent.invalidate_rectangle(rect);
            m.unlock();
        }
        base_window& parent_window (
        ) { return parent; }
        const base_window& parent_window (
        ) const { return parent; }
        virtual int next_free_user_event_number (
        )const { return 0; }
    protected:   
        rectangle rect;
        const rmutex& m;
        drawable_window& parent;
        bool hidden;
        bool enabled;
        const long& lastx;
        const long& lasty;
        std::shared_ptr<font> mfont;
        
        void enable_events (
        );
        bool events_are_enabled (
        ) const { auto_mutex M(m); return enabled_events; }
        void disable_events (
        );
    private:
        long z_order_value;
        const unsigned long events;
        bool enabled_events;
        unsigned long event_id;
        // restricted functions
        drawable(drawable&);        // copy constructor
        drawable& operator=(drawable&);    // assignment operator
    protected:
        virtual void draw (
            const canvas& c
        ) const=0;
        virtual void on_user_event (
            int 
        ){}
        virtual void on_window_resized(
        ){}
        virtual void on_window_moved(
        ){}
               
        virtual void on_mouse_down (
            unsigned long ,
            unsigned long ,
            long ,
            long ,
            bool 
        ){}
        virtual void on_mouse_up (
            unsigned long ,
            unsigned long ,
            long ,
            long 
        ){}
        virtual void on_mouse_move (
            unsigned long ,
            long ,
            long 
        ){}
        virtual void on_mouse_leave (
        ){}
        virtual void on_mouse_enter (
        ){}
        virtual void on_wheel_up (
            unsigned long 
        ){}
        virtual void on_wheel_down (
            unsigned long 
        ){}
        virtual void on_focus_gained (
        ){}
        virtual void on_focus_lost (
        ){}
        virtual void on_keydown (
            unsigned long ,
            bool ,
            unsigned long 
        ){}
        virtual void on_string_put (
            const std::wstring&
        ){}
    };
// ----------------------------------------------------------------------------------------
}
#ifdef NO_MAKEFILE
#include "drawable.cpp"
#endif
#endif // DLIB_DRAWABLe_