// Copyright (C) 2005  Davis E. King (davisking@users.sourceforge.net), Keita Mochizuki
// License: Boost Software License   See LICENSE.txt for the full license.
#ifndef DLIB_GUI_CORE_KERNEl_2_
#define DLIB_GUI_CORE_KERNEl_2_

#ifdef DLIB_ISO_CPP_ONLY
#error "DLIB_ISO_CPP_ONLY is defined so you can't use this OS dependent code.  Turn DLIB_ISO_CPP_ONLY off if you want to use it."
#endif

#ifdef DLIB_NO_GUI_SUPPORT
#error "DLIB_NO_GUI_SUPPORT is defined so you can't use the GUI code.  Turn DLIB_NO_GUI_SUPPORT off if you want to use it."
#error "Also make sure you have libx11-dev installed on your system"
#endif

#include <string>

#include "gui_core_kernel_abstract.h"
#include "../algs.h"
#include "../threads.h"
#include "../geometry/rectangle.h"
#include "../binary_search_tree.h"
#include <string.h>
#include "../pixel.h"
#include "../unicode.h"


namespace dlib
{

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

    namespace gui_core_kernel_2_globals
    {
        class event_handler_thread;
        void trigger_user_event_threadproc (void*);

        // This is a forward declaration for a struct that contains any 
        // X11 variables.  This allows me to avoid having any dlib header files
        // include the X11 headers.  Which in turn speeds build times and simplifies
        // build setups.
        struct x11_base_windowstuff;
    }

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

    void put_on_clipboard (
        const std::string& str
    );

    void put_on_clipboard (
        const std::wstring& str
    );

    void put_on_clipboard (
        const dlib::ustring& str
    );

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

    void get_from_clipboard (
        std::string& str
    );

    void get_from_clipboard (
        std::wstring& str
    );

    void get_from_clipboard (
        dlib::ustring& str
    );

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

    class canvas : public rectangle
    {
    public:
        struct pixel
        {
            unsigned char blue;
            unsigned char green;
            unsigned char red;
        private:
            friend class canvas;
            unsigned char _padding;
        };

        ~canvas() {}

        inline pixel* operator[] (
            unsigned long row
        ) const
        {
            DLIB_ASSERT(row < height(),
                "\tpixel* canvas::operator[]"
                << "\n\tyou have to give a row that is less than the height()"
                << "\n\tthis:     " << this
                << "\n\trow:      " << row 
                << "\n\theight(): " << height() 
                );
            unsigned char* temp = bits + row_width*row;
            return reinterpret_cast<pixel*>(temp);
        }

        void fill (
            unsigned char red_,
            unsigned char green_,
            unsigned char blue_
        ) const;

    private:

        friend class gui_core_kernel_2_globals::event_handler_thread;


        canvas (
            unsigned char* bits__,
            unsigned long left__,
            unsigned long top__,            
            unsigned long right__,            
            unsigned long bottom__   
        ) : 
            rectangle(left__,top__,right__,bottom__),
            bits(bits__),
            width_(width()),
            height_(height()),
            row_width(width_*4)
        {}

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

        unsigned char* const bits;
        const unsigned long width_;
        const unsigned long height_;
        const unsigned long row_width;
    };

    template <>
    struct pixel_traits<canvas::pixel>
    {
        const static bool rgb  = true;
        const static bool rgb_alpha  = false;
        const static bool grayscale = false;
        const static bool hsi = false;
        const static long num = 3;
        static unsigned long max() { return 255;}
        const static bool has_alpha = false;
    };

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

    class base_window
    {
        friend class gui_core_kernel_2_globals::event_handler_thread;
        friend void gui_core_kernel_2_globals::trigger_user_event_threadproc (void*);

    public:

        enum  mouse_state_masks
        {
            NONE = 0,
            LEFT = 1,
            RIGHT = 2,
            MIDDLE = 4,
            SHIFT = 8,
            CONTROL = 16
        };

        enum keyboard_state_masks
        {
            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 on_close_return_code
        {
            DO_NOT_CLOSE_WINDOW,
            CLOSE_WINDOW
        };

        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
        };

    private:

        gui_core_kernel_2_globals::x11_base_windowstuff& x11_stuff;

        int x, y, width, height;
        bool is_mapped;

        const bool resizable;
        bool has_been_destroyed;
        bool has_been_resized;  // true if someone called set_size() and the on_window_resized() event 
                                // hasn't yet occurred.
        bool has_been_moved;    // true if someone called set_pos() and the on_window_moved() event
                                // hasn't yet occurred.


        // The following 3 variables (and x11_stuff.last_click_time) are only accessed from the 
        // event handling loop (except for being initialized below). They record the last 
        // mouse click event details.
        long last_click_x, last_click_y;
        unsigned long last_click_button;


    protected:
        const rmutex& wm; 

    public:

        base_window (
            bool resizable_ = true,
            bool undecorated = false
        );

        virtual ~base_window (
        );

        void close_window (
        );

        void wait_until_closed (
        ) const;

        void set_im_pos (
            long x_,
            long y_
        );

        bool is_closed (
        ) const;

        void set_title (
            const std::string& title_
        );

        void set_title (
            const std::wstring& title_
        );

        void set_title (
            const dlib::ustring& title_
        );

        virtual void show (
        );    

        virtual void hide(
        );    

        void set_size (
            int width_,
            int height_
        );

        void set_pos (
            long x_,
            long y_
        );

        void get_pos (
            long& x_,
            long& y_
        );

        void get_size (
            unsigned long& width_,
            unsigned long& height_
        ) const;

        void get_display_size (
            unsigned long& width,
            unsigned long& height
        ) const;

        void invalidate_rectangle (
            const rectangle& rect
        );

        void trigger_user_event (
            void* p,
            int i
        );

    protected:

        virtual on_close_return_code on_window_close(
        ){return CLOSE_WINDOW;}

        virtual void on_window_resized(
        ){}

        virtual void on_window_moved(
        ){}
        virtual void on_user_event (
            void* p,
            int i
        ){}

        virtual void on_mouse_down (
            unsigned long btn,
            unsigned long state,
            long x,
            long y,
            bool is_double_click
        ){}

        virtual void on_mouse_up (
            unsigned long btn,
            unsigned long state,
            long x,
            long y
        ){}

        virtual void on_mouse_move (
            unsigned long state,
            long x,
            long y
        ){}

        virtual void on_mouse_leave (
        ){}

        virtual void on_mouse_enter (
        ){}

        virtual void on_wheel_up (
            unsigned long state
        ){}

        virtual void on_wheel_down (
            unsigned long state
        ){}

        virtual void on_focus_gained (
        ){}

        virtual void on_focus_lost (
        ){}

        virtual void on_keydown (
            unsigned long key,            
            bool is_printable,
            unsigned long state
        ){}

        virtual void on_string_put (
            const std::wstring &str
        ){}

    private:

        virtual void paint (
            const canvas& c
        ) =0;



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

    };

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


}


#ifdef NO_MAKEFILE
#include "gui_core_kernel_2.cpp"
#endif

#endif // DLIB_GUI_CORE_KERNEl_2_

