/* GDK - The GIMP Drawing Kit
 * Copyright (C) 2020 Red Hat
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "config.h"

#include "gdktoplevellayout.h"

#include "gdkmonitor.h"

/**
 * GdkToplevelLayout:
 *
 * Contains information that is necessary to present a sovereign
 * window on screen.
 *
 * The `GdkToplevelLayout` struct is necessary for using
 * [method@Gdk.Toplevel.present].
 *
 * Toplevel surfaces are sovereign windows that can be presented
 * to the user in various states (maximized, on all workspaces,
 * etc).
 */
struct _GdkToplevelLayout
{
  /* < private >*/
  grefcount ref_count;

  guint resizable  : 1;

  guint maximized_valid : 1;
  guint maximized  : 1;
  guint fullscreen_valid : 1;
  guint fullscreen : 1;
  GdkMonitor *fullscreen_monitor;
};

G_DEFINE_BOXED_TYPE (GdkToplevelLayout, gdk_toplevel_layout,
                     gdk_toplevel_layout_ref,
                     gdk_toplevel_layout_unref)

/**
 * gdk_toplevel_layout_new: (constructor)
 *
 * Create a toplevel layout description.
 *
 * Used together with [method@Gdk.Toplevel.present] to describe
 * how a toplevel surface should be placed and behave on-screen.
 *
 * The size is in ”application pixels”, not
 * ”device pixels” (see [method@Gdk.Surface.get_scale]).
 *
 * Returns: (transfer full): newly created instance of `GdkToplevelLayout`
 */
GdkToplevelLayout *
gdk_toplevel_layout_new (void)
{
  GdkToplevelLayout *layout;

  layout = g_new0 (GdkToplevelLayout, 1);
  g_ref_count_init (&layout->ref_count);
  layout->resizable = TRUE;
  layout->maximized_valid = FALSE;
  layout->maximized = FALSE;
  layout->fullscreen_valid = FALSE;
  layout->fullscreen = FALSE;
  layout->fullscreen_monitor = NULL;

  return layout;
}

/**
 * gdk_toplevel_layout_ref:
 * @layout: a toplevel layout
 *
 * Increases the reference count of @layout.
 *
 * Returns: the same @layout
 */
GdkToplevelLayout *
gdk_toplevel_layout_ref (GdkToplevelLayout *layout)
{
  g_ref_count_inc (&layout->ref_count);
  return layout;
}

/**
 * gdk_toplevel_layout_unref:
 * @layout: a toplevel layout
 *
 * Decreases the reference count of @layout.
 */
void
gdk_toplevel_layout_unref (GdkToplevelLayout *layout)
{
  if (g_ref_count_dec (&layout->ref_count))
    {
      g_clear_object (&layout->fullscreen_monitor);
      g_free (layout);
    }
}

/**
 * gdk_toplevel_layout_copy:
 * @layout: a toplevel layout
 *
 * Create a new `GdkToplevelLayout` and copy the contents of @layout into it.
 *
 * Returns: (transfer full): a copy of @layout.
 */
GdkToplevelLayout *
gdk_toplevel_layout_copy (GdkToplevelLayout *layout)
{
  GdkToplevelLayout *new_layout;

  new_layout = g_new0 (GdkToplevelLayout, 1);
  g_ref_count_init (&new_layout->ref_count);

  new_layout->resizable = layout->resizable;
  new_layout->maximized_valid = layout->maximized_valid;
  new_layout->maximized = layout->maximized;
  new_layout->fullscreen_valid = layout->fullscreen_valid;
  new_layout->fullscreen = layout->fullscreen;
  if (layout->fullscreen_monitor)
    new_layout->fullscreen_monitor = g_object_ref (layout->fullscreen_monitor);

  return new_layout;
}

/**
 * gdk_toplevel_layout_equal:
 * @layout: a toplevel layout
 * @other: another toplevel layout
 *
 * Check whether @layout and @other has identical layout properties.
 *
 * Returns: true if @layout and @other have identical layout properties,
 *   otherwise false.
 */
gboolean
gdk_toplevel_layout_equal (GdkToplevelLayout *layout,
                           GdkToplevelLayout *other)
{
  g_return_val_if_fail (layout, FALSE);
  g_return_val_if_fail (other, FALSE);

  return layout->resizable == other->resizable &&
         layout->maximized_valid == other->maximized_valid &&
         layout->maximized == other->maximized &&
         layout->fullscreen_valid == other->fullscreen_valid &&
         layout->fullscreen == other->fullscreen &&
         layout->fullscreen_monitor == other->fullscreen_monitor;
}

/**
 * gdk_toplevel_layout_set_resizable:
 * @layout: a toplevel layout
 * @resizable: true to allow resizing
 *
 * Sets whether the layout should allow the user
 * to resize the surface after it has been presented.
 */
void
gdk_toplevel_layout_set_resizable (GdkToplevelLayout *layout,
                                   gboolean           resizable)
{
  layout->resizable = resizable;
}

/**
 * gdk_toplevel_layout_get_resizable:
 * @layout: a toplevel layout
 *
 * Returns whether the layout should allow the user
 * to resize the surface.
 *
 * Returns: true if the layout is resizable
 */
gboolean
gdk_toplevel_layout_get_resizable (GdkToplevelLayout *layout)
{
  return layout->resizable;
}

/**
 * gdk_toplevel_layout_set_maximized:
 * @layout: a toplevel layout
 * @maximized: true to maximize
 *
 * Sets whether the layout should cause the surface
 * to be maximized when presented.
 */
void
gdk_toplevel_layout_set_maximized (GdkToplevelLayout *layout,
                                   gboolean           maximized)
{
  layout->maximized_valid = TRUE;
  layout->maximized = maximized;
}

/**
 * gdk_toplevel_layout_get_maximized:
 * @layout: a toplevel layout
 * @maximized: (out): set to true if the toplevel should be maximized
 *
 * If the layout specifies whether to the toplevel should go maximized,
 * the value pointed to by @maximized is set to true if it should go
 * maximized, or false, if it should go unmaximized.
 *
 * Returns: whether the @layout specifies the maximized state for the toplevel
 */
gboolean
gdk_toplevel_layout_get_maximized (GdkToplevelLayout *layout,
                                   gboolean          *maximized)
{
  if (layout->maximized_valid)
    {
      *maximized = layout->maximized;
      return TRUE;
    }

  return FALSE;
}

/**
 * gdk_toplevel_layout_set_fullscreen:
 * @layout: a toplevel layout
 * @fullscreen: true to fullscreen the surface
 * @monitor: (nullable): the monitor to fullscreen on
 *
 * Sets whether the layout should cause the surface
 * to be fullscreen when presented.
 */
void
gdk_toplevel_layout_set_fullscreen (GdkToplevelLayout *layout,
                                    gboolean           fullscreen,
                                    GdkMonitor        *monitor)
{
  layout->fullscreen_valid = TRUE;
  layout->fullscreen = fullscreen;
  if (monitor)
    layout->fullscreen_monitor = g_object_ref (monitor);
}

/**
 * gdk_toplevel_layout_get_fullscreen:
 * @layout: a toplevel layout
 * @fullscreen: (out): location to store whether the toplevel should be fullscreen
 *
 * If the layout specifies whether to the toplevel should go fullscreen,
 * the value pointed to by @fullscreen is set to true if it should go
 * fullscreen, or false, if it should go unfullscreen.
 *
 * Returns: whether the @layout specifies the fullscreen state for the toplevel
 */
gboolean
gdk_toplevel_layout_get_fullscreen (GdkToplevelLayout *layout,
                                    gboolean          *fullscreen)
{
  if (layout->fullscreen_valid)
    {
      *fullscreen = layout->fullscreen;
      return TRUE;
    }

  return FALSE;
}

/**
 * gdk_toplevel_layout_get_fullscreen_monitor:
 * @layout: a toplevel layout
 *
 * Returns the monitor that the layout is fullscreening
 * the surface on.
 *
 * Returns: (nullable) (transfer none): the monitor on which @layout fullscreens
 */
GdkMonitor *
gdk_toplevel_layout_get_fullscreen_monitor (GdkToplevelLayout *layout)
{
  return layout->fullscreen_monitor;
}
