/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * Additional copyright for this file:
 * Copyright (C) 1999-2000 Revolution Software Ltd.
 * This code is based on source code created by Revolution Software,
 * used with permission.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 */

#include "engines/icb/p4.h"
#include "engines/icb/debug_pc.h"
#include "engines/icb/common/px_globalvariables.h"

namespace ICB {

CpxGlobalScriptVariables::CpxGlobalScriptVariables() {
	m_no_vars = 0;
	m_sorted = 0;

	int32 i;
	for (i = 0; i < MAX_global_vars; i++) {
		m_vars[i].hash = 666;
		m_vars[i].value = 666;
		m_varInit[i] = GLOBAL_VAR_NOT_SET;
	}
}

int32 CpxGlobalScriptVariables::FindVariable(uint32 hash) {
	int32 index = -1;

	if (m_sorted == 0) {
		SortVariables();
	} else {
		// Use binary search system to find the variables
		// The variables are stored in ascending hash sorted order
		int32 min = 0;
		int32 max = m_no_vars;
		index = ((max - min) >> 1);
		CpxVariable *pvar = m_vars + index;

		// Start at the middle of the list
		while (pvar->hash != hash) {
			// Not found
			if ((index == min) || (index == max)) {
				index = -1;
				break;
			}

			// Is it further up the table ?
			if (hash > pvar->hash) {
				min = index;
				// Go down from min so we creep towards maximum
				index = max - ((max - min) >> 1);
			} else {
				max = index;
				// Go up from min so we creep towards minimum
				index = min + ((max - min) >> 1);
			}
			pvar = m_vars + index;

			if (max == min)
				Fatal_error("GlobalVars Binary search failed max==min %d number %d", max, m_no_vars);
		}

//#define CHECK_BINARY_SEARCH
#ifdef CHECK_BINARY_SEARCH
		uint32 i;
		int32 oldi = 0;
		// Check the binarry search worked
		for (i = 0; i < m_no_vars; i++) {
			if (m_vars[i].hash == hash) {
				oldi = i;
				break;
			}
		}
		if (oldi != index) {
			Fatal_error("Binary search failed");
		}
#endif
	}

	return index;
}

void CpxGlobalScriptVariables::InitVariable(uint32 hash, int32 value, const char *name) {
	// If the variable exists then it has already been initialised

	int32 i = FindVariable(hash);
	if (i == -1) {
		m_vars[m_no_vars].hash = hash;
		m_vars[m_no_vars].value = value;
		if (name) {
			Tdebug("gtable.txt", "%s , %d , 0x%X", name, value, hash);
		}
		m_no_vars++;
		// The list is no int32er sorted !
		m_sorted = 0;
	} else {
		// The variable has been set, so initing it is an error
		//		Fatal_error( "Global variable with hash 0x%08x has already been initialised",hash); // Game engine error
		m_vars[i].value = value;
	}
}

void CpxGlobalScriptVariables::SetVariable(uint32 hash, int32 value) {
	// Set a variable
	// You can't set a variable that has not been initialised
	// Has the variable been defined already
	int32 i = FindVariable(hash);
	if (i != -1) {
		// Once a variable is set then flag it as such
		m_varInit[i] = GLOBAL_VAR_SET;

		// Ok, just return the value
		m_vars[i].value = value;
	} else {
		// The variable hasn't been set, so accessing it is an error
		Fatal_error("SetVariable::Global variable with hash 0x%08x has been accessed before it was initialised", hash); // Game engine error
	}
}

// warn = 1
// Give a warning if someone Get's the value before Setting it
// i.e. they are relying on the initialisation code which is bad
// warn = 0
// Don't give the warning - this is needed for save games which Get all global's !
int32 CpxGlobalScriptVariables::GetVariable(uint32 hash, const char *name, int32 warn) {
	// Has the variable been defined already
	int32 i = FindVariable(hash);
	if (i != -1) {
		if (warn == 1) {
			// Give a warning if someone Get's the value before Setting it
			// i.e. they are relying on the initialisation code which is bad
			// Once a variable is set then flag it as such
			if (m_varInit[i] == GLOBAL_VAR_NOT_SET) {
				// Only give the warning once
				m_varInit[i] = GLOBAL_VAR_SET;

				if (name) {
					{ Message_box("GetVariable::Global variable '%s' hash 0x%08X value %d accessed before it was set", name, hash, m_vars[i].value); }
				} else {
					{ Message_box("GetVariable::Global variable hash 0x%08X value %d accessed before it was set", hash, m_vars[i].value); }
				}
			}
		}

		// Ok, just return the value
		return m_vars[i].value;
	} else {
		// The variable hasn't been set, so accessing it is an error
		Fatal_error("GetVariable::Global variable with hash 0x%08X has been accessed before it was initialised", hash); // Game engine error
		return 0;
	}
}

//
// Sort the variables so searching for them is quicker !
// The search method can then use a binary chop system
//
void CpxGlobalScriptVariables::SortVariables() {
	uint32 i;
	uint32 j;
	CpxVariable temp;
	uint8 temp8;

	// Sort the variable in ascending order
	// e.g. lowest -> highest
	for (i = 0; i < m_no_vars; i++) {
		for (j = i + 1; j < m_no_vars; j++) {
			if (m_vars[i].hash > m_vars[j].hash) {
				temp = m_vars[i];
				m_vars[i] = m_vars[j];
				m_vars[j] = temp;

				temp8 = m_varInit[i];
				m_varInit[i] = m_varInit[j];
				m_varInit[j] = temp8;
			}
		}
	}
	// The list is now sorted
	m_sorted = 1;
}

} // End of namespace ICB
