/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001-2005 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"

namespace Simon {

static const char *const russian_verb_names[] = {
	"Ietj _",
	"Qnotrft< pa",
	"Nt_r[t<",
	"Ecjdat<",

	"Q=fst<",
	"C^]t<",
	"Ha_r[t<",
	"Isqom<^ocat<",

	"Docorjt<",
	"Qp]t<",
	"Neft<",
	"Eat<"
};

static const char *const hebrew_verb_names[] = {
	"LJ @L",
	"DQZKL RL",
	"TZG",
	"DFF",

	"@KEL",
	"DXM",
	"QBEX",
	"DYZNY",

	"CAX @L",
	"DQX",
	"LAY",
	"ZO"
};

static const char *const spanish_verb_names[] = {
	"Caminar",
	"Mirar",
	"Abrir",
	"Mover",

	"Consumir",
	"Coger",
	"Cerrar",
	"Usar",

	"Hablar",
	"Quitar",
	"Llevar",
	"Dar"
};

static const char *const italian_verb_names[] = {
	"Vai verso",
	"Osserva",
	"Apri",
	"Sposta",

	"Mangia",
	"Raccogli",
	"Chiudi",
	"Usa",

	"Parla a",
	"Togli",
	"Indossa",
	"Dai"
};

static const char *const french_verb_names[] = {
	"Aller vers",
	"Regarder",
	"Ouvrir",
	"D/placer",

	"Consommer",
	"Prendre",
	"Fermer",
	"Utiliser",

	"Parler ;",
	"Enlever",
	"Mettre",
	"Donner"
};

static const char *const german_verb_names[] = {
	"Gehe zu",
	"Schau an",
	";ffne",
	"Bewege",

	"Verzehre",
	"Nimm",
	"Schlie+e",
	"Benutze",

	"Rede mit",
	"Entferne",
	"Trage",
	"Gib"
};

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

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

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

static const char *const russian_verb_prep_names[] = {
	"", "", "", "",
	"", "", "", "s yfn?",
	"", "", "", "_onu ?"
};

static const char *const hebrew_verb_prep_names[] = {
	"", "", "", "",
	"", "", "", "RM ND ?",
	"", "", "", "LNI ?"
};

static const char *const spanish_verb_prep_names[] = {
	"", "", "", "",
	"", "", "", "^con qu/?",
	"", "", "", "^a qui/n?"
};

static const char *const italian_verb_prep_names[] = {
	"", "", "", "",
	"", "", "", "con cosa ?",
	"", "", "", "a chi ?"
};

static const char *const french_verb_prep_names[] = {
	"", "", "", "",
	"", "", "", "avec quoi ?",
	"", "", "", "; qui ?"
};

static const char *const german_verb_prep_names[] = {
	"", "", "", "",
	"", "", "", "mit was ?",
	"", "", "", "zu wem ?"
};

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

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

	if (_game & GF_SIMON2) {
		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 && (ha = findHitAreaByID(200)) && (ha->flags & 0x40) && !(last->flags & 0x40))
		focusVerb(last->id);
}

void SimonEngine::focusVerb(uint hitarea_id) {
	uint x;
	const char *txt;
	const char * const *verb_names;
	const char * const *verb_prep_names;

	hitarea_id -= 101;

	if (_show_preposition) {
		switch (_language) {
		case 21: verb_prep_names = russian_verb_prep_names; break;
		case 20: verb_prep_names = hebrew_verb_prep_names; break;
		case  5: verb_prep_names = spanish_verb_prep_names; break;
		case  3: verb_prep_names = italian_verb_prep_names; break;
		case  2: verb_prep_names = french_verb_prep_names; break;
		case  1: verb_prep_names = german_verb_prep_names; break;
		default: verb_prep_names = english_verb_prep_names; break;
		}
		CHECK_BOUNDS(hitarea_id, english_verb_prep_names);
		txt = verb_prep_names[hitarea_id];
	} else {
		switch (_language) {
		case 21: verb_names = russian_verb_names; break;
		case 20: verb_names = hebrew_verb_names; break;
		case  5: verb_names = spanish_verb_names; break;
		case  3: verb_names = italian_verb_names; break;
		case  2: verb_names = french_verb_names; break;
		case  1: verb_names = german_verb_names; break;
		default: verb_names = english_verb_names; break;
		}
		CHECK_BOUNDS(hitarea_id, english_verb_names);
		txt = verb_names[hitarea_id];
	}
	x = (53 - strlen(txt)) * 3;
	showActionString(x, (const byte *)txt);
}

void SimonEngine::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 SimonEngine::hitareaChangedHelper() {
	FillOrCopyStruct *fcs;

	if (_game & GF_SIMON2) {
		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 *SimonEngine::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 *SimonEngine::findEmptyHitArea() {
	HitArea *ha = _hit_areas;
	uint count = ARRAYSIZE(_hit_areas);

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

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

void SimonEngine::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 SimonEngine::set_hitarea_x_y(uint hitarea, int x, int y) {
	HitArea *ha = findHitAreaByID(hitarea);
	if (ha != NULL) {
		ha->x = x;
		ha->y = y;
	}
}

void SimonEngine::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 SimonEngine::is_hitarea_0x40_clear(uint hitarea) {
	HitArea *ha = findHitAreaByID(hitarea);
	if (ha == NULL)
		return false;
	return (ha->flags & 0x40) == 0;
}

void SimonEngine::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 SimonEngine::hitarea_proc_1() {
	uint id;
	HitArea *ha;

	if (_game & GF_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 SimonEngine::handle_verb_hitarea(HitArea *ha) {
	HitArea *tmp = _hitarea_ptr_5;

	if (ha == tmp)
		return;

	if (!(_game & GF_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 SimonEngine::hitarea_leave(HitArea *ha) {
	if (!(_game & GF_SIMON2)) {
		video_toggle_colors(ha, 0xdf, 0xd5, 0xda, 5);
	} else {
		video_toggle_colors(ha, 0xe7, 0xe5, 0xe6, 1);
	}
}

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

void SimonEngine::handle_uparrow_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 SimonEngine::handle_downarrow_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 SimonEngine::setup_hitarea_from_pos(uint x, uint y, uint mode) {
	HitArea *best_ha;
	HitArea *ha = _hit_areas;
	uint count = ARRAYSIZE(_hit_areas);
	uint16 layer = 0;
	uint16 x_ = x;
	const uint16 y_ = y;

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

	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 SimonEngine::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 SimonEngine::hitarea_proc_2(uint a) {
	uint x;
	const byte *string_ptr;

	if (_game & GF_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 SimonEngine::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 = (Child2 *)findChildOfType(item, 2);
	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;
}

} // End of namespace Simon