/*
    This file is part of darktable,
    Copyright (C) 2009-2023 darktable developers.

    darktable is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    darktable 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with darktable.  If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "common/act_on.h"
#include "common/action.h"
#include "common/history.h"
#include "common/image.h"
#ifdef HAVE_PRINT
#include "common/cups_print.h"
#include "common/printing.h"
#endif
#ifdef HAVE_MAP
#include "common/geo.h"
#include "common/map_locations.h"
#include <osm-gps-map.h>
#endif
#include <cairo.h>
#include <gmodule.h>
#include <gui/gtk.h>
#include <inttypes.h>
#include <sqlite3.h>
#ifdef USE_LUA
#include "lua/call.h"
#include "lua/events.h"
#include "lua/modules.h"
#include "lua/types.h"
#include "lua/view.h"
#endif

/** available views flags, a view should return its type and
    is also used in modules flags available in src/libs to
    control which view the module should be available in also
    which placement in the panels the module have.
*/
typedef enum dt_view_type_flags_t
{
  DT_VIEW_NONE       = 0,
  DT_VIEW_LIGHTTABLE = 1 << 0,
  DT_VIEW_DARKROOM   = 1 << 1,
  DT_VIEW_TETHERING  = 1 << 2,
  DT_VIEW_MAP        = 1 << 3,
  DT_VIEW_SLIDESHOW  = 1 << 4,
  DT_VIEW_PRINT      = 1 << 5,
  DT_VIEW_KNIGHT     = 1 << 6,
  DT_VIEW_MULTI      = 1 << 28,
  DT_VIEW_FALLBACK   = 1 << 29,
  DT_VIEW_OTHER      = 1 << 30, // for your own unpublished user view
  DT_VIEW_ALL        = ~DT_VIEW_FALLBACK,
} dt_view_type_flags_t;

// flags that a view can set in flags()
typedef enum dt_view_flags_t
{
  VIEW_FLAGS_NONE = 0,
  VIEW_FLAGS_HIDDEN = 1 << 0,       // Hide the view from userinterface
} dt_view_flags_t;

typedef enum dt_lighttable_layout_t
{
  DT_LIGHTTABLE_LAYOUT_FIRST = -1,
  DT_LIGHTTABLE_LAYOUT_ZOOMABLE = 0,
  DT_LIGHTTABLE_LAYOUT_FILEMANAGER = 1,
  DT_LIGHTTABLE_LAYOUT_CULLING = 2,
  DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC = 3,
  DT_LIGHTTABLE_LAYOUT_PREVIEW = 4,
  DT_LIGHTTABLE_LAYOUT_LAST = 5
} dt_lighttable_layout_t;

typedef enum dt_lighttable_culling_restriction_t
{
  DT_LIGHTTABLE_CULLING_RESTRICTION_AUTO,
  DT_LIGHTTABLE_CULLING_RESTRICTION_SELECTION,
  DT_LIGHTTABLE_CULLING_RESTRICTION_COLLECTION
} dt_lighttable_culling_restriction_t;

typedef enum dt_darkroom_layout_t
{
  DT_DARKROOM_LAYOUT_FIRST = -1,
  DT_DARKROOM_LAYOUT_EDITING = 0,
  DT_DARKROOM_LAYOUT_COLOR_ASSESMENT = 1,
  DT_DARKROOM_LAYOUT_LAST = 3
} dt_darkroom_layout_t;

// mouse actions struct
typedef enum dt_mouse_action_type_t
{
  DT_MOUSE_ACTION_LEFT = 0,
  DT_MOUSE_ACTION_RIGHT,
  DT_MOUSE_ACTION_MIDDLE,
  DT_MOUSE_ACTION_SCROLL,
  DT_MOUSE_ACTION_DOUBLE_LEFT,
  DT_MOUSE_ACTION_DOUBLE_RIGHT,
  DT_MOUSE_ACTION_DRAG_DROP,
  DT_MOUSE_ACTION_LEFT_DRAG,
  DT_MOUSE_ACTION_RIGHT_DRAG
} dt_mouse_action_type_t;

// flags that a view can set in flags()
typedef enum dt_view_surface_value_t
{
  DT_VIEW_SURFACE_OK = 0,
  DT_VIEW_SURFACE_KO,
  DT_VIEW_SURFACE_SMALLER
} dt_view_surface_value_t;

typedef struct dt_mouse_action_t
{
  GdkModifierType mods;
  dt_mouse_action_type_t action;
  gchar name[256];
} dt_mouse_action_t;

/* maximum zoom factor for the lighttable */
#define DT_LIGHTTABLE_MAX_ZOOM 25

/**
 * main dt view module (as lighttable or darkroom)
 */
struct dt_view_t;
typedef struct dt_view_t
{
  dt_action_t actions; // !!! NEEDS to be FIRST (to be able to cast convert)

#define INCLUDE_API_FROM_MODULE_H
#include "views/view_api.h"

  char module_name[64];
  // dlopened module
  GModule *module;
  // custom data for module
  void *data;
  // width and height of allocation
  uint32_t width, height;
  // scroll bar control
  float vscroll_size, vscroll_lower, vscroll_viewport_size, vscroll_pos;
  float hscroll_size, hscroll_lower, hscroll_viewport_size, hscroll_pos;
} dt_view_t;

typedef enum dt_view_image_over_t
{
  DT_VIEW_ERR     = -1,
  DT_VIEW_DESERT  =  0,
  DT_VIEW_STAR_1  =  1,
  DT_VIEW_STAR_2  =  2,
  DT_VIEW_STAR_3  =  3,
  DT_VIEW_STAR_4  =  4,
  DT_VIEW_STAR_5  =  5,
  DT_VIEW_REJECT  =  6,
  DT_VIEW_GROUP   =  7,
  DT_VIEW_AUDIO   =  8,
  DT_VIEW_ALTERED =  9,
  DT_VIEW_TAGS    = 10,
  DT_VIEW_END     = 11, // placeholder for the end of the list
} dt_view_image_over_t;

/** returns an uppercase string of file extension **plus** some flag information **/
char* dt_view_extend_modes_str(const char * name,
                               const gboolean is_hdr,
                               const gboolean is_bw,
                               const gboolean is_bw_flow);
/** expose an image and return a cair0_surface. */
dt_view_surface_value_t dt_view_image_get_surface(const dt_imgid_t imgid,
                                                  const int32_t width,
                                                  const int32_t height,
                                                  cairo_surface_t **surface,
                                                  const gboolean quality);


/** Set the selection bit to a given value for the specified image */
void dt_view_set_selection(const dt_imgid_t imgid,
                           const int value);
/** toggle selection of given image. */
void dt_view_toggle_selection(const dt_imgid_t imgid);
/** get the type of the currently active view in darktable.view_manager. */
dt_view_type_flags_t dt_view_get_current(void);

/**
 * holds all relevant data needed to manage the view
 * modules.
 */
typedef struct dt_view_manager_t
{
  GList *views;
  dt_view_t *current_view;

  // images currently active in the main view (there can be more than 1 in culling)
  GSList *active_images;

  // copy/paste history structure
  dt_history_copy_item_t copy_paste;

  struct
  {
    GtkWidget *window;
    GtkWidget *sticky_btn;
    GtkWidget *flow_box;
    gboolean sticky;
    gboolean prevent_refresh;
  } accels_window;

  // cached list of images to act on
  dt_act_on_cache_t act_on_cache_all;
  dt_act_on_cache_t act_on_cache_visible;

  /* reusable db statements
   * TODO: reconsider creating a common/database helper API
   *       instead of having this spread around in sources..
   */
  struct
  {
    /* select num from history where imgid = ?1*/
    sqlite3_stmt *have_history;
    /* select * from selected_images where imgid = ?1 */
    sqlite3_stmt *is_selected;
    /* delete from selected_images where imgid = ?1 */
    sqlite3_stmt *delete_from_selected;
    /* insert into selected_images values (?1) */
    sqlite3_stmt *make_selected;
    /* select color from color_labels where imgid=?1 */
    sqlite3_stmt *get_color;
    /* select images in group from images where imgid=?1 (also bind to ?2) */
    sqlite3_stmt *get_grouped;
  } statements;

  struct
  {
    GPid audio_player_pid;   // the pid of the child process
    int32_t audio_player_id; // the imgid of the image the audio is played for
    guint audio_player_event_source;
  } audio;

  // toggle button for guides (in the module toolbox)
  GtkWidget *guides_toggle, *guides, *guides_colors, *guides_contrast, *guides_popover;

  /*
   * Proxy
   */
  struct
  {

    /* view toolbox proxy object */
    struct
    {
      struct dt_lib_module_t *module;
      void (*add)(struct dt_lib_module_t *, GtkWidget *, dt_view_type_flags_t );
    } view_toolbox;

    /* module toolbox proxy object */
    struct
    {
      struct dt_lib_module_t *module;
      void (*add)(struct dt_lib_module_t *, GtkWidget *, dt_view_type_flags_t);
    } module_toolbox;

    /* filter toolbox proxy object */
    struct
    {
      struct dt_lib_module_t *module;
      GtkWidget *(*get_filter_box)(struct dt_lib_module_t *);
      GtkWidget *(*get_sort_box)(struct dt_lib_module_t *);
      GtkWidget *(*get_count)(struct dt_lib_module_t *);
    } filter;

    /* module collection proxy object */
    struct
    {
      struct dt_lib_module_t *module;
      void (*update)(struct dt_lib_module_t *);
    } module_collect;

    /* module recent collection proxy object */
    struct
    {
      struct dt_lib_module_t *module;
    } module_recentcollect;

    /* module filtering proxy object */
    struct
    {
      struct dt_lib_module_t *module;
      void (*update)(struct dt_lib_module_t *);
      void (*set_sort)(struct dt_lib_module_t *,
                       const int sort,
                       const gboolean asc);
      void (*reset_filter)(struct dt_lib_module_t *,
                           const gboolean smart_filter);
      void (*show_pref_menu)(struct dt_lib_module_t *,
                             GtkWidget *bt);
    } module_filtering;

    /* filmstrip proxy object */
    struct
    {
      struct dt_lib_module_t *module;
    } filmstrip;

    /* darkroom view proxy object */
    struct
    {
      struct dt_view_t *view;
      dt_darkroom_layout_t (*get_layout)(struct dt_view_t *view);
    } darkroom;

    /* lighttable view proxy object */
    struct
    {
      struct dt_lib_module_t *module;
      struct dt_view_t *view;
      void (*update_layout_btn)(struct dt_lib_module_t *module);
      void (*set_zoom)(struct dt_lib_module_t *module,
                       const gint zoom);
      gint (*get_zoom)(struct dt_lib_module_t *module);
      dt_lighttable_layout_t (*get_layout)(struct dt_lib_module_t *module);
      void (*set_layout)(struct dt_lib_module_t *module,
                         const dt_lighttable_layout_t layout);
      void (*culling_preview_refresh)(struct dt_view_t *view);
      void (*culling_preview_reload_overlays)(struct dt_view_t *view);
      dt_lighttable_culling_restriction_t (*get_culling_initial_restriction)(struct dt_lib_module_t *module);
      dt_lighttable_culling_restriction_t (*get_culling_restricted_state)(struct dt_view_t *view);
      dt_imgid_t (*get_culling_selection)(struct dt_view_t *view);
      void (*set_culling_restricted_state)(struct dt_view_t *view,
                                           const dt_lighttable_culling_restriction_t state);
      gboolean (*get_preview_state)(struct dt_view_t *view);
      void (*set_preview_state)(struct dt_view_t *view,
                                const gboolean state,
                                const gboolean sticky,
                                const gboolean focus,
                                const dt_lighttable_culling_restriction_t restriction);
      void (*change_offset)(struct dt_view_t *view,
                            const gboolean reset,
                            const dt_imgid_t imgid);
    } lighttable;

    /* tethering view proxy object */
    struct
    {
      struct dt_view_t *view;
      const char *(*get_job_code)(const dt_view_t *view);
      void (*set_job_code)(const dt_view_t *view,
                           const char *name);
      int32_t (*get_selected_imgid)(const dt_view_t *view);
    } tethering;

    /* timeline module proxy */
    struct
    {
      struct dt_lib_module_t *module;
    } timeline;


/* map view proxy object */
#ifdef HAVE_MAP
    struct
    {
      struct dt_view_t *view;
      void (*center_on_location)(const dt_view_t *view,
                                 const gdouble lon,
                                 const gdouble lat,
                                 const double zoom);
      void (*center_on_bbox)(const dt_view_t *view,
                             const gdouble lon1,
                             const gdouble lat1,
                             const gdouble lon2,
                             const gdouble lat2);
      void (*show_osd)(const dt_view_t *view);
      void (*set_map_source)(const dt_view_t *view,
                             const OsmGpsMapSource_t map_source);
      GObject *(*add_marker)(const dt_view_t *view,
                             const dt_geo_map_display_t type,
                             GList *points);
      gboolean (*remove_marker)(const dt_view_t *view,
                                const dt_geo_map_display_t type,
                                GObject *marker);
      void (*add_location)(const dt_view_t *view,
                           dt_map_location_data_t *p,
                           const guint posid);
      void (*location_action)(const dt_view_t *view,
                              const int action);
      void (*drag_set_icon)(const dt_view_t *view,
                            GdkDragContext *context,
                            const dt_imgid_t imgid,
                            const int count);
      gboolean (*redraw)(gpointer user_data);
      gboolean (*display_selected)(gpointer user_data);
    } map;
#endif

    /* map view proxy object */
#ifdef HAVE_PRINT
    struct
    {
      struct dt_view_t *view;
      void (*print_settings)(const dt_view_t *view,
                             dt_print_info_t *pinfo,
                             dt_images_box *imgs);
    } print;
#endif
  } proxy;


} dt_view_manager_t;

void dt_view_manager_init(dt_view_manager_t *vm);
void dt_view_manager_gui_init(dt_view_manager_t *vm);
void dt_view_manager_cleanup(dt_view_manager_t *vm);

/** return translated name. */
const char *dt_view_manager_name(dt_view_manager_t *vm);
/** switch to this module. returns TRUE if the module fails to change. */
gboolean dt_view_manager_switch(dt_view_manager_t *vm,
                                const char *view_name);
gboolean dt_view_manager_switch_by_view(dt_view_manager_t *vm,
                                        const dt_view_t *new_view);
/** expose current module. */
void dt_view_manager_expose(dt_view_manager_t *vm,
                            cairo_t *cr,
                            const int32_t width,
                            const int32_t height,
                            const int32_t pointerx,
                            const int32_t pointery);
/** reset current view. */
void dt_view_manager_reset(dt_view_manager_t *vm);
/** get current view of the view manager. */
const dt_view_t *dt_view_manager_get_current_view(dt_view_manager_t *vm);

void dt_view_manager_mouse_enter(dt_view_manager_t *vm);
void dt_view_manager_mouse_leave(dt_view_manager_t *vm);
void dt_view_manager_mouse_moved(dt_view_manager_t *vm,
                                 const double x,
                                 const double y,
                                 const double pressure,
                                 const int which);
int dt_view_manager_button_released(dt_view_manager_t *vm,
                                    const double x,
                                    const double y,
                                    const int which,
                                    const uint32_t state);
int dt_view_manager_button_pressed(dt_view_manager_t *vm,
                                   const double x,
                                   const double y,
                                   const double pressure,
                                   const int which,
                                   const int type,
                                   const uint32_t state);
void dt_view_manager_configure(dt_view_manager_t *vm,
                               const int width,
                               const int height);
void dt_view_manager_scrolled(dt_view_manager_t *vm,
                              const double x,
                              const double y,
                              const int up,
                              const int state);
void dt_view_manager_scrollbar_changed(dt_view_manager_t *vm,
                                       const double x,
                                       const double y);

/** add widget to the current view toolbox */
void dt_view_manager_view_toolbox_add(dt_view_manager_t *vm,
                                      GtkWidget *tool,
                                      const dt_view_type_flags_t view);

/** add widget to the current module toolbox */
void dt_view_manager_module_toolbox_add(dt_view_manager_t *vm,
                                        GtkWidget *tool,
                                        const dt_view_type_flags_t view);

/** set scrollbar positions, gui method. */
void dt_view_set_scrollbar(dt_view_t *view,
                           const float hpos,
                           const float hscroll_lower,
                           const float hsize,
                           const float hwinsize,
                           const float vpos,
                           const float vscroll_lower,
                           const float vsize,
                           const float vwinsize);

/** add mouse action record to list of mouse actions */
GSList *dt_mouse_action_create_simple(GSList *actions,
                                      const dt_mouse_action_type_t type,
                                      const GdkModifierType accel,
                                      const char *const description);
GSList *dt_mouse_action_create_format(GSList *actions,
                                      const dt_mouse_action_type_t type,
                                      const GdkModifierType accel,
                                      const char *const format_string,
                                      const char *const replacement);

/*
 * Tethering View PROXY
 */
/** get the current selected image id for tethering session */
int32_t dt_view_tethering_get_selected_imgid(const dt_view_manager_t *vm);
/** set the current jobcode for tethering session */
void dt_view_tethering_set_job_code(const dt_view_manager_t *vm,
                                    const char *name);
/** get the current jobcode for tethering session */
const char *dt_view_tethering_get_job_code(const dt_view_manager_t *vm);

/** update the collection module */
void dt_view_collection_update(const dt_view_manager_t *vm);
void dt_view_filtering_set_sort(const dt_view_manager_t *vm,
                                const int sort,
                                const gboolean asc);

/*
 * Filter dropdown proxy
 */
void dt_view_filtering_reset(const dt_view_manager_t *vm,
                             const gboolean smart_filter);
void dt_view_filtering_show_pref_menu(const dt_view_manager_t *vm, GtkWidget *bt);
GtkWidget *dt_view_filter_get_filters_box(const dt_view_manager_t *vm);
GtkWidget *dt_view_filter_get_sort_box(const dt_view_manager_t *vm);
GtkWidget *dt_view_filter_get_count(const dt_view_manager_t *vm);

// active images functions
void dt_view_active_images_reset(const gboolean raise);
void dt_view_active_images_add(const dt_imgid_t imgid,
                               const gboolean raise);
GSList *dt_view_active_images_get();

/** get the lighttable current layout */
dt_lighttable_layout_t dt_view_lighttable_get_layout(dt_view_manager_t *vm);
/** get the darkroom current layout */
dt_darkroom_layout_t dt_view_darkroom_get_layout(dt_view_manager_t *vm);
/** update the layout buttons of the lighttable lib */
void dt_view_lighttable_update_layout_buttons(dt_view_manager_t *vm);
/** get the lighttable culling initial restricted state */
dt_lighttable_culling_restriction_t dt_view_lighttable_culling_initial_restriction(dt_view_manager_t *vm);
/** get the lighttable culling restricted state */
dt_lighttable_culling_restriction_t dt_view_lighttable_culling_restricted_state(dt_view_manager_t *vm);
/** set the lighttable culling restricted state */
void dt_view_lighttable_set_culling_restricted_state(dt_view_manager_t *vm,
                                                     const dt_lighttable_culling_restriction_t state);
/** get the lighttable full preview state */
gboolean dt_view_lighttable_preview_state(dt_view_manager_t *vm);
/** get the culling selection imgid (used in the "selection" act_on algorithm) */
dt_imgid_t dt_view_lighttable_get_culling_selection(dt_view_manager_t *vm);
/** set the lighttable full preview state */
void dt_view_lighttable_set_preview_state(dt_view_manager_t *vm,
                                          const gboolean state,
                                          const gboolean sticky,
                                          const gboolean focus,
                                          const dt_lighttable_culling_restriction_t restriction);
/** sets the lighttable image in row zoom */
void dt_view_lighttable_set_zoom(dt_view_manager_t *vm,
                                 const gint zoom);
/** gets the lighttable image in row zoom */
gint dt_view_lighttable_get_zoom(dt_view_manager_t *vm);
/** force refresh of culling and/or preview */
void dt_view_lighttable_culling_preview_refresh(dt_view_manager_t *vm);
/** force refresh of culling and/or preview overlays */
void dt_view_lighttable_culling_preview_reload_overlays(dt_view_manager_t *vm);
/** sets the offset image (for culling and full preview) */
void dt_view_lighttable_change_offset(dt_view_manager_t *vm,
                                      const gboolean reset,
                                      const dt_imgid_t imgid);

/* accel window */
void dt_view_accels_show(dt_view_manager_t *vm);
void dt_view_accels_hide(dt_view_manager_t *vm);
void dt_view_accels_refresh(dt_view_manager_t *vm);

/* audio */
void dt_view_audio_start(dt_view_manager_t *vm,
                         const dt_imgid_t imgid);
void dt_view_audio_stop(dt_view_manager_t *vm);

/*
 * Map View Proxy
 */
#ifdef HAVE_MAP
void dt_view_map_center_on_location(const dt_view_manager_t *vm,
                                    const gdouble lon,
                                    const gdouble lat,
                                    const gdouble zoom);
void dt_view_map_center_on_bbox(const dt_view_manager_t *vm,
                                const gdouble lon1,
                                const gdouble lat1,
                                const gdouble lon2,
                                const gdouble lat2);
void dt_view_map_show_osd(const dt_view_manager_t *vm);
void dt_view_map_set_map_source(const dt_view_manager_t *vm,
                                const OsmGpsMapSource_t map_source);
GObject *dt_view_map_add_marker(const dt_view_manager_t *vm,
                                const dt_geo_map_display_t type,
                                GList *points);
gboolean dt_view_map_remove_marker(const dt_view_manager_t *vm,
                                   const dt_geo_map_display_t type,
                                   GObject *marker);
void dt_view_map_add_location(const dt_view_manager_t *vm,
                              dt_map_location_data_t *p,
                              const guint posid);
void dt_view_map_location_action(const dt_view_manager_t *vm,
                                 const int action);
void dt_view_map_drag_set_icon(const dt_view_manager_t *vm,
                               GdkDragContext *context,
                               const dt_imgid_t imgid,
                               const int count);
#endif

/*
 * Print View Proxy
 */
#ifdef HAVE_PRINT
void dt_view_print_settings(const dt_view_manager_t *vm,
                            dt_print_info_t *pinfo,
                            dt_images_box *imgs);
#endif

/*
 * Paint buffer (size processed_width x processed_height) in cairo (as a surface)
 * on the viewport (size width x height).
 */

typedef enum _window_t
{
  DT_WINDOW_MAIN,
  DT_WINDOW_SECOND,
  DT_WINDOW_SLIDESHOW
} dt_window_t;

void dt_view_paint_buffer(cairo_t *cr,
                          const size_t width,
                          const size_t height,
                          uint8_t *buffer,
                          const size_t processed_width,
                          const size_t processed_height,
                          const dt_window_t window);

void dt_view_paint_pixbuf(cairo_t *cr,
                          const size_t width,
                          const size_t height,
                          uint8_t *buffer,
                          const size_t processed_width,
                          const size_t processed_height,
                          const dt_window_t window);

cairo_surface_t *dt_view_create_surface(uint8_t *buffer,
                                        const size_t processed_width,
                                        const size_t processed_height);

void dt_view_paint_surface(cairo_t *cr,
                           const size_t width,
                           const size_t height,
                           dt_dev_viewport_t *port,
                           const dt_window_t window,
                           uint8_t *buf,
                           float buf_scale,
                           int buf_width,
                           int buf_height,
                           float buf_zoom_x,
                           float buf_zoom_y);

typedef dt_hash_t dt_view_context_t;

dt_view_context_t dt_view_get_context_hash(void);

gboolean dt_view_check_context_hash(dt_view_context_t *ctx);

// clang-format off
// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
// vim: shiftwidth=2 expandtab tabstop=2 cindent
// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
// clang-format on
