///////////////////////////////////////////////////////////////////////////
// Name:        src/gtk/display.cpp
// Author:      Paul Cornett
// Created:     2014-04-17
// Copyright:   (c) 2014 Paul Cornett
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

#include "wx/wxprec.h"

#if wxUSE_DISPLAY
    #include "wx/display.h"
    #include "wx/display_impl.h"
#endif
#include "wx/utils.h" // wxClientDisplayRect

#include <gtk/gtk.h>
#ifdef GDK_WINDOWING_X11
    #include <gdk/gdkx.h>
#endif
#include "wx/gtk/private/gtk2-compat.h"

GtkWidget* wxGetRootWindow();

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

#if !(wxUSE_LIBHILDON || wxUSE_LIBHILDON2)

void wxGetWorkAreaX11(Screen* screen, int& x, int& y, int& width, int& height);

#ifndef __WXGTK3__
static inline int wx_gdk_screen_get_primary_monitor(GdkScreen* screen)
{
    int monitor = 0;
#if GTK_CHECK_VERSION(2,20,0)
    if (gtk_check_version(2,20,0) == NULL)
        monitor = gdk_screen_get_primary_monitor(screen);
#endif
    return monitor;
}
#define gdk_screen_get_primary_monitor wx_gdk_screen_get_primary_monitor
#endif // !__WXGTK3__

static inline void
wx_gdk_screen_get_monitor_workarea(GdkScreen* screen, int monitor, GdkRectangle* dest)
{
#if GTK_CHECK_VERSION(3,4,0)
    if (gtk_check_version(3,4,0) == NULL)
        gdk_screen_get_monitor_workarea(screen, monitor, dest);
    else
#endif
    {
        gdk_screen_get_monitor_geometry(screen, monitor, dest);
#ifdef GDK_WINDOWING_X11
#ifdef __WXGTK3__
        if (GDK_IS_X11_SCREEN(screen))
#endif
        {
            GdkRectangle rect = { 0 };
            wxGetWorkAreaX11(GDK_SCREEN_XSCREEN(screen),
                rect.x, rect.y, rect.width, rect.height);
            // in case _NET_WORKAREA result is too large
            if (rect.width && rect.height)
                gdk_rectangle_intersect(dest, &rect, dest);
        }
#endif // GDK_WINDOWING_X11
    }
}
#define gdk_screen_get_monitor_workarea wx_gdk_screen_get_monitor_workarea

#endif // !(wxUSE_LIBHILDON || wxUSE_LIBHILDON2)

void wxClientDisplayRect(int* x, int* y, int* width, int* height)
{
#if wxUSE_LIBHILDON || wxUSE_LIBHILDON2
    GdkRectangle rect = { 0, 0, 672, 396 };
#else
    GdkRectangle rect;
    GdkWindow* window = gtk_widget_get_window(wxGetRootWindow());
    GdkScreen* screen = gdk_window_get_screen(window);
    int monitor = gdk_screen_get_monitor_at_window(screen, window);
    gdk_screen_get_monitor_workarea(screen, monitor, &rect);
#endif
    if (x)
        *x = rect.x;
    if (y)
        *y = rect.y;
    if (width)
        *width = rect.width;
    if (height)
        *height = rect.height;
}
//-----------------------------------------------------------------------------

#if wxUSE_DISPLAY
class wxDisplayFactoryGTK: public wxDisplayFactory
{
public:
    virtual wxDisplayImpl* CreateDisplay(unsigned n);
    virtual unsigned GetCount();
    virtual int GetFromPoint(const wxPoint& pt);
};

class wxDisplayImplGTK: public wxDisplayImpl
{
    typedef wxDisplayImpl base_type;
public:
    wxDisplayImplGTK(unsigned i);
    virtual wxRect GetGeometry() const;
    virtual wxRect GetClientArea() const;
    virtual wxString GetName() const;
    virtual bool IsPrimary() const;
    virtual wxArrayVideoModes GetModes(const wxVideoMode& mode) const;
    virtual wxVideoMode GetCurrentMode() const;
    virtual bool ChangeMode(const wxVideoMode& mode);

    GdkScreen* const m_screen;
};

static inline GdkScreen* GetScreen()
{
    return gtk_widget_get_screen(wxGetRootWindow());
}
//-----------------------------------------------------------------------------

wxDisplayImpl* wxDisplayFactoryGTK::CreateDisplay(unsigned n)
{
    return new wxDisplayImplGTK(n);
}

unsigned wxDisplayFactoryGTK::GetCount()
{
    return gdk_screen_get_n_monitors(GetScreen());
}

int wxDisplayFactoryGTK::GetFromPoint(const wxPoint& pt)
{
    GdkScreen* screen = GetScreen();
    int monitor = gdk_screen_get_monitor_at_point(screen, pt.x, pt.y);
    GdkRectangle rect;
    gdk_screen_get_monitor_geometry(screen, monitor, &rect);
    if (!wxRect(rect.x, rect.y, rect.width, rect.height).Contains(pt))
        monitor = wxNOT_FOUND;
    return monitor;
}
//-----------------------------------------------------------------------------

wxDisplayImplGTK::wxDisplayImplGTK(unsigned i)
    : base_type(i)
    , m_screen(GetScreen())
{
}

wxRect wxDisplayImplGTK::GetGeometry() const
{
    GdkRectangle rect;
    gdk_screen_get_monitor_geometry(m_screen, m_index, &rect);
    return wxRect(rect.x, rect.y, rect.width, rect.height);
}

wxRect wxDisplayImplGTK::GetClientArea() const
{
    GdkRectangle rect;
    gdk_screen_get_monitor_workarea(m_screen, m_index, &rect);
    return wxRect(rect.x, rect.y, rect.width, rect.height);
}

wxString wxDisplayImplGTK::GetName() const
{
    return wxString();
}

bool wxDisplayImplGTK::IsPrimary() const
{
    return gdk_screen_get_primary_monitor(m_screen) == int(m_index);
}

wxArrayVideoModes wxXF86VidMode_GetModes(const wxVideoMode& mode, Display* pDisplay, int nScreen);
wxVideoMode wxXF86VidMode_GetCurrentMode(Display* display, int nScreen);
bool wxXF86VidMode_ChangeMode(const wxVideoMode& mode, Display* display, int nScreen);
wxArrayVideoModes wxX11_GetModes(const wxDisplayImpl* impl, const wxVideoMode& modeMatch, Display* display);

wxArrayVideoModes wxDisplayImplGTK::GetModes(const wxVideoMode& mode) const
{
    wxArrayVideoModes modes;
#ifdef GDK_WINDOWING_X11
#ifdef __WXGTK3__
    if (GDK_IS_X11_SCREEN(m_screen))
#endif
    {
        Display* display = GDK_DISPLAY_XDISPLAY(gdk_screen_get_display(m_screen));
#ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
        int nScreen = gdk_x11_screen_get_screen_number(m_screen);
        modes = wxXF86VidMode_GetModes(mode, display, nScreen);
#else
        modes = wxX11_GetModes(this, mode, display);
#endif
    }
#endif // GDK_WINDOWING_X11
    return modes;
}

wxVideoMode wxDisplayImplGTK::GetCurrentMode() const
{
    wxVideoMode mode;
#if defined(GDK_WINDOWING_X11) && defined(HAVE_X11_EXTENSIONS_XF86VMODE_H)
#ifdef __WXGTK3__
    if (GDK_IS_X11_SCREEN(m_screen))
#endif
    {
        Display* display = GDK_DISPLAY_XDISPLAY(gdk_screen_get_display(m_screen));
        int nScreen = gdk_x11_screen_get_screen_number(m_screen);
        mode = wxXF86VidMode_GetCurrentMode(display, nScreen);
    }
#endif
    return mode;
}

bool wxDisplayImplGTK::ChangeMode(const wxVideoMode& mode)
{
    bool success = false;
#if defined(GDK_WINDOWING_X11) && defined(HAVE_X11_EXTENSIONS_XF86VMODE_H)
#ifdef __WXGTK3__
    if (GDK_IS_X11_SCREEN(m_screen))
#endif
    {
        Display* display = GDK_DISPLAY_XDISPLAY(gdk_screen_get_display(m_screen));
        int nScreen = gdk_x11_screen_get_screen_number(m_screen);
        success = wxXF86VidMode_ChangeMode(mode, display, nScreen);
    }
#else
    wxUnusedVar(mode);
#endif
    return success;
}

wxDisplayFactory* wxDisplay::CreateFactory()
{
    return new wxDisplayFactoryGTK;
}
#endif // wxUSE_DISPLAY
