/*
Copyright (C) 2000-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.
*/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include "Actor_gump.h"
#include "actors.h"
#include "array_size.h"
#include "gamewin.h"
#include "misc_buttons.h"
#include "ignore_unused_variable_warning.h"

#include <cstdio>

using std::size_t;

#define TWO_HANDED_BROWN_SHAPE  48
#define TWO_HANDED_BROWN_FRAME  0
#define TWO_FINGER_BROWN_SHAPE  48
#define TWO_FINGER_BROWN_FRAME  1

/*
 *  Statics:
 */

Actor_gump::Position Actor_gump::disk   = {124, 115};
Actor_gump::Position Actor_gump::heart  = {124, 132};
Actor_gump::Position Actor_gump::combat = {52, 100};
Actor_gump::Position Actor_gump::halo   = {47, 110};
Actor_gump::Position Actor_gump::cmode  = {48, 132};
Actor_gump::Position Actor_gump::coords[12] = {
	{114, 10},    /* head */      {115, 24},    /* back */
	{115, 37},    /* belt */      {115, 55},    /* lhand */
	{115, 71},    /* lfinger */   {114, 85},    /* legs */
	{76, 98},     /* feet */      {35, 70},     /* rfinger */
	{37, 56},     /* rhand */     {37, 37},     /* torso */
	{37, 24},     /* neck */      {37, 11}      /* ammo */
};

/*
 *  Find the index of the closest 'spot' to a mouse point.
 *
 *  Output: Index, or -1 if unsuccessful.
 */

int Actor_gump::find_closest(
    int mx, int my,         // Mouse point in window.
    int only_empty          // Only allow empty spots.
) {
	mx -= x;
	my -= y;       // Get point rel. to us.
	long closest_squared = 1000000; // Best distance squared.
	int closest = -1;       // Best index.
	for (size_t i = 0; i < array_size(coords); i++) {
		int dx = mx - coords[i].x;
		int dy = my - coords[i].y;
		long dsquared = dx * dx + dy * dy;
		// Better than prev.?
		if (dsquared < closest_squared && (!only_empty ||
		                                   !container->get_readied(i))) {
			closest_squared = dsquared;
			closest = i;
		}
	}
	return closest;
}

/*
 *  Create the gump display for an actor.
 */

Actor_gump::Actor_gump(
    Container_game_object *cont,    // Container it represents.  MUST
    //   be an Actor.
    int initx, int inity,       // Coords. on screen.
    int shnum           // Shape #.
) : Gump(cont, initx, inity, shnum) {
	set_object_area(TileRect(26, 0, 104, 132), 6, 136);
	Actor *npc = cont->as_actor();
	add_elem(new Heart_button(this, heart.x, heart.y));
	if (npc->get_npc_num() == 0) {
		add_elem(new Disk_button(this, disk.x, disk.y));
		add_elem(new Combat_button(this, combat.x, combat.y));
	}
	add_elem(new Halo_button(this, halo.x, halo.y, npc));
	add_elem(new Combat_mode_button(this, cmode.x, cmode.y, npc));

	for (size_t i = 0; i < array_size(coords); i++) {
		// Set object coords.
		Game_object *obj = container->get_readied(i);
		if (obj)
			set_to_spot(obj, i);
	}
}

/*
 *  Add an object.
 *
 *  Output: false if cannot add it.
 */

bool Actor_gump::add(
    Game_object *obj,
    int mx, int my,         // Screen location of mouse.
    int sx, int sy,         // Screen location of obj's hotspot.
    bool dont_check,        // Skip volume check.
    bool combine            // True to try to combine obj.  MAY
    //   cause obj to be deleted.
)
{
	ignore_unused_variable_warning(sx, sy);
	Game_object *cont = find_object(mx, my);

	if (cont && cont->add(obj, false, combine))
		return true;

	int index = find_closest(mx, my, 1);

	if (index != -1 && container->add_readied(obj, index))
		return true;

	if (container->add(obj, dont_check, combine))
		return true;

	return false;
}

/*
 *  Set object's coords. to given spot.
 */

void Actor_gump::set_to_spot(
    Game_object *obj,
    int index           // Spot index.
) {
	// Get shape info.
	Shape_frame *shape = obj->get_shape();
	if (!shape)
		return;         // Not much we can do.
	int w = shape->get_width();
	int h = shape->get_height();
	// Set object's position.
	obj->set_shape_pos(
	    coords[index].x + shape->get_xleft() - w / 2 - object_area.x,
	    coords[index].y + shape->get_yabove() - h / 2 - object_area.y);
	// Shift if necessary.
	int x0 = obj->get_tx() - shape->get_xleft();
	int y0 = obj->get_ty() - shape->get_yabove();
	int newcx = obj->get_tx();
	int newcy = obj->get_ty();
	if (x0 < 0)
		newcx -= x0;
	if (y0 < 0)
		newcy -= y0;
	int x1 = x0 + w;
	int y1 = y0 + h;
	if (x1 > object_area.w)
		newcx -= x1 - object_area.w;
	if (y1 > object_area.h)
		newcy -= y1 - object_area.h;
	obj->set_shape_pos(newcx, newcy);
}

/*
 *  Paint on screen.
 */

void Actor_gump::paint(
) {
	// Watch for any newly added objs.
	for (size_t i = 0; i < array_size(coords); i++) {
		// Set object coords.
		Game_object *obj = container->get_readied(i);
		if (obj)//&& !obj->get_tx() && !obj->get_ty())
			set_to_spot(obj, i);
	}

	Gump::paint();          // Paint gump & objects.

	// Paint over blue lines for 2 handed
	Actor *actor = container->as_actor();
	if (actor) {
		if (actor->is_two_fingered()) {
			int sx = x + 36;
			int // Note this is the right finger slot shifted slightly
			    sy = y + 70;
			ShapeID sid(TWO_FINGER_BROWN_SHAPE, TWO_FINGER_BROWN_FRAME, SF_GUMPS_VGA);
			sid.paint_shape(sx, sy);
		}
		if (actor->is_two_handed()) {
			int sx = x + 36;
			int // Note this is the right hand slot shifted slightly
			    sy = y + 55;
			ShapeID sid(TWO_HANDED_BROWN_SHAPE, TWO_HANDED_BROWN_FRAME, SF_GUMPS_VGA);
			sid.paint_shape(sx, sy);
		}
	}
	// Show weight.
	int max_weight = container->get_max_weight();
	int weight = container->get_weight() / 10;
	char text[20];
	snprintf(text, 20, "%d/%d", weight, max_weight);
	int twidth = sman->get_text_width(2, text);
	const int boxw = 102;
	sman->paint_text(2, text, x + 28 + (boxw - twidth) / 2, y + 120);
}

Container_game_object *Actor_gump::find_actor(int mx, int my) {
	ignore_unused_variable_warning(mx, my);
	return container;
}
