/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the Collabora Office project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#pragma once

#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>

#include <com/sun/star/accessibility/XAccessibleContext.hpp>
#include <com/sun/star/accessibility/XAccessibleEventListener.hpp>
#include <unx/gendata.hxx>
#include <unx/saldisp.hxx>
#include <unx/gtk/gtksys.hxx>
#include <vcl/ptrstyle.hxx>
#include <osl/conditn.hxx>
#include <saltimer.hxx>
#include <o3tl/enumarray.hxx>
#include <unotools/weakref.hxx>

#include <exception>
#include <string_view>

namespace com::sun::star::accessibility { class XAccessibleEventListener; }

class GtkSalDisplay;

inline void main_loop_run(GMainLoop* pLoop)
{
    gdk_threads_leave();
    g_main_loop_run(pLoop);
    gdk_threads_enter();
}

inline void css_provider_load_from_data(GtkCssProvider *css_provider,
                                        const gchar *data,
                                        gssize length)
{
    gtk_css_provider_load_from_data(css_provider, data, length, nullptr);
}

inline GtkWidget* widget_get_toplevel(GtkWidget* pWidget)
{
    return gtk_widget_get_toplevel(pWidget);
}

inline const char* image_get_icon_name(GtkImage *pImage)
{
    const gchar* icon_name;
    gtk_image_get_icon_name(pImage, &icon_name, nullptr);
    return icon_name;
}

inline GtkWidget* widget_get_first_child(GtkWidget *pWidget)
{
    GList* pChildren = gtk_container_get_children(GTK_CONTAINER(pWidget));
    GList* pChild = g_list_first(pChildren);
    GtkWidget* pRet = pChild ? static_cast<GtkWidget*>(pChild->data) : nullptr;
    g_list_free(pChildren);
    return pRet;
}

inline void style_context_get_color(GtkStyleContext *pStyle, GdkRGBA *pColor)
{
    return gtk_style_context_get_color(pStyle, gtk_style_context_get_state(pStyle), pColor);
}

inline GdkWindow* widget_get_surface(GtkWidget* pWidget)
{
    return gtk_widget_get_window(pWidget);
}

inline void widget_set_cursor(GtkWidget *pWidget, GdkCursor *pCursor)
{
    gdk_window_set_cursor(gtk_widget_get_window(pWidget), pCursor);
}

inline cairo_surface_t * surface_create_similar_surface(GdkWindow *pSurface,
                                                        cairo_content_t eContent,
                                                        int nWidth,
                                                        int nHeight)
{
    return gdk_window_create_similar_surface(pSurface, eContent, nWidth, nHeight);
}

inline void im_context_set_client_widget(GtkIMContext *pIMContext, GtkWidget *pWidget)
{
    gtk_im_context_set_client_window(pIMContext, pWidget ? gtk_widget_get_window(pWidget) : nullptr);
}

typedef int gtk_coord;

inline bool surface_get_device_position(GdkWindow* pSurface,
                                        GdkDevice* pDevice,
                                        double& x,
                                        double& y,
                                        GdkModifierType* pMask)
{
    int nX(x), nY(y);
    bool bRet = gdk_window_get_device_position(pSurface, pDevice,
                                               &nX, &nY,
                                               pMask);
    x = nX;
    y = nY;
    return bRet;
}

void set_buildable_id(GtkBuildable* pWidget, const std::u16string_view rId);
OUString get_buildable_id(GtkBuildable* pWidget);

void container_remove(GtkWidget* pContainer, GtkWidget* pChild);
void container_add(GtkWidget* pContainer, GtkWidget* pChild);

int getButtonPriority(std::u16string_view rType);

class GtkSalTimer final : public SalTimer
{
    struct SalGtkTimeoutSource *m_pTimeout;

    void ImplDestroy();
public:
    GtkSalTimer();
    virtual ~GtkSalTimer() override;
    virtual void Start( sal_uInt64 nMS ) override;
    virtual void Stop() override;
    bool         Expired();

    sal_uLong    m_nTimeoutMS;
};


class GtkSalData final : public GenericUnixSalData
{
    GSource*        m_pUserEvent;
    osl::Mutex      m_aDispatchMutex;
    osl::Condition  m_aDispatchCondition;
    std::exception_ptr m_aException;

public:
    GtkSalData();
    virtual ~GtkSalData() override;

    void Init();
    virtual void Dispose() override;

    static void initNWF();
    static void deInitNWF();

    void TriggerUserEventProcessing();
    void TriggerAllUserEventsProcessed();

    bool Yield( bool bWait, bool bHandleAllCurrentEvents );
    inline GdkDisplay *GetGdkDisplay();

    virtual void ErrorTrapPush() override;
    virtual bool ErrorTrapPop( bool bIgnoreError = true ) override;

    inline GtkSalDisplay *GetGtkDisplay() const;
    void setException(const std::exception_ptr& exception) { m_aException = exception; }
};

class GtkSalFrame;

class GtkSalDisplay final : public SalGenericDisplay
{
    GtkSalSystem*                   m_pSys;
    GdkDisplay*                     m_pGdkDisplay;
    o3tl::enumarray<PointerStyle, GdkCursor*> m_aCursors;
    bool                            m_bStartupCompleted;

    GdkCursor* getFromSvg( OUString const & name, int nXHot, int nYHot );

public:
             GtkSalDisplay( GdkDisplay* pDisplay );
    virtual ~GtkSalDisplay() override;

    GdkDisplay* GetGdkDisplay() const { return m_pGdkDisplay; }

    GtkSalSystem* getSystem() const { return m_pSys; }

    GtkWidget* findGtkWidgetForNativeHandle(sal_uIntPtr hWindow) const;

    virtual void deregisterFrame( SalFrame* pFrame ) override;
    GdkCursor *getCursor( PointerStyle ePointerStyle );
    int CaptureMouse(GtkSalFrame* pFrame);

    SalX11Screen GetDefaultXScreen() { return m_pSys->GetDisplayDefaultXScreen(); }
    AbsoluteScreenPixelSize GetScreenSize( int nDisplayScreen );

    void startupNotificationCompleted() { m_bStartupCompleted = true; }

    void screenSizeChanged( GdkScreen const * );
    void monitorsChanged( GdkScreen const * );

    virtual void TriggerUserEventProcessing() override;
    virtual void TriggerAllUserEventsProcessed() override;
};

inline GtkSalData* GetGtkSalData()
{
    return static_cast<GtkSalData*>(ImplGetSVData()->mpSalData);
}
inline GdkDisplay *GtkSalData::GetGdkDisplay()
{
    return GetGtkDisplay()->GetGdkDisplay();
}

GtkSalDisplay *GtkSalData::GetGtkDisplay() const
{
    return static_cast<GtkSalDisplay *>(GetDisplay());
}


/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
