// Copyright (C) 2008  Davis E. King (davisking@users.sourceforge.net), and Nils Labugt
// License: Boost Software License   See LICENSE.txt for the full license.
#ifndef DLIB_WIDGETs_STYLE_
#define DLIB_WIDGETs_STYLE_

#include "../algs.h"
#include "style_abstract.h"
#include "../gui_core.h"
#include "canvas_drawing.h"
#include <string>
#include <sstream>
#include "../unicode.h"
#include "../array2d.h"
#include "../pixel.h"
#include "fonts.h"


namespace dlib
{

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    // button styles  
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    class button_style
    {
    public:

        button_style()
        {
        }

        virtual ~button_style() 
        {}

        virtual bool redraw_on_mouse_over (
        ) const { return false; }

        virtual rectangle get_invalidation_rect (
            const rectangle& rect
        ) const { return rect; }

        virtual rectangle get_min_size (
            const ustring& name,
            const font& mfont 
        ) const = 0;

        virtual void draw_button (
            const canvas& c,
            const rectangle& rect,
            const bool enabled,
            const font& mfont,
            const long lastx,
            const long lasty,
            const ustring& name,
            const bool is_depressed
        ) const = 0;
    };

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

    class button_style_default : public button_style
    {
    public:
        button_style_default () : padding(4), name_width(0) {}

        virtual void draw_button (
            const canvas& c,
            const rectangle& rect,
            const bool enabled,
            const font& mfont,
            const long lastx,
            const long lasty,
            const ustring& name,
            const bool is_depressed
        ) const;

        virtual rectangle get_min_size (
            const ustring& name,
            const font& mfont 
        ) const;

    private:

        // this is the minimum amount of padding that can separate the name from the
        // edge of the button
        const unsigned long padding;
        // this is the width of the name string
        mutable unsigned long name_width;

    };

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

    class button_style_toolbar1 : public button_style
    {
    public:
        button_style_toolbar1 () : padding(4), name_width(0) {}

        virtual void draw_button (
            const canvas& c,
            const rectangle& rect,
            const bool enabled,
            const font& mfont,
            const long lastx,
            const long lasty,
            const ustring& name,
            const bool is_depressed
        ) const;

        virtual rectangle get_invalidation_rect (
            const rectangle& rect
        ) const 
        { 
            rectangle temp(rect);
            temp.left() -= 2;
            temp.top() -= 2;
            temp.right() += 2;
            temp.bottom() += 2;
            return temp; 
        }

        virtual bool redraw_on_mouse_over (
        ) const { return true; }

        virtual rectangle get_min_size (
            const ustring& name,
            const font& mfont 
        ) const;

    private:

        // this is the minimum amount of padding that can separate the name from the
        // edge of the button
        const unsigned long padding;
        // this is the width of the name string
        mutable unsigned long name_width;

    };

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

    class button_style_toolbar_icon1 : public button_style
    {
    public:
        template <typename image_type>
        button_style_toolbar_icon1 (const image_type& img_, unsigned long pad = 6) : padding(pad) 
        { 
            assign_image(img_mouseover,img_); 
            make_images();  
        }

        button_style_toolbar_icon1( const button_style_toolbar_icon1& item): padding(item.padding) 
        {
            assign_image(img_mouseover, item.img_mouseover);
            assign_image(img_normal, item.img_normal);
            assign_image(img_disabled, item.img_disabled);
        }

        virtual void draw_button (
            const canvas& c,
            const rectangle& rect,
            const bool enabled,
            const font& mfont,
            const long lastx,
            const long lasty,
            const ustring& name,
            const bool is_depressed
        ) const;

        virtual bool redraw_on_mouse_over (
        ) const { return true; }

        virtual rectangle get_min_size (
            const ustring& name,
            const font& mfont 
        ) const;

    private:

        void make_images (
        )
        {
            // make the disabled image grayscale and make both non-mouseover images have weaker alpha channels
            img_disabled.set_size(img_mouseover.nr(), img_mouseover.nc());
            img_normal.set_size(img_mouseover.nr(), img_mouseover.nc());

            for (long r = 0; r < img_mouseover.nr(); ++r)
            {
                for (long c = 0; c < img_mouseover.nc(); ++c)
                {
                    rgb_alpha_pixel p = img_mouseover[r][c];
                    long avg = p.red;
                    avg += p.green;
                    avg += p.blue;
                    avg /= 3;

                    if (p.alpha > 40)
                        p.alpha -= 40;
                    else
                        p.alpha = 0;

                    img_normal[r][c] = p;

                    if (p.alpha > 80)
                        p.alpha -= 80;
                    else
                        p.alpha = 0;

                    p.red = avg;
                    p.green = avg;
                    p.blue = avg;
                    img_disabled[r][c] = p;
                }
            }
        }

        array2d<rgb_alpha_pixel>::kernel_1a img_mouseover;
        array2d<rgb_alpha_pixel>::kernel_1a img_normal;
        array2d<rgb_alpha_pixel>::kernel_1a img_disabled;

        // this is the minimum amount of padding that can separate the name from the
        // edge of the button
        const unsigned long padding;

    };

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

    class button_style_arrow : public button_style
    {

    public:

        enum arrow_direction 
        {
            UP,
            DOWN,
            LEFT,
            RIGHT
        };

        button_style_arrow (
            arrow_direction dir_
        ) : dir(dir_) {}

        virtual void draw_button (
            const canvas& c,
            const rectangle& rect,
            const bool enabled,
            const font& mfont,
            const long lastx,
            const long lasty,
            const ustring& name,
            const bool is_depressed
        ) const;

        virtual rectangle get_min_size (
            const ustring& name,
            const font& mfont 
        ) const { return rectangle(); }

    private:
        arrow_direction dir;
    };

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    // toggle button styles  
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    class toggle_button_style
    {
    public:

        toggle_button_style()
        {
        }

        virtual ~toggle_button_style() 
        {}

        virtual bool redraw_on_mouse_over (
        ) const { return false; }

        virtual rectangle get_min_size (
            const ustring& name,
            const font& mfont 
        ) const = 0;

        virtual void draw_toggle_button (
            const canvas& c,
            const rectangle& rect,
            const bool enabled,
            const font& mfont,
            const long lastx,
            const long lasty,
            const ustring& name,
            const bool is_depressed,
            const bool is_checked
        ) const = 0;
    };

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

    class toggle_button_style_default : public toggle_button_style
    {
    public:
        toggle_button_style_default () : padding(4), name_width(0) {}

        virtual void draw_toggle_button (
            const canvas& c,
            const rectangle& rect,
            const bool enabled,
            const font& mfont,
            const long lastx,
            const long lasty,
            const ustring& name,
            const bool is_depressed,
            const bool is_checked
        ) const;

        virtual rectangle get_min_size (
            const ustring& name,
            const font& mfont 
        ) const;

    private:

        // this is the minimum amount of padding that can separate the name from the
        // edge of the button
        const unsigned long padding;
        // this is the width of the name string
        mutable unsigned long name_width;

    };

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

    class toggle_button_style_check_box : public toggle_button_style
    {
    public:
        virtual void draw_toggle_button (
            const canvas& c,
            const rectangle& rect,
            const bool enabled,
            const font& mfont,
            const long lastx,
            const long lasty,
            const ustring& name,
            const bool is_depressed,
            const bool is_checked
        ) const;

        virtual rectangle get_min_size (
            const ustring& name,
            const font& mfont 
        ) const;

    };

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

    class toggle_button_style_radio_button : public toggle_button_style
    {
    public:
        virtual void draw_toggle_button (
            const canvas& c,
            const rectangle& rect,
            const bool enabled,
            const font& mfont,
            const long lastx,
            const long lasty,
            const ustring& name,
            const bool is_depressed,
            const bool is_checked
        ) const;

        virtual rectangle get_min_size (
            const ustring& name,
            const font& mfont 
        ) const;

    };

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    // scroll_bar styles  
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    class scroll_bar_style
    {
    public:

        virtual ~scroll_bar_style() {}

        virtual bool redraw_on_mouse_over_slider (
        ) const { return false; }

        virtual long get_width (
        ) const = 0;

        virtual long get_slider_length (
            long total_length,
            long max_pos
        ) const = 0;

        virtual long get_button_length (
            long total_length,
            long max_pos
        ) const = 0;

        virtual void draw_scroll_bar_background (
            const canvas& c,
            const rectangle& rect,
            const bool enabled,
            const long lastx,
            const long lasty,
            const bool is_depressed
        ) const = 0;

        virtual void draw_scroll_bar_slider (
            const canvas& c,
            const rectangle& rect,
            const bool enabled,
            const long lastx,
            const long lasty,
            const bool is_being_dragged
        ) const = 0;

    };

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

    class scroll_bar_style_default : public scroll_bar_style
    {
    public:
        button_style_arrow get_up_button_style (
        ) const { return button_style_arrow(button_style_arrow::UP); }

        button_style_arrow get_down_button_style (
        ) const { return button_style_arrow(button_style_arrow::DOWN); }

        button_style_arrow get_left_button_style (
        ) const { return button_style_arrow(button_style_arrow::LEFT); }

        button_style_arrow get_right_button_style (
        ) const { return button_style_arrow(button_style_arrow::RIGHT); }

        virtual long get_width (
        ) const  { return 16; }

        virtual long get_slider_length (
            long total_length,
            long max_pos
        ) const;

        virtual long get_button_length (
            long total_length,
            long max_pos
        ) const;

        virtual void draw_scroll_bar_background (
            const canvas& c,
            const rectangle& rect,
            const bool enabled,
            const long lastx,
            const long lasty,
            const bool is_depressed
        ) const;

        virtual void draw_scroll_bar_slider (
            const canvas& c,
            const rectangle& rect,
            const bool enabled,
            const long lastx,
            const long lasty,
            const bool is_being_dragged
        ) const;
    };

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    // scrollable_region styles  
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    class scrollable_region_style
    {
    public:

        virtual ~scrollable_region_style() {}

        virtual long get_border_size (
        ) const = 0;

        virtual void draw_scrollable_region_border (
            const canvas& c,
            const rectangle& rect,
            const bool enabled
        ) const = 0;

    };

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

    class scrollable_region_style_default : public scrollable_region_style
    {
    public:
        scroll_bar_style_default get_horizontal_scroll_bar_style (
        ) const { return scroll_bar_style_default(); }

        scroll_bar_style_default get_vertical_scroll_bar_style (
        ) const { return scroll_bar_style_default(); }

        virtual long get_border_size (
        ) const { return 2; }

        virtual void draw_scrollable_region_border (
            const canvas& c,
            const rectangle& rect,
            const bool enabled
        ) const  { draw_sunken_rectangle(c,rect); }

    };

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    // list_box styles  
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    class list_box_style
    {
    public:

        virtual ~list_box_style() {}

        virtual void draw_list_box_background (
            const canvas& c,
            const rectangle& display_rect,
            const bool enabled
        ) const = 0;

        virtual void draw_list_box_item (
            const canvas& c,
            const rectangle& rect,
            const rectangle& display_rect,
            const bool enabled,
            const font& mfont,
            const std::string& text,
            const bool is_selected
        ) const = 0;

        virtual void draw_list_box_item (
            const canvas& c,
            const rectangle& rect,
            const rectangle& display_rect,
            const bool enabled,
            const font& mfont,
            const std::wstring& text,
            const bool is_selected
        ) const = 0;

        virtual void draw_list_box_item (
            const canvas& c,
            const rectangle& rect,
            const rectangle& display_rect,
            const bool enabled,
            const font& mfont,
            const ustring& text,
            const bool is_selected
        ) const = 0;

    };

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

    class list_box_style_default : public list_box_style
    {
    public:
        scrollable_region_style_default get_scrollable_region_style (
        ) const { return scrollable_region_style_default(); }

        virtual void draw_list_box_item (
            const canvas& c,
            const rectangle& rect,
            const rectangle& display_rect,
            const bool enabled,
            const font& mfont,
            const std::string& text,
            const bool is_selected
        ) const { draw_list_box_item_template(c,rect,display_rect, enabled, mfont, text, is_selected); }

        virtual void draw_list_box_item (
            const canvas& c,
            const rectangle& rect,
            const rectangle& display_rect,
            const bool enabled,
            const font& mfont,
            const std::wstring& text,
            const bool is_selected
        ) const { draw_list_box_item_template(c,rect,display_rect, enabled, mfont, text, is_selected); }

        virtual void draw_list_box_item (
            const canvas& c,
            const rectangle& rect,
            const rectangle& display_rect,
            const bool enabled,
            const font& mfont,
            const ustring& text,
            const bool is_selected
        ) const { draw_list_box_item_template(c,rect,display_rect, enabled, mfont, text, is_selected); }

        template <typename string_type>
        void draw_list_box_item_template (
            const canvas& c,
            const rectangle& rect,
            const rectangle& display_rect,
            const bool enabled,
            const font& mfont,
            const string_type& text,
            const bool is_selected
        ) const
        {
            if (is_selected)
            {
                if (enabled)
                    fill_rect_with_vertical_gradient(c,rect,rgb_pixel(110,160,255),  rgb_pixel(100,130,250),display_rect);
                else
                    fill_rect_with_vertical_gradient(c,rect,rgb_pixel(140,190,255),  rgb_pixel(130,160,250),display_rect);
            }

            if (enabled)
                mfont.draw_string(c,rect,text,rgb_pixel(0,0,0),0,std::string::npos,display_rect);
            else
                mfont.draw_string(c,rect,text,rgb_pixel(128,128,128),0,std::string::npos,display_rect);
        }

        virtual void draw_list_box_background (
            const canvas& c,
            const rectangle& display_rect,
            const bool enabled
        ) const
        {
            if (enabled)
            {
                // first fill our area with white
                fill_rect(c, display_rect,rgb_pixel(255,255,255));
            }
            else
            {
                // first fill our area with gray 
                fill_rect(c, display_rect,rgb_pixel(212,208,200));
            }
        }

    };

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    // text_field styles  
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    class text_field_style
    {
    public:

        text_field_style()
        {
        }

        virtual ~text_field_style() 
        {}

        virtual unsigned long get_padding (
            const font& mfont 
        ) const = 0;

        virtual void draw_text_field (
            const canvas& c,
            const rectangle& rect,
            const rectangle& text_rect,
            const bool enabled,
            const font& mfont,
            const ustring& text,
            const unsigned long cursor_x,
            const unsigned long text_pos,
            const rgb_pixel& text_color,
            const rgb_pixel& bg_color,
            const bool has_focus,
            const bool cursor_visible,
            const long highlight_start,
            const long highlight_end
        ) const = 0;
    };

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

    class text_field_style_default : public text_field_style
    {
    public:

        text_field_style_default()
        {
        }

        virtual ~text_field_style_default() 
        {}

        virtual unsigned long get_padding (
            const font& mfont 
        ) const;

        virtual void draw_text_field (
            const canvas& c,
            const rectangle& rect,
            const rectangle& text_rect,
            const bool enabled,
            const font& mfont,
            const ustring& text,
            const unsigned long cursor_x,
            const unsigned long text_pos,
            const rgb_pixel& text_color,
            const rgb_pixel& bg_color,
            const bool has_focus,
            const bool cursor_visible,
            const long highlight_start,
            const long highlight_end
        ) const;

    };

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

}

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

#endif // DLIB_WIDGETs_STYLE_


