// Copyright (C) 2005  Davis E. King (davis@dlib.net), Keita Mochizuki
// License: Boost Software License   See LICENSE.txt for the full license.
#undef DLIB_GUI_CORE_KERNEl_ABSTRACT_
#ifdef DLIB_GUI_CORE_KERNEl_ABSTRACT_

#include <string>
#include "../algs.h"
#include "../geometry/rectangle_abstract.h"
#include "../unicode/unicode_abstract.h"

namespace dlib
{

    /*!
        OVERVIEW:
            This is a set of objects and functions which provide a very basic
            framework for manipulating windows.  It is intended to provide a 
            portable interface which can be used to build a more complex windowing 
            toolkit.

        EXCEPTIONS
            Do not let an exception leave any of the base_window event handlers. 
            The results of doing so are undefined.

        THREAD SAFETY
            Event Handlers
                All event handlers are executed in a special event handling thread. 
                This means that you must not do anything that will take a long time or
                block while in an event handler.  Doing so will freeze all event 
                processing.  
                
                Also, don't rely on get_thread_id() always returning the same ID from
                inside event handlers.

            canvas
                Never access a canvas object outside of the paint() callback
                that supplied it.  Only access a canvas object from the event 
                handling thread.  After the paint() event handler has returned do 
                not access that canvas object again.

            base_window
                All methods for this class are thread safe.  You may call them 
                from any thread and do not need to serialize access.
    !*/

// ----------------------------------------------------------------------------------------

    void put_on_clipboard (
        const std::string& str
    );
    /*!
        ensures
            - posts the contents of str to the system clipboard
        throws
            - std::bad_alloc
            - dlib::gui_error
            - dlib::thread_error
    !*/

    // overloads for wide character strings
    void put_on_clipboard (const std::wstring& str);
    void put_on_clipboard (const dlib::ustring& str);

// ----------------------------------------------------------------------------------------

    void get_from_clipboard (
        std::string& str
    );
    /*!
        ensures
            - if (there is string data on the system clipboard) then
                - #str == the data from the clipboard
            - else
                - #str == ""
        throws
            - std::bad_alloc
            - dlib::gui_error
            - dlib::thread_error
    !*/

    // overloads for wide character strings
    void get_from_clipboard (std::wtring& str);
    void get_from_clipboard (dlib::utring& str);

// ----------------------------------------------------------------------------------------


    class canvas : public rectangle
    {
        /*!
            POINTERS AND REFERENCES TO INTERNAL DATA
                All functions of this object may invalidate pointers and references
                to internal data.  

            INITIAL VALUE
                The initial value of each pixel is undefined.  
                is_empty() == false

            WHAT THIS OBJECT REPRESENTS
                This object represents a rectangular area of a window that you 
                can draw on. 

                Each pixel can be accessed with the following syntax:
                    canvas_instance[y][x].red   == the red value for this pixel
                    canvas_instance[y][x].blue  == the blue value for this pixel
                    canvas_instance[y][x].green == the green value for this pixel

                The origin, i.e. (0,0), of the x,y coordinate plane of the canvas is in 
                the upper left corner of the canvas.  Note that the upper left corner 
                of the canvas appears at the point (left(),top()) in its window.
        !*/

    public:

        struct pixel
        {
            /*!
                WHAT THIS OBJECT REPRESENTS
                    This object represents a single pixel.  Each pixel's value
                    ranges from 0 to 255 with 0 indicating that the color is not
                    present in the pixel at all and 255 indicating that the color
                    is present in the pixel with maximum intensity.

                    Note that the structure, order, and size of of this struct are 
                    implementation dependent.  It will always contain fields called 
                    red, green, and blue but they may not be in that order and there 
                    may be padding.  

                    Also note that pixel_traits<> is defined for this pixel type,
                    thus you can use it in assign_pixel() calls.
            !*/
            unsigned char red;
            unsigned char green;
            unsigned char blue;
        };


        pixel* operator[] (
            unsigned long row
        ) const;
        /*!
            requires
                - row < height()
            ensures
                - returns an array of width() pixel structs that represents the given
                  row of pixels in the canvas.  
        !*/

        void fill (
            unsigned char red,
            unsigned char green,
            unsigned char blue
        ) const;
        /*!
            ensures
                - for all valid values of x and y:
                    - (#*this)[y][x].red = red
                    - (#*this)[y][x].green = green
                    - (#*this)[y][x].blue = blue
        !*/
            
    private:

        // restricted functions
        canvas();        // normal constructor
        canvas(canvas&);        // copy constructor
        canvas& operator=(canvas&);    // assignment operator    
    };

// ----------------------------------------------------------------------------------------

    class base_window
    {

        /*!
            WHAT THIS OBJECT REPRESENTS
                This object represents a window on the desktop.  A window has a "client 
                area" that is a region of the screen that you can draw whatever you like 
                on.  You implement the paint() callback and use the canvas object to do 
                this drawing.

            INITIAL STATE
                - The initial state of the window is to be hidden.  This means you need
                  to call show() to make it appear.
                - is_closed() == false

            paint() callback:
                This is where you will do all your drawing.  It is triggered when
                part of the window needs to be drawn/redrawn.

            mouse events:
                It is important to note a few things about the mouse events.  First,
                the on_mouse_move() event is not triggered for each pixel the mouse crosses
                but rather its frequency and precision is implementation dependent.  
                
                Second, it is possible that a mouse button may be depressed but the 
                corresponding button release event does not go to the window.  For instance, 
                if the mouse is outside the window and some other application jumps to the 
                top it is possible that the new application will receive any mouse button 
                release events rather than the original window.  But the point is that 
                you should not rely on always getting a button up event for every button
                down event.

            keydown event:
                Note that the existence of a typematic action (holding down a key
                and having it start to repeat itself after a moment) for each key is
                totally implementation dependent.  So don't rely on it for any key
                and conversely don't assume it isn't present either.  

            The base_window::wm mutex
                This is a reference to a global rmutex.  All instances of base_window make
                reference to the same global rmutex.  It is used to synchronize access to 
                the base_window to make it thread safe.  It is also always locked before 
                an event handler is called.
        !*/

    public:

        enum on_close_return_code
        {
            DO_NOT_CLOSE_WINDOW,
            CLOSE_WINDOW
        };

        enum mouse_state_masks
        {
            /*!
                These constants represent the various buttons referenced by
                mouse events.
            !*/
            NONE = 0,
            LEFT = 1,
            RIGHT = 2,
            MIDDLE = 4,
            SHIFT = 8,
            CONTROL = 16
        };

        enum keyboard_state_masks
        {
            /*!
                These constants represent the various modifier buttons that
                could be in effect during a key press on the keyboard
            !*/
            KBD_MOD_NONE = 0,
            KBD_MOD_SHIFT = 1,
            KBD_MOD_CONTROL = 2,
            KBD_MOD_ALT = 4,
            KBD_MOD_META = 8,
            KBD_MOD_CAPS_LOCK = 16,
            KBD_MOD_NUM_LOCK = 32,
            KBD_MOD_SCROLL_LOCK = 64
        };

        enum non_printable_keyboard_keys
        {
            KEY_BACKSPACE,
            KEY_SHIFT,
            KEY_CTRL,
            KEY_ALT,
            KEY_PAUSE,
            KEY_CAPS_LOCK,
            KEY_ESC,
            KEY_PAGE_UP,
            KEY_PAGE_DOWN,
            KEY_END,
            KEY_HOME,
            KEY_LEFT,           // This is the left arrow key
            KEY_RIGHT,          // This is the right arrow key
            KEY_UP,             // This is the up arrow key
            KEY_DOWN,           // This is the down arrow key
            KEY_INSERT,
            KEY_DELETE,
            KEY_SCROLL_LOCK,
  
            // Function Keys
            KEY_F1,
            KEY_F2,
            KEY_F3,
            KEY_F4,
            KEY_F5,
            KEY_F6,
            KEY_F7,
            KEY_F8,
            KEY_F9,
            KEY_F10,
            KEY_F11,
            KEY_F12
        };

        base_window (
            bool resizable = true,
            bool undecorated = false
        );
        /*!
            requires
                - if (undecorated == true) then
                    - resizable == false
            ensures
                - #*this has been properly initialized 
                - if (resizable == true) then 
                    - this window will be resizable by the user
                - else 
                    - this window will not be resizable by the user
                - if (undecorated == true) then
                    - this window will not have any graphical elements outside
                      of its drawable area or appear in the system task bar. It
                      also won't take the input focus from other windows.
                      (it is suitable for making things such as popup menus)
            throws
                - std::bad_alloc
                - dlib::thread_error
                - dlib::gui_error
                    This exception is thrown if there is an error while 
                    creating this window.
        !*/

        virtual ~base_window (
        );
        /*!
            ensures
                - does NOT trigger the on_window_close() event
                - all resources associated with *this have been released                
                - closes this window
        !*/

        void close_window (
        );
        /*!
            ensures
                - #is_closed() == true
                  (i.e. permanently closes this window.  The window is removed from the 
                  screen and no more events will be dispatched to this window. )
                - does NOT trigger the on_window_close() event
        !*/

        void wait_until_closed (
        ) const;
        /*!
            ensures
                - blocks until is_closed() == true 
        !*/

        bool is_closed (
        ) const;
        /*!
            ensures
                - returns true if this window has been closed, false otherwise.
                  (Note that closed windows do not receive any callbacks at all.
                   They are also not visible on the screen.)
        !*/

        void set_title (
            const std::string& title
        );
        /*!
            ensures
                - if (is_closed() == false) then
                    - sets the title of the window
        !*/

        void set_title (
            const std::wstring& title
        );
        /*!
            ensures
                - if (is_closed() == false) then
                    - sets the title of the window
        !*/

        void set_title (
            const dlib::ustring& title
        );
        /*!
            ensures
                - if (is_closed() == false) then
                    - sets the title of the window
        !*/

        virtual void show (
        );
        /*!
            ensures
                - if (is_closed() == false) then
                    - this window will appear on the screen
        !*/

        virtual void hide(
        );
        /*!
            ensures
                - if (is_closed() == false) then
                    - the window does not appear on the screen
        !*/

        void set_size (
            int width,
            int height
        );
        /*!
            ensures
                - if (is_closed() == false) then
                    - The width of the client area of this window is at least width
                      pixels.
                    - The height of the client area of this window is at least height
                      pixels.
                    - if (the window wasn't already this size) then
                        - triggers the on_window_resized() callback
        !*/

        void set_pos (
            long x,
            long y
        );
        /*!
            ensures 
                - if (is_closed() == false) then
                    - sets the upper left corner of this window to the position (x,y) 
                      on the desktop.  Note that the origin (0,0) is at the upper left
                      corner of the desktop.
        !*/

        void get_pos (
            long& x,
            long& y
        ) const;
        /*!
            ensures
                - if (is_closed() == false) then
                    - #x == the x coordinate of the upper left corner of the client area of
                      this window.
                    - #y == the y coordinate of the upper left corner of the client area of
                      this window.
                    - i.e. the point (#x,#y) on the desktop is coincident with the point
                      (0,0) in the client area of this window.
                - else
                    - #x == 0
                    - #y == 0
        !*/

        void get_size (
            unsigned long& width,
            unsigned long& height
        ) const;
        /*!
            ensures
                - if (is_closed() == false) then
                    - #width == the width of the client area of this window in pixels
                    - #height == the height of the client area of this window in pixels
                - else
                    - #width == 0
                    - #height == 0
        !*/

        void get_display_size (
            unsigned long& width,
            unsigned long& height
        ) const;
        /*!
            ensures
                - if (is_closed() == false) then
                    - #width == the width in pixels of the display device that contains this window 
                    - #height == the height in pixels of the display device that contains this window 
                - else
                    - #width == 0
                    - #height == 0
        !*/

        void invalidate_rectangle (
            const rectangle& rect
        );
        /*!
            ensures
                - if (is_closed() == false) then
                    - causes the area of this window defined by rect to become invalid.
                      This means that a paint() message will be dispatched to repaint
                      this area of the window.  Note that it is possible that the 
                      resulting paint() message may include a bigger rectangle than
                      the one defined by rect.
        !*/

        void trigger_user_event (
            void* p,
            int i
        );
        /*!
            ensures
                - will never block (even if some other thread has a lock on the
                  global mutex referenced by wm.)
                - if (is_closed() == false) then
                    - causes the on_user_event() event to be called with 
                      the given arguments.
        !*/

        void set_im_pos (
            long x_,
            long y_
        );
        /*!
            ensures
                - if (is_closed() == false) then
                    - sets the left-top position of input method rectangle used
                      for wide character input methods.
        !*/

    protected:
        const rmutex& wm;

        // let the window close by default
        virtual on_close_return_code on_window_close(
        ){return CLOSE_WINDOW;}
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called when the user attempts to close this window
                - if (this function returns CLOSE_WINDOW) then
                    - #is_closed() == true  (i.e. this window will be closed)
                    - it is safe to call "delete this;" inside on_window_close() 
                      if *this was allocated on the heap and no one will try to 
                      access *this anymore.
                - else
                    - this window will not be closed and the attempt to close it
                      by the user will have no effect. 
                    - #is_closed() == false
            ensures
                - does not change the state of mutex wm
        !*/

        // do nothing by default
        virtual void on_user_event (
            void* p,
            int i
        ){}
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called whenever someone calls trigger_user_event()
            ensures
                - does not change the state of mutex wm
        !*/

        // do nothing by default
        virtual void on_window_resized(
        ){}
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called when this window is resized
            ensures
                - does not change the state of mutex wm
        !*/
             
        // do nothing by default
        virtual void on_window_moved(
        ){}
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called when this window's position changes 
            ensures
                - does not change the state of mutex wm
        !*/
             
        // do nothing by default  
        virtual void on_mouse_down (
            unsigned long btn,
            unsigned long state,
            long x,
            long y,
            bool is_double_click
        ){}
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called when the user depresses one of the mouse buttons
                - btn == the button that was depressed. (either LEFT, MIDDLE, or RIGHT)
                - state == the bitwise OR of the buttons that are currently depressed 
                  excluding the button given by btn. (from the mouse_state_masks enum) 
                - (x,y) == the position of the mouse (relative to the upper left corner
                  of the window) when this event occurred.  Note that the mouse may be
                  outside the window.
                - if (this is the second button press of a double click) then
                    - is_double_click == true
                - else
                    - is_double_click == false
            ensures
                - does not change the state of mutex wm
        !*/

        // do nothing by default
        virtual void on_mouse_up (
            unsigned long btn,
            unsigned long state,
            long x,
            long y
        ){}
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called when the user releases one of the mouse buttons
                - btn == the button that was released. (either LEFT, MIDDLE, or RIGHT)
                - state == the bitwise OR of the buttons that are currently depressed
                  (from the mouse_state_masks enum)
                - (x,y) == the position of the mouse (relative to the upper left corner
                  of the window) when this event occurred.  Note that the mouse may be
                  outside the window.
            ensures
                - does not change the state of mutex wm
        !*/

        // do nothing by default
        virtual void on_mouse_move (
            unsigned long state,
            long x,
            long y
        ){}
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called when the user moves the mouse
                - state == the bitwise OR of the buttons that are currently depressed
                  (from the mouse_state_masks enum)
                - (x,y) == the position of the mouse (relative to the upper left corner
                  of the window) when this event occurred. 
                - if (the user is holding down one or more of the mouse buttons) then
                    - the mouse move events will continue to track the mouse even if
                      it goes out of the window.  This will continue until the user
                      releases all the mouse buttons.
            ensures
                - does not change the state of mutex wm
        !*/

        // do nothing by default
        virtual void on_mouse_leave (
        ){}
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called when the mouse leaves this window
            ensures
                - does not change the state of mutex wm
        !*/

        // do nothing by default
        virtual void on_mouse_enter (
        ){}
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called when the mouse enters this window
            ensures
                - does not change the state of mutex wm
        !*/

        // do nothing by default
        virtual void on_focus_gained (
        ){}
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called when this window gains input focus 
            ensures
                - does not change the state of mutex wm
        !*/

        // do nothing by default
        virtual void on_focus_lost (
        ){}
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called when this window loses input focus 
            ensures
                - does not change the state of mutex wm
        !*/

        // do nothing by default
        virtual void on_wheel_up (
            unsigned long state
        ){}
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called every time the mouse wheel is scrolled up one notch
                - state == the bitwise OR of the buttons that are currently depressed 
                  (from the mouse_state_masks enum) 
            ensures
                - does not change the state of mutex wm
        !*/

        // do nothing by default
        virtual void on_wheel_down (
            unsigned long state
        ){}
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called every time the mouse wheel is scrolled down one notch
                - state == the bitwise OR of the buttons that are currently depressed 
                  (from the mouse_state_masks enum) 
            ensures
                - does not change the state of mutex wm
        !*/

        // do nothing by default
        virtual void on_keydown (
            unsigned long key,
            bool is_printable,
            unsigned long state
        ){}
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called when a keyboard key is pressed or if a key is held
                  down then this is called repeatedly at a certain rate once the
                  typematic action begins (note that some keys might not have any 
                  typematic action on some platforms).
                - if (is_printable) then
                    - key == the character that was pressed. (e.g. 'a', 'b', '1' etc.)
                    - this is a printable character.  Note that ' ', '\t', and 
                      '\n' (this is the return/enter key) are all considered printable.
                - else
                    - key == one of the non_printable_keyboard_keys enums.  
                - state == the bitwise OR of the keyboard modifiers that are currently
                  depressed (taken from keyboard_state_masks).  
                - if (key is not in the range 'a' to 'z' or 'A' to 'Z') then
                    - if (the shift key was down when this key was pressed) then                    
                        - (state & KBD_MOD_SHIFT) != 0 
                    - else
                        - (state & KBD_MOD_SHIFT) == 0 
                - else
                    - the state of the shift key is implementation defined
            ensures
                - does not change the state of mutex wm
        !*/

        virtual void on_string_put (
            const std::wstring &str
        ){}
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called when a wide/multibyte character input method determines a string
                  that is being input to the window.
                - str == the string that is being input
            ensures
                - does not change the state of mutex wm
        !*/

    private:

        virtual void paint (
            const canvas& c
        ) =0;
        /*!
            requires
                - is_closed() == false
                - mutex wm is locked
                - is called when part of the window needs to be repainted for 
                  any reason.
                - c == a canvas object that represents the invalid area of this
                  window which needs to be painted.
            ensures
                - does not change the state of mutex wm
        !*/

        base_window(base_window&);        // copy constructor
        base_window& operator=(base_window&);    // assignment operator

    };

// ----------------------------------------------------------------------------------------

}

#endif // DLIB_GUI_CORE_KERNEl_ABSTRACT_