/**
 ** A GTK widget showing a list of shapes from an image file.
 **
 ** Written: 7/25/99 - JSF
 **/

#ifndef INCL_SHAPELST
#define INCL_SHAPELST 1

/*
Copyright (C) 1999-2022 Jeffrey S. Freedman

This program 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 2
of the License, or (at your option) any later version.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "objbrowse.h"
#include "rect.h"
#include "shapedraw.h"

#include <ctime>
#include <memory>
#include <vector>

class Vga_file;
class Image_buffer8;
class Shapes_vga_file;
class Editing_file;

/*
 *  Store information about an individual shape shown in the list.
 */
class Shape_entry {
	friend class Shape_chooser;
	short    shapenum, framenum;    // The given shape/frame.
	TileRect box;                   // Box where drawn.
public:
	void set(int shnum, int frnum, int rx, int ry, int rw, int rh) {
		shapenum = static_cast<short>(shnum);
		framenum = static_cast<short>(frnum);
		box      = TileRect(rx, ry, rw, rh);
	}
};

/*
 *  One row.
 */
class Shape_row {
	friend class Shape_chooser;
	short    height = 0;    // In pixels.
	long     y;             // Absolute y-coord. in pixels.
	unsigned index0;        // Index of 1st Shape_entry in row.
};

/*
 *  This class manages a list of shapes from an image file.
 */
class Shape_chooser : public Object_browser, public Shape_draw {
	Shapes_vga_file*         shapes_file;    // Non-null if 'shapes.vga'.
	GtkWidget*               sbar;           // Status bar.
	guint                    sbar_sel;     // Status bar context for selection.
	GtkWidget*               fspin;        // Spin button for frame #.
	GtkAdjustment*           frame_adj;    // Adjustment for frame spin btn.
	int                      framenum0;    // Default frame # to display.
	std::vector<Shape_entry> info;         // Pos. of each shape/frame.
	std::vector<Shape_row>   rows;
	unsigned                 row0;    // Row # at top of window.
	int  row0_voffset;                // Vert. pos. (in pixels) of top row.
	long total_height;                // In pixels, for all rows.
	int  last_shape;                  // Last shape visible in window.
	bool frames_mode;                 // Show all frames horizontally.
	int  hoffset;                     // Horizontal offset in pixels (when in
									  //   frames_mode).
	int voffset;                      // Vertical offset in pixels.
	int status_id;                    // Statusbar msg. ID.
	void (*sel_changed)();            // Called when selection changes.
	// List of files being edited by an
	//   external program (Gimp, etc.)
	static std::vector<std::unique_ptr<Editing_file>> editing_files;
	static int check_editing_timer;    // For monitoring files being edited.
	// Blit onto screen.
	void show(int x, int y, int w, int h) override;
	void tell_server_shape();    // Tell Exult what shape is selected.
	void select(int new_sel);    // Show new selection.
	void render() override;      // Draw list.

	void set_background_color(guint32 c) override {
		Shape_draw::set_background_color(c);
	}

	void setup_info(bool savepos = true) override;
	void setup_shapes_info();
	void setup_frames_info();
	void scroll_to_frame();             // Scroll so sel. frame is visible.
	int  find_shape(int shnum);         // Find index for given shape.
	void goto_index(unsigned index);    // Get desired index in view.

	int get_selected_id() override {
		return selected < 0 ? -1 : info[selected].shapenum;
	}

	void       scroll_row_vertical(unsigned newrow);
	void       scroll_vertical(int newoffset);    // Scroll.
	void       setup_vscrollbar();                // Set new scroll amounts.
	void       setup_hscrollbar(int newmax);
	GtkWidget* create_popup() override;    // Popup menu.
public:
	Shape_chooser(
			Vga_file* i, unsigned char* palbuf, int w, int h,
			Shape_group* g = nullptr, Shape_file_info* fi = nullptr);
	~Shape_chooser() override;

	void set_shapes_file(Shapes_vga_file* sh) {
		shapes_file = sh;
	}

	void set_framenum0(int f) {
		framenum0 = f;
	}

	void shape_dropped_here(int file, int shapenum, int framenum);
	int  get_count();    // Get # shapes we can display.
	void search(const char* search, int dir) override;
	void locate(bool upwards) override;    // Locate shape on game map.
	// Turn off selection.
	void unselect(bool need_render = true);
	void update_statusbar();

	bool is_selected() {    // Is a shape selected?
		return selected >= 0;
	}

	void set_selected_callback(void (*fun)()) {
		sel_changed = fun;
	}

	unsigned get_num_cols(unsigned rownum) {
		return ((rownum < rows.size() - 1) ? rows[rownum + 1].index0
										   : info.size())
			   - rows[rownum].index0;
	}

	// Configure when created/resized.
	gint configure(GdkEventConfigure* event);
	// Blit to screen.
	static gint     expose(GtkWidget* widget, cairo_t* cairo, gpointer data);
	static gboolean on_new_shape_font_color_draw_expose_event(
			GtkWidget* widget, cairo_t* cairo, gpointer data);
	// Handle mouse press.
	gint mouse_press(GtkWidget* widget, GdkEventButton* event);
	// Export current frame as a PNG.
	time_t export_png(const char* fname);
	// Export given image as a PNG.
	time_t export_png(
			const char* fname, Image_buffer8& img, int xoff, int yoff);
	// Export frames tiled.
	time_t export_tiled_png(const char* fname, int tiles, bool bycols);
	void   edit_shape_info();    // Edit selected shape's info.
	// Edit selected shape-frame.
	void edit_shape(int tiles = 0, bool bycols = false);
	// Deal with list of files being edited
	//   by an external prog. (Gimp).
	static gint check_editing_files_cb(gpointer data);
	static gint check_editing_files();
	static void read_back_edited(Editing_file* ed);
	static void clear_editing_files();
	// Import/export from file selector.
	static void export_frame(const char* fname, gpointer user_data);
	static void import_frame(const char* fname, gpointer user_data);
	static void export_all_frames(const char* fname, gpointer user_data);
	static void export_shape(const char* fname, gpointer user_data);
	void        export_all_pngs(const char* fname, int shnum);
	static void import_all_frames(const char* fname, gpointer user_data);
	static void import_shape(const char* fname, gpointer user_data);
	void        import_all_pngs(const char* fname, int shnum);
	void        new_frame();    // Add/del.
	void        from_font_toggled(bool on);
	void        new_shape();
	void        create_new_shape();
	void        del_frame();
	// Give dragged shape.
	static void drag_data_get(
			GtkWidget* widget, GdkDragContext* context,
			GtkSelectionData* seldata, guint info, guint time, gpointer data);
	static gint drag_begin(
			GtkWidget* widget, GdkDragContext* context, gpointer data);
	// Handle scrollbar.
	static void vscrolled(GtkAdjustment* adj, gpointer data);
	static void hscrolled(GtkAdjustment* adj, gpointer data);
	// Handle spin-button for frames.
	static void frame_changed(GtkAdjustment* adj, gpointer data);
	static void all_frames_toggled(GtkToggleButton* btn, gpointer user_data);
	static gint drag_motion(
			GtkWidget* widget, GdkEventMotion* event, gpointer data);
	// Menu items:
	static void on_shapes_popup_info_activate(
			GtkMenuItem* item, gpointer udata);
	static void on_shapes_popup_edit_activate(
			GtkMenuItem* item, gpointer udata);
	static void on_shapes_popup_edtiles_activate(
			GtkMenuItem* item, gpointer udata);
};

#endif
