/*
 * This file is part of the Simutrans project under the Artistic License.
 * (see LICENSE.txt)
 */


#include "../dataobj/environment.h"
#include "../display/simimg.h"
#include "../display/simgraph.h"
#include "../player/simplay.h"
#include "../utils/simstring.h"
#include "../world/simworld.h"
#include "../tool/simmenu.h"
#include "../simskin.h"
#include "gui_frame.h"
#include "simwin.h"
#include "tool_selector.h"

#define MIN_WIDTH (80)


tool_selector_t::tool_selector_t(const char* title, const char *help_file, uint32 toolbar_id, bool allow_break) :
	gui_frame_t( translator::translate(title) ), tools(0)
{
	set_table_layout(0,0); // we do our own positioning of icons (for now)
	this->toolbar_id = toolbar_id;
	this->allow_break = allow_break;
	this->help_file = help_file;
	this->tool_icon_disp_start = 0;
	this->tool_icon_disp_end = 0;
	this->title = title;
	has_prev_next = false;
	is_dragging = false;
	offset = scr_coord( 0, 0 );
	set_windowsize( scr_size(max(env_t::iconsize.w,MIN_WIDTH), D_TITLEBAR_HEIGHT) );
	dirty = true;
}


/**
 * Add a new tool with values and tooltip text.
 * tool_in must be created by new tool_t(copy_tool)!
 */
void tool_selector_t::add_tool_selector(tool_t *tool_in)
{
	image_id tool_img = tool_in->get_icon(welt->get_active_player());
	if(  tool_img == IMG_EMPTY  &&  tool_in!=tool_t::dummy  ) {
		return;
	}

	// only for non-empty icons ...
	tools.append(tool_in);

	scr_rect screen = win_get_max_window_area();
	int ww = max(3,(screen.w/env_t::iconsize.w)-2); // to avoid zero or negative ww on posix (no graphic) backends
	tool_icon_width = tools.get_count();
DBG_DEBUG4("tool_selector_t::add_tool()","ww=%i, tool_icon_width=%i",ww,tool_icon_width);
	if(  allow_break  &&  (ww<tool_icon_width
		||  (env_t::toolbar_max_width>0  &&  env_t::toolbar_max_width<tool_icon_width)
		||  (env_t::toolbar_max_width<0  &&  (ww+env_t::toolbar_max_width)<tool_icon_width))
		) {
		//break them
		if (env_t::toolbar_max_width > 0) {
			ww = min(ww, env_t::toolbar_max_width);
		}
		else if (env_t::toolbar_max_width < 0) {
			ww = min(3, ww - env_t::toolbar_max_width);
		}

		int rows = (tool_icon_width/ww)+((tool_icon_width % ww)>0);
		uint16 max_tool_icon_height = max(1, (screen.h - win_get_statusbar_height()) / env_t::iconsize.h);
		tool_icon_width = min(ww, tool_icon_width);
		tool_icon_height = min(rows, max_tool_icon_height);
	}
	else {
		tool_icon_height = 1;
	}
	if(  env_t::toolbar_max_height > 0  ) {
		tool_icon_height = min(tool_icon_height, env_t::toolbar_max_height);
	}
	dirty = true;
	set_windowsize( scr_size( tool_icon_width*env_t::iconsize.w, tool_icon_height*env_t::iconsize.h+D_TITLEBAR_HEIGHT ) );
	tool_icon_disp_start = 0;
	tool_icon_disp_end = min( tool_icon_disp_start+tool_icon_width*tool_icon_height, tools.get_count() );
	has_prev_next = ((uint32)tool_icon_width*tool_icon_height < tools.get_count());

DBG_DEBUG4("tool_selector_t::add_tool()", "at position %i (width %i)", tools.get_count(), tool_icon_width);
}


// reset the tools to empty state
void tool_selector_t::reset_tools()
{
	tools.clear();
	gui_frame_t::set_windowsize( scr_size(max(env_t::iconsize.w,MIN_WIDTH), D_TITLEBAR_HEIGHT) );
	tool_icon_width = 0;
	tool_icon_disp_start = 0;
	tool_icon_disp_end = 0;
	offset = scr_coord( 0, 0 );
}


bool tool_selector_t::is_hit(int x, int y)
{
	int dx = (x-offset.x)/env_t::iconsize.w;
	int dy = (y-D_TITLEBAR_HEIGHT-offset.y)/env_t::iconsize.h;

	// either click in titlebar or on an icon
	if(  x>=0   &&  y>=0  &&  ( (y<D_TITLEBAR_HEIGHT  &&  x<get_windowsize().w)  ||  (dx<tool_icon_width  &&  dy<tool_icon_height) )  ) {
		return y < D_TITLEBAR_HEIGHT || dx + tool_icon_width * dy + tool_icon_disp_start < (int)tools.get_count();
	}
	return false;
}


bool tool_selector_t::infowin_event(const event_t *ev)
{
	if(  has_prev_next  &&  (IS_LEFTDRAG(ev)  ||  is_dragging)  ) {
		if( !is_dragging ) {
			old_offset = offset;
		}
		// currently only drag in x directions
		is_dragging = true;
		offset = old_offset + (tool_icon_height == 1 ? scr_coord(ev->mouse_pos.x - ev->click_pos.x, 0) : scr_coord(0, ev->mouse_pos.y - ev->click_pos.y));
		int xy = tool_icon_width*tool_icon_height;
		if(  tool_icon_height == 1  &&  tool_icon_disp_start + xy >= (int)tools.get_count()  ) {
			// we have to take into account that the height is not a full icon
			tool_icon_disp_start = max(0, (int)tools.get_count() - xy);
			offset.x = -min(-offset.x, env_t::iconsize.w-(get_windowsize().w % env_t::iconsize.w));
		}
		if(  tool_icon_width == 1  &&  tool_icon_disp_start + xy >= (int)tools.get_count()  ) {
			// we have to take into account that the height is not a full icon
			tool_icon_disp_start = max(0, (int)tools.get_count() - xy);
			offset.y = -min( -offset.y, (get_windowsize().h-D_TITLEBAR_HEIGHT) % env_t::iconsize.h);
		}
		if(  tool_icon_disp_start == 0  &&  (offset.x > 0  ||  offset.y > 0)  ) {
			offset.x = 0;
			offset.y = 0;
		}
		if (offset.x > 0) {
			// we must change the old offset, since the mouse starting point changed!
			old_offset.x -= env_t::iconsize.w;
			offset.x -= env_t::iconsize.w;
			tool_icon_disp_start--;
		}
		if (offset.x <= -env_t::iconsize.w) {
			old_offset.x += env_t::iconsize.w;
			offset.x += env_t::iconsize.w;
			tool_icon_disp_start++;
		}
		if (offset.y > 0) {
			// we must change the old offset, since the mouse starting point changed!
			old_offset.y -= env_t::iconsize.h;
			offset.y -= env_t::iconsize.h;
			tool_icon_disp_start--;
		}
		if (offset.y <= -env_t::iconsize.h) {
			old_offset.y += env_t::iconsize.h;
			offset.y += env_t::iconsize.h;
			tool_icon_disp_start++;
		}
		if(  !IS_LEFTRELEASE(ev)  &&  ev->mouse_button_state != MOUSE_LEFTBUTTON ) {
			is_dragging = false;
		}
	}

	// offsets sanity check
	while (offset.x < -env_t::iconsize.w) {
		offset.x += env_t::iconsize.w;
	}
	while (offset.x > 0) {
		offset.x -= env_t::iconsize.w;
	}
	while (offset.y < -env_t::iconsize.h) {
		offset.y += env_t::iconsize.h;
	}
	while (offset.y > 0) {
		offset.y -= env_t::iconsize.h;
	}
	if( tool_icon_disp_start > tool_icon_disp_end ) {
		tool_icon_disp_start = 0;
		offset.x = 0;
		offset.y = 0;
	}
	int xy = tool_icon_width*tool_icon_height;
	tool_icon_disp_end = (tool_icon_height == 1) ? min(tool_icon_disp_start+xy+(offset.x!=0), tools.get_count()) : min(tool_icon_disp_start + xy + (offset.y != 0), tools.get_count());

	if(IS_LEFTRELEASE(ev)  ||  IS_RIGHTRELEASE(ev)) {
		if( is_dragging ) {
			is_dragging = false;
			if( abs(old_offset.x - offset.x) > 2   ||  abs(old_offset.y - offset.y) > 2  ||  ev->click_pos.x-ev->mouse_pos.x != offset.x  ||  ev->click_pos.x - ev->mouse_pos.y != offset.y  ) {
				// we did dragg sucesfully before, so no tool selection!
				return true;
			}
		}

		// No dragging => Next check tooltips
		const int x = (ev->mouse_pos.x-offset.x) / env_t::iconsize.w;
		const int y = (ev->mouse_pos.y-offset.y-D_TITLEBAR_HEIGHT) / env_t::iconsize.h;

		const int wz_idx = x+(tool_icon_width*y)+tool_icon_disp_start;
		if( wz_idx>=0  &&  wz_idx < (int)tools.get_count()  ) {
			// change tool
			tool_t *tool = tools[wz_idx].tool;
			if(IS_LEFTRELEASE(ev)) {
				if(  env_t::reselect_closes_tool  &&  tool  &&  tool->is_selected() ) {
					// ->exit triggers tool_selector_t::infowin_event in the closing toolbar,
					// which resets active tool to query tool
					if( tool->exit( welt->get_active_player() ) ) {
						welt->set_tool( tool_t::general_tool[TOOL_QUERY], welt->get_active_player() );
					}
				}
				else {
					welt->set_tool( tool, welt->get_active_player() );
				}
			}
			else {
				// right-click on toolbar icon closes toolbars and dialogues. Resets selectable simple and general tools to the query-tool
				if(  tool  &&  tool->is_selected()  ) {
					// ->exit triggers tool_selector_t::infowin_event in the closing toolbar,
					// which resets active tool to query tool
					if(  tool->exit(welt->get_active_player())  ) {
						welt->set_tool( tool_t::general_tool[TOOL_QUERY], welt->get_active_player() );
					}
				}
			}
			return true;
		}
	}
	// this resets to query-tool, when closing toolsbar - but only for selected general tools in the closing toolbar
	else if(ev->ev_class==INFOWIN &&  ev->ev_code==WIN_CLOSE) {
		for(tool_data_t const i : tools) {
			if (i.tool->is_selected() && i.tool->get_id() & GENERAL_TOOL) {
				welt->set_tool( tool_t::general_tool[TOOL_QUERY], welt->get_active_player() );
				break;
			}
		}
	}
	// reset title, language may have changed
	else if(ev->ev_class==INFOWIN  &&  (ev->ev_code==WIN_TOP  ||  ev->ev_code==WIN_OPEN) ) {
		set_name( translator::translate(title) );
	}

	if(IS_WINDOW_CHOOSE_NEXT(ev)) {
		int xy = tool_icon_width*tool_icon_height;
		if(ev->ev_code==NEXT_WINDOW) {
			assert( xy >= tool_icon_width );
			tool_icon_disp_start += xy;
			if(  tool_icon_disp_start + xy > (int)tools.get_count() ) {
				tool_icon_disp_start = tools.get_count() - xy;
			}
			offset = scr_coord( 0, 0 );
		}
		else {
			if(  tool_icon_disp_start > xy  ) {
				tool_icon_disp_start -= xy;
			}
			else {
				tool_icon_disp_start = 0;
			}
			offset = scr_coord( 0, 0 );
		}
		tool_icon_disp_end = min(tool_icon_disp_start+xy, tools.get_count());
		dirty = true;
	}
	return false;
}


void tool_selector_t::draw(scr_coord pos, scr_size sz)
{
	player_t* player = welt->get_active_player();
	const scr_size screen = gfx->get_screen_size();

	if (toolbar_id == 0) {
		// checks for main menu (since it can change during changing layout)
		if (env_t::menupos == MENU_TOP || env_t::menupos == MENU_BOTTOM) {
			offset.y = 0;
			allow_break = false;
			tool_icon_width = (screen.w + env_t::iconsize.w - 1) / env_t::iconsize.w;
			tool_icon_height = 1; // only single row for title bar
			set_windowsize(sz);
			// check for too large values (after changing width etc.)
			if (screen.w >= (int)tools.get_count() * env_t::iconsize.w) {
				tool_icon_disp_start = 0;
				offset.x = 0;
			}
			else {
				scr_coord_val wx = (tools.get_count() - tool_icon_disp_start + 1) * env_t::iconsize.w + offset.x;
				if (wx < screen.w) {
					tool_icon_disp_start = tool_icon_disp_end < tool_icon_height ? 0 : tool_icon_disp_end - tool_icon_width;
					offset.x = screen.w - (tools.get_count() - tool_icon_disp_start) * env_t::iconsize.w;
				}
			}
			has_prev_next = (int)tools.get_count() * env_t::iconsize.w > sz.w;
		}
		else {
			offset.x = 0;
			allow_break = false;
			tool_icon_width = 1;
			// only single column for title bar
			scr_rect screen = win_get_max_window_area();
			tool_icon_height = max(1, (screen.h - win_get_statusbar_height() + env_t::iconsize.h - 1) / env_t::iconsize.h);
			set_windowsize(scr_size(env_t::iconsize.w, screen.h - win_get_statusbar_height()));

			if (screen.h >= (int)tools.get_count() * env_t::iconsize.h) {
				tool_icon_disp_start = 0;
				offset.y = 0;
			}
			else {
				scr_coord_val hx = (tools.get_count() - tool_icon_disp_start + 1) * env_t::iconsize.h + offset.y;
				if (hx < screen.h) {
					tool_icon_disp_end = tools.get_count();
					tool_icon_disp_start = tool_icon_disp_end < tool_icon_height ? 0 : tool_icon_disp_end - tool_icon_height;
					offset.y = screen.h - (tools.get_count() - tool_icon_disp_start) * env_t::iconsize.h;
				}
			}

			has_prev_next = (int)tools.get_count() * env_t::iconsize.h > sz.h;
		}
	}
	clip_dimension const p_cr = gfx->get_clip_rect(CLIP_NUM_DEFAULT_VALUE);
	if (toolbar_id != 0) {
		gfx->set_clip_rect(pos.x, pos.y, sz.w, sz.h CLIP_NUM_DEFAULT, false);
	}

	for(  uint i = tool_icon_disp_start;  i < tool_icon_disp_end;  i++  ) {
		const image_id icon_img = tools[i].tool->get_icon(player);
#if COLOUR_DEPTH != 0
		const scr_coord_val additional_xoffset = ( (i-tool_icon_disp_start)%(tool_icon_width+(offset.x!=0)) )*env_t::iconsize.w;
		const scr_coord_val additional_yoffset = D_TITLEBAR_HEIGHT+( (i-tool_icon_disp_start)/(tool_icon_width+(offset.x!=0)) )*env_t::iconsize.h;
#else
		const scr_coord_val additional_xoffset = 0;
		const scr_coord_val additional_yoffset = 0;
#endif
		const scr_coord draw_pos = pos + offset + scr_coord(additional_xoffset, additional_yoffset);
		const char *param = tools[i].tool->get_default_param();

		// we don't draw in main menu as it is already made in simwin.cc
		// no background if separator starts with "-b" and has an icon defined
		if(  toolbar_id>0  &&  !(strstart((param==NULL)? "" : param, "-b"))  ) {
			if(  skinverwaltung_t::toolbar_background  &&  skinverwaltung_t::toolbar_background->get_image_id(toolbar_id) != IMG_EMPTY  ) {
				const image_id back_img = skinverwaltung_t::toolbar_background->get_image_id(toolbar_id);
				gfx->fit_img_to_width( back_img, env_t::iconsize.w );
				gfx->draw_color_img( back_img, draw_pos.x, draw_pos.y, welt->get_active_player_nr(), false, true CLIP_NUM_DEFAULT);
			}
			else {
				gfx->draw_rect_clipped(draw_pos.x, draw_pos.y, env_t::iconsize.w, env_t::iconsize.h, gfx->palette_lookup(MN_GREY2), false CLIP_NUM_DEFAULT);
			}
		}

		// if there's no image we simply skip, button will be transparent showing toolbar background
		if(  icon_img != IMG_EMPTY  ) {
			bool tool_dirty = dirty  ||  (tools[i].tool->is_selected() ^ tools[i].selected);
			gfx->fit_img_to_width( icon_img, env_t::iconsize.w );
			gfx->draw_color_img(icon_img, draw_pos.x, draw_pos.y, player->get_player_nr(), false, tool_dirty CLIP_NUM_DEFAULT);
			tools[i].tool->draw_after( draw_pos, tool_dirty);
			if (!tools[i].tool->enabled) {
				// grey out disbaled entries
				gfx->draw_img_blend(icon_img, draw_pos.x, draw_pos.y, TRANSPARENT75_FLAG | OUTLINE_FLAG | gfx->palette_lookup(COL_WHITE), false, tool_dirty);
			}
			// store whether tool was selected
			tools[i].selected = tools[i].tool->is_selected();
		}
	}

	if( is_dragging ) {
		gfx->mark_rect_dirty_wc(pos.x, pos.y+D_TITLEBAR_HEIGHT, pos.x+sz.w, pos.y+sz.h+D_TITLEBAR_HEIGHT );
	}
	else if(  dirty  &&  (tool_icon_disp_end-tool_icon_disp_start < tool_icon_width*tool_icon_height)  ) {
		// mark empty space empty
		gfx->mark_rect_dirty_wc(pos.x, pos.y, pos.x + tool_icon_width*env_t::iconsize.w, pos.y + tool_icon_height*env_t::iconsize.h);
	}
	if (toolbar_id != 0) {
		gfx->set_clip_rect(p_cr.x, p_cr.y, p_cr.w, p_cr.h CLIP_NUM_DEFAULT, false);
	}

	if(  offset.x != 0  &&  tool_icon_disp_start > 0  ) {
		gfx->draw_color_img(gui_theme_t::arrow_button_left_img[0], pos.x, pos.y + D_TITLEBAR_HEIGHT, 0, false, false CLIP_NUM_DEFAULT);
	}
	if(  offset.y != 0  &&  tool_icon_disp_start > 0  ) {
		gfx->draw_color_img(gui_theme_t::arrow_button_up_img[0], pos.x, pos.y + D_TITLEBAR_HEIGHT, 0, false, false CLIP_NUM_DEFAULT);
	}
	if(  tool_icon_height == 1  &&  (tool_icon_disp_start+tool_icon_width < tools.get_count()  ||  (-offset.x) < env_t::iconsize.w*tool_icon_width-get_windowsize().w)  ) {
		gfx->draw_color_img( gui_theme_t::arrow_button_right_img[0], pos.x+sz.w-D_ARROW_UP_WIDTH, pos.y+D_TITLEBAR_HEIGHT, 0, false, false CLIP_NUM_DEFAULT);
	}
	if(  tool_icon_width == 1  &&  (tool_icon_disp_start+tool_icon_height < tools.get_count()  ||  (-offset.y) < env_t::iconsize.h*tool_icon_height-get_windowsize().h)  ) {
		gfx->draw_color_img(gui_theme_t::arrow_button_down_img[0], pos.x+sz.w-D_ARROW_DOWN_WIDTH, pos.y+D_TITLEBAR_HEIGHT+sz.h-D_ARROW_DOWN_HEIGHT, 0, false, false CLIP_NUM_DEFAULT);
	}

	if(  !is_dragging  ) {
		// tooltips?
		const scr_coord mouse_pos = get_mouse_pos();

		if(  is_hit(mouse_pos.x-pos.x, mouse_pos.y-pos.y)  &&  (mouse_pos.y-pos.y > D_TITLEBAR_HEIGHT)) {
			const scr_coord_val xdiff = (mouse_pos.x - pos.x                     - offset.x) / env_t::iconsize.w;
			const scr_coord_val ydiff = (mouse_pos.y - pos.y - D_TITLEBAR_HEIGHT - offset.y) / env_t::iconsize.h;

			if(  xdiff>=0  &&  xdiff<tool_icon_width  &&  ydiff>=0  ) {
				const int tipnr = xdiff+(tool_icon_width*ydiff)+tool_icon_disp_start;
				if(  tipnr < (int)tool_icon_disp_end  ) {
					const char* tipstr = tools[tipnr].tool->get_tooltip(welt->get_active_player());
					win_set_tooltip(mouse_pos + TOOLTIP_MOUSE_OFFSET, tipstr, tools[tipnr].tool, this);
				}
			}
		}
	}

	dirty = false;
	//as we do not call gui_frame_t::draw, we reset dirty flag explicitly
	unset_dirty();
}


bool tool_selector_t::empty(player_t *player) const
{
	for(tool_data_t w : tools) {
		if (w.tool->get_icon(player) != IMG_EMPTY) {
			return false;
		}
	}
	return true;
}
