/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001/2002 The ScummVM project
 *
 * 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.
 *
 * $Header$
 *
 */

// Verb and hitarea handling
#include "stdafx.h"
#include "simon/simon.h"
#include "simon/intern.h"

void SimonState::defocusHitarea()
{
	HitArea *last;
	HitArea *ha;

	if ((_game == GAME_SIMON2WIN) || (_game == GAME_SIMON2DOS)) {
		if (_bit_array[4] & 0x8000) {
			o_unk_120(202);
			_last_hitarea_2_ptr = NULL;
			return;
		}
	}

	last = _hitarea_ptr_5;

	if (last == _hitarea_ptr_7)
		return;

	hitareaChangedHelper();
	_hitarea_ptr_7 = last;

	if (last != NULL && _hitarea_unk_6 &&
			(ha = findHitAreaByID(200)) && (ha->flags & 0x40) && !(last->flags & 0x40))
		focusVerb(last->id);
}

static const char *const verb_names[] = {
	"Walk to",
	"Look at",
	"Open",
	"Move",

	"Consume",
	"Pick up",
	"Close",
	"Use",

	"Talk to",
	"Remove",
	"Wear",
	"Give"
};

static const char *const verb_prep_names[] = {
	"", "", "", "",
	"", "", "", "with what ?",
	"", "", "", "to whom ?"
};

void SimonState::focusVerb(uint hitarea_id)
{
	uint x;
	const char *txt;

	hitarea_id -= 101;

	CHECK_BOUNDS(hitarea_id, verb_prep_names);

	if (_show_preposition) {
		txt = verb_prep_names[hitarea_id];
	} else {
		txt = verb_names[hitarea_id];
	}
	x = (53 - strlen(txt)) * 3;
	showActionString(x, (const byte *)txt);

}

void SimonState::showActionString(uint x, const byte *string)
{
	FillOrCopyStruct *fcs;

	fcs = _fcs_ptr_array_3[1];
	if (fcs == NULL || fcs->text_color == 0)
		return;

	fcs->textColumn = x >> 3;
	fcs->textColumnOffset = x & 7;

	for (; *string; string++)
		video_putchar(fcs, *string);
}


void SimonState::hitareaChangedHelper()
{
	FillOrCopyStruct *fcs;

	if ((_game == GAME_SIMON2WIN) || (_game == GAME_SIMON2DOS)) {
		if (_bit_array[4] & 0x8000)
			return;
	}

	fcs = _fcs_ptr_array_3[1];
	if (fcs != NULL && fcs->text_color != 0)
		video_fill_or_copy_from_3_to_2(fcs);

	_last_hitarea_2_ptr = NULL;
	_hitarea_ptr_7 = NULL;
}

HitArea *SimonState::findHitAreaByID(uint hitarea_id)
{
	HitArea *ha = _hit_areas;
	uint count = ARRAYSIZE(_hit_areas);

	do {
		if (ha->id == hitarea_id)
			return ha;
	} while (ha++, --count);
	return NULL;
}

HitArea *SimonState::findEmptyHitArea()
{
	HitArea *ha = _hit_areas;
	uint count = ARRAYSIZE(_hit_areas);

	do {
		if (ha->flags == 0)
			return ha;
	} while (ha++, --count);
	return NULL;
}

void SimonState::clear_hitarea_bit_0x40(uint hitarea)
{
	HitArea *ha = findHitAreaByID(hitarea);
	if (ha != NULL)
		ha->flags &= ~0x40;
}

void SimonState::set_hitarea_bit_0x40(uint hitarea)
{
	HitArea *ha = findHitAreaByID(hitarea);
	if (ha != NULL) {
		ha->flags |= 0x40;
		ha->flags &= ~2;
		if (hitarea == 102)
			hitarea_proc_1();
	}
}

void SimonState::set_hitarea_x_y(uint hitarea, int x, int y)
{
	HitArea *ha = findHitAreaByID(hitarea);
	if (ha != NULL) {
		ha->x = x;
		ha->y = y;
	}
}

void SimonState::delete_hitarea(uint hitarea)
{
	HitArea *ha = findHitAreaByID(hitarea);
	if (ha != NULL) {
		ha->flags = 0;
		if (ha == _last_hitarea_2_ptr)
			defocusHitarea();
		_need_hitarea_recalc++;
	}
}

bool SimonState::is_hitarea_0x40_clear(uint hitarea)
{
	HitArea *ha = findHitAreaByID(hitarea);
	if (ha == NULL)
		return false;
	return (ha->flags & 0x40) == 0;
}

void SimonState::addNewHitArea(int id, int x, int y, int width, int height,
															 int flags, int unk3, Item *item_ptr)
{

	HitArea *ha;
	delete_hitarea(id);

	ha = findEmptyHitArea();
	ha->x = x;
	ha->y = y;
	ha->width = width;
	ha->height = height;
	ha->flags = flags | 0x20;
	ha->id = ha->layer = id;
	ha->unk3 = unk3;
	ha->item_ptr = item_ptr;

	_need_hitarea_recalc++;
}

void SimonState::hitarea_proc_1()
{
	uint id;
	HitArea *ha;

	if (_game & GAME_SIMON2) {
		id = 2;
		if (!(_bit_array[4] & 0x8000))
			id = (_mouse_y >= 136) ? 102 : 101;
	} else {
		id = (_mouse_y >= 136) ? 102 : 101;

	}

	_hitarea_unk_4 = id;

	ha = findHitAreaByID(id);
	if (ha == NULL)
		return;

	if (ha->flags & 0x40) {
		_hitarea_unk_4 = 999;
		_hitarea_ptr_5 = NULL;
	} else {
		_verb_hitarea = ha->unk3;
		handle_verb_hitarea(ha);
	}
}

void SimonState::handle_verb_hitarea(HitArea * ha)
{
	HitArea *tmp = _hitarea_ptr_5;

	if (ha == tmp)
		return;

	if (!(_game & GAME_SIMON2)) {
		if (tmp != NULL) {
			tmp->flags |= 8;
			video_toggle_colors(tmp, 0xd5, 0xd0, 0xd5, 0xA);
		}

		if (ha->flags & 2)
			video_toggle_colors(ha, 0xda, 0xd5, 0xd5, 5);
		else
			video_toggle_colors(ha, 0xdf, 0xda, 0xda, 0xA);

		ha->flags &= ~(2 + 8);

	} else {
		if (ha->id < 101)
			return;
		_mouse_cursor = ha->id - 101;
		_need_hitarea_recalc++;

	}

	_hitarea_ptr_5 = ha;
}

void SimonState::hitarea_leave(HitArea * ha)
{
	if (!(_game & GAME_SIMON2)) {
		video_toggle_colors(ha, 0xdf, 0xd5, 0xda, 5);
	} else {
		video_toggle_colors(ha, 0xe7, 0xe5, 0xe6, 1);
	}
}

void SimonState::leaveHitAreaById(uint hitarea_id)
{
	HitArea *ha = findHitAreaByID(hitarea_id);
	if (ha)
		hitarea_leave(ha);
}

void SimonState::handle_unk2_hitarea(FillOrCopyStruct *fcs)
{
	uint index;

	index = get_fcs_ptr_3_index(fcs);

	if (fcs->fcs_data->unk1 == 0)
		return;

	lock();
	fcs_unk_proc_1(index, fcs->fcs_data->item_ptr, fcs->fcs_data->unk1 - 1, fcs->fcs_data->unk2);
	unlock();
}

void SimonState::handle_unk_hitarea(FillOrCopyStruct *fcs)
{
	uint index;

	index = get_fcs_ptr_3_index(fcs);

	lock();
	fcs_unk_proc_1(index, fcs->fcs_data->item_ptr, fcs->fcs_data->unk1 + 1, fcs->fcs_data->unk2);
	unlock();
}

void SimonState::setup_hitarea_from_pos(uint x, uint y, uint mode)
{
	HitArea *best_ha;

	if (_game & GAME_SIMON2) {
		if (_bit_array[4] & 0x8000 || y < 134) {
			x += _x_scroll * 8;
		}
	}

	{
		HitArea *ha = _hit_areas;
		uint count = ARRAYSIZE(_hit_areas);
		uint16 layer = 0;
		const uint16 x_ = x;
		const uint16 y_ = y;

		best_ha = NULL;

		do {
			if (ha->flags & 0x20) {
				if (!(ha->flags & 0x40)) {
					if (x_ >= ha->x && y_ >= ha->y &&
							x_ - ha->x < ha->width && y_ - ha->y < ha->height && layer <= ha->layer) {
						layer = ha->layer;
						best_ha = ha;
					} else {
						if (ha->flags & 2) {
							hitarea_leave(ha);
							ha->flags &= ~2;
						}
					}
				} else {
					ha->flags &= ~2;
				}
			}
		} while (ha++, --count);
	}

	if (best_ha == NULL) {
		defocusHitarea();
		return;
	}

	if (mode != 0 && mode != 3) {
		_last_hitarea = best_ha;
		_variableArray[1] = x;
		_variableArray[2] = y;
	}

	if (best_ha->flags & 4) {
		defocusHitarea();
	} else if (best_ha != _last_hitarea_2_ptr) {
		new_current_hitarea(best_ha);
	}

	if (best_ha->flags & 8 && !(best_ha->flags & 2)) {
		hitarea_leave(best_ha);
		best_ha->flags |= 2;
	}

	return;
}

void SimonState::new_current_hitarea(HitArea * ha)
{
	bool result;

	hitareaChangedHelper();
	if (ha->flags & 1) {
		result = hitarea_proc_2(ha->flags >> 8);
	} else {
		result = hitarea_proc_3(ha->item_ptr);
	}

	if (result)
		_last_hitarea_2_ptr = ha;
}

bool SimonState::hitarea_proc_2(uint a)
{
	uint x;
	const byte *string_ptr;

	if (_game & GAME_SIMON2) {
		if (_bit_array[4] & 0x8000) {
			Subroutine *sub;
			_variableArray[84] = a;
			sub = getSubroutineByID(5003);
			if (sub != NULL)
				startSubroutineEx(sub);
			return true;
		}
	}

	if (a >= 20)
		return false;

	string_ptr = getStringPtrByID(_stringid_array_2[a]);
	// Arisme : hack for long strings in the French version
	if ((strlen((const char*)string_ptr) - 1) <= 53)
		x = (53 - (strlen((const char *)string_ptr) - 1)) * 3;
	else
		x = 0;
	showActionString(x, string_ptr);

	return true;
}

bool SimonState::hitarea_proc_3(Item *item)
{
	Child2 *child2;
	uint x;
	const byte *string_ptr;

	if (item == 0 || item == _dummy_item_2 || item == _dummy_item_3)
		return false;

	child2 = findChildOfType2(item);
	if (child2 == NULL)
		return false;

	string_ptr = getStringPtrByID(child2->string_id);
	// Arisme : hack for long strings in the French version
	if ((strlen((const char*)string_ptr) - 1) <= 53)
		x = (53 - (strlen((const char *)string_ptr) - 1)) * 3;
	else
		x = 0;
	showActionString(x, string_ptr);
	return true;
}