/**
 ** A GTK widget showing the list of NPC's.
 **
 ** Written: 7/6/2005 - JSF
 **/

#ifndef INCL_NPCLST
#define INCL_NPCLST 1

/*
Copyright (C) 2005-2022 The Exult Team

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 <string>
#include <vector>

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

/*
 *  Store information about an NPC shown in the list.
 */
class Npc_entry {
	friend class Npc_chooser;
	short npcnum, framenum; // The given npc/frame.
	TileRect box;          // Box where drawn.
public:
	void set(int num, int frnum, int rx, int ry, int rw, int rh) {
		npcnum   = static_cast<short>(num);
		framenum = static_cast<short>(frnum);
		box = TileRect(rx, ry, rw, rh);
	}
};

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

/*
 *  This class manages a list of NPC's.
 */
class Npc_chooser: public Object_browser, public Shape_draw {
	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<Npc_entry> info;    // Pos. of each shape/frame.
	std::vector<Npc_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_npc;           // 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.
	int red;            // Index of color red in palbuf.
	bool drop_enabled;      // So we only do it once.
	void (*sel_changed)();      // Called when selection changes.
	// Blit onto screen.
	void show(int x, int y, int w, int h) override;
	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_npc(int npcnum);   // Find index for given NPC.
	void goto_index(unsigned index); // Get desired index in view.
	int get_selected_id() override {
		return selected;
	}
	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:
	Npc_chooser(Vga_file *i, unsigned char *palbuf, int w, int h,
	            Shape_group *g = nullptr, Shape_file_info *fi = nullptr);
	~Npc_chooser() override;
	void set_framenum0(int f) {
		framenum0 = f;
	}
	int get_count();        // Get # shapes we can display.
	std::vector<Estudio_npc> &get_npcs();
	void search(const char *srch, int dir) override;
	void locate(bool upwards) override;  // Locate NPC on game map.
	// Turn off selection.
	void unselect(bool need_render = true);
	void update_npc(int num);
	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);
	// Handle mouse press.
	gint mouse_press(GtkWidget *widget, GdkEventButton *event);
	// Give dragged shape.
	static void drag_data_get(GtkWidget *widget, GdkDragContext *context,
	                          GtkSelectionData *seldata,
	                          guint info, guint time, gpointer data);
	void edit_npc();
	static gint drag_begin(GtkWidget *widget, GdkDragContext *context,
	                       gpointer data);
	// Handler for drop.
	static void drag_data_received(GtkWidget *widget, GdkDragContext *context,
	                               gint x, gint y,
	                               GtkSelectionData *seldata,
	                               guint info, guint time, gpointer udata);
	void enable_drop();
	// 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);
};

#endif
