/* 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.
 *
 * 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/>.
 *
 */
/*
 * This code is based on original Tony Tough source code
 *
 * Copyright (c) 1997-2003 Nayma Software
 */

/****************************************************************************\
*       General Introduction
\****************************************************************************/

/*
 * MPAL (MultiPurpose Adventure Language) is a high level language
 * for the definition of adventure. Through the use of MPAL you can describe
 * storyboard the adventure, and then use it with any user interface.
 * In fact, unlike many other similar products, MPAL is not programmed through
 * the whole adventure, but are defined only the locations, objects, as they may
 * interact with each other, etc.. thus making MPAL useful for any type of adventure.
 */

/****************************************************************************\
*       Structure
\****************************************************************************/

/*
 * MPAL consists of two main files: MPAL.DLL and MPAL.H
 * The first is the DLL that contains the code to interface with MPAL
 * adventures, the second is the header that defines the prototypes
 * functions. MPAL is compiled for Win32, and it can therefore be used with
 * any compiler that supports Win32 DLL (Watcom C++, Visual C++,
 * Delphi, etc.), and therefore compatible with both Windows 95 and Windows NT.
 *
 * To use the DLL, and 'obviously need to create a library for symbols to export.
 *
 */

/****************************************************************************\
*       Custom Functions
\****************************************************************************/

/*
 * A custom function and a function specified by the program that uses the
 * library, to perform the particular code. The custom functions are
 * retrieved from the library as specified in the source MPAL, and in particular
 * in defining the behavior of an item with some action.
 *
 * To use the custom functions, you need to prepare an array of
 * pointers to functions (such as using the type casting LPCUSTOMFUNCTION,
 * (defined below), and pass it as second parameter to mpalInit (). Note you
 * must specify the size of the array, as elements of pointers and which do not
 * contain the same: the library will call it only those functions specified in
 * the source MPAL. It can be useful, for debugging reasons, do not bet
 * the shares of arrays used to debugging function, to avoid unpleasant crash,
 * if it has been made an error in source and / or some oversight in the code.
 *
 */

#ifndef TONY_MPAL_H
#define TONY_MPAL_H

#include "common/scummsys.h"
#include "common/coroutines.h"
#include "common/rect.h"
#include "common/str.h"
#include "tony/mpal/memory.h"

namespace Tony {

namespace MPAL {

/****************************************************************************\
*       Macro definitions and structures
\****************************************************************************/

// OK value for the error codes
#define OK 0

#define MAXFRAMES 400 // frame animation of an object
#define MAXPATTERN 40 // pattern of animation of an object
#define MAXPOLLINGLOCATIONS 64

#define GETARG(type)   va_arg(v, type)

/**
 * Macro for use with queries that may refer to X and Y co-ordinates
 */
enum QueryCoordinates {
	MPQ_X,
	MPQ_Y
};

/**
 * Query can be used with mpalQuery methods. In practice corresponds all claims
 * that can do at the library
 */
enum QueryTypes {
	// General Query
	MPQ_VERSION = 10,

	MPQ_GLOBAL_VAR = 50,
	MPQ_RESOURCE,
	MPQ_MESSAGE,

	// Query on leases
	MPQ_LOCATION_IMAGE = 100,
	MPQ_LOCATION_SIZE,

	// Queries about items
	MPQ_ITEM_LIST = 200,
	MPQ_ITEM_DATA,
	MPQ_ITEM_PATTERN,
	MPQ_ITEM_NAME,
	MPQ_ITEM_IS_ACTIVE,

	// Query dialog
	MPQ_DIALOG_PERIOD = 300,
	MPQ_DIALOG_WAITFORCHOICE,
	MPQ_DIALOG_SELECTLIST,
	MPQ_DIALOG_SELECTION,

	// Query execution
	MPQ_DO_ACTION = 400,
	MPQ_DO_DIALOG
};

/**
 * Framework to manage the animation of an item
 */
typedef struct {
	char *_frames[MAXFRAMES];
	Common::Rect _frameslocations[MAXFRAMES];
	Common::Rect _bbox[MAXFRAMES];
	short _pattern[MAXPATTERN][MAXFRAMES];
	short _speed;
	char _numframe;
	char _numpattern;
	char _curframe;
	char _curpattern;
	short _destX, _destY;
	signed char _destZ;
	short _objectID;
} Item;
typedef Item *LpItem;

/**
 * Define a custom function, to use the language MPAL to perform various controls as a result of an action
 */
typedef void (*LPCUSTOMFUNCTION)(CORO_PARAM, uint32, uint32, uint32, uint32);
typedef LPCUSTOMFUNCTION *LPLPCUSTOMFUNCTION;

/**
 *
 * Define an IRQ of an item that is called when the  pattern changes or the status of an item
 */
typedef void (*LPITEMIRQFUNCTION)(uint32, int, int);
typedef LPITEMIRQFUNCTION* LPLPITEMIRQFUNCTION;

/**
 * @defgroup Macrofunctions query
 *
 * The following are defines used for simplifying calling the mpalQuery variants
 */
//@{

/**
 * Gets the current version of MPAL
 *
 * @returns	Version number (0x1232 = 1.2.3b)
 */
#define mpalQueryVersion()                              \
		(uint16)mpalQueryDWORD(MPQ_VERSION)

/**
 * Gets the numerical value of a global variable
 *
 * @param lpszVarName		Variable name (ASCIIZ)
 * @returns		Global variable value
 * @remarks		This query was implemented for debugging. The program,
 *				if well designed, should not need to access variables from
 *				within the library.
 */
#define mpalQueryGlobalVar(lpszVarName)                 \
		mpalQueryDWORD(MPQ_GLOBAL_VAR, (const char *)(lpszVarName))

/**
 * Provides access to a resource inside the .MPC file
 *
 * @param dwResId			Resource Id
 * @returns		Handle to a memory area containing the resource,  ready for use.
 */
#define mpalQueryResource(dwResId)                      \
		mpalQueryHANDLE(MPQ_RESOURCE, (uint32)(dwResId))

/**
 * Returns a message.
 *
 * @param nMsg				Message number
 * @returns		ASCIIZ message
 * @remarks		The returned pointer must be freed via the memory manager
*				after use. The message will be in ASCIIZ format.
*/
#define mpalQueryMessage(nMsg)                          \
		(char *)mpalQueryHANDLE(MPQ_MESSAGE, (uint32)(nMsg))

/**
 * Provides a location image
 * @return		Returns a picture handle
 */
#define mpalQueryLocationImage(nLoc)                    \
		mpalQueryHANDLE(MPQ_LOCATION_IMAGE, (uint32)(nLoc))

/**
 * Request the x or y size of a location in pixels
 *
 * @param nLoc				Location number
 * @param dwCoord           MPQ_X or MPQ_Y coordinate to retrieve
 * @returns		Size
 */
#define mpalQueryLocationSize(nLoc, dwCoord)             \
		mpalQueryDWORD(MPQ_LOCATION_SIZE, (uint32)(nLoc), (uint32)(dwCoord))

/**
 * Provides the list of objects in a location.
 *
 * @param nLoc              Location number
 * @returns					List of objects (accessible by Item [0], Item [1], etc.)
 */
// TODO: Determine if this is endian safe
#define mpalQueryItemList(nLoc)                         \
		(uint32 *)mpalQueryHANDLE(MPQ_ITEM_LIST, (uint32)(nLoc))

/**
 * Provides information on an item
 *
 * @param nItem             Item number
 * @returns		Structure filled with requested information
 */
#define mpalQueryItemData(nItem)                          \
		(LpItem)mpalQueryHANDLE(MPQ_ITEM_DATA, (uint32)(nItem))

/**
 * Provides the current pattern of an item
 *
 * @param nItem             Item number
 * @returns		Number of animation patterns to be executed.
 * @remarks		By default, the pattern of 0 indicates that we should do nothing.
 */
#define mpalQueryItemPattern(nItem)                  \
		mpalQueryDWORD(MPQ_ITEM_PATTERN, (uint32)(nItem))

/**
 * Returns true if an item is active
 *
 * @param nItem             Item number
 * @returns		TRUE if the item is active, FALSE otherwise
 */
#define mpalQueryItemIsActive(nItem)                  \
		(bool)mpalQueryDWORD(MPQ_ITEM_IS_ACTIVE, (uint32)(nItem))

/**
 * Returns the name of an item
 *
 * @param nItem             Item number
 * @param lpszName          Pointer to a buffer of at least 33 bytes
 *                          that will be filled with the name
 * @remarks		If the item is not active (ie. if its status or number
 *				is less than or equal to 0), the string will be empty.
 */
#define mpalQueryItemName(nItem, lpszName)             \
		mpalQueryHANDLE(MPQ_ITEM_NAME, (uint32)(nItem), (char *)(lpszName))

/**
 * Returns a sentence of dialog.
 *
 * @param nDialog           Dialog number
 * @param nPeriod           Number of words
 * @returns		A pointer to the string of words, or NULL on failure.
 * @remarks		The string must be freed after use using the memory manager.
 * Unlike normal messages, the sentences of dialogue are formed by a single
 * string terminated with 0.
 */
#define mpalQueryDialogPeriod(nPeriod)                  \
		(char *)mpalQueryHANDLE(MPQ_DIALOG_PERIOD, (uint32)(nPeriod))

/**
 * Wait until the moment in which the need is signaled to make a choice by the user.
 * @returns		Number of choice to be made, or -1 if the dialogue is finished.
 */
#define mpalQueryDialogWaitForChoice(dwRet)                  \
		CORO_INVOKE_2(mpalQueryCORO, MPQ_DIALOG_WAITFORCHOICE, dwRet)

/**
 * Requires a list of various options for some choice within the current dialog.
 *
 * @param nChoice			Choice number
 * @returns		A pointer to an array containing the data matched to each option.
 * @remarks		The figure 'a uint32 specified in the source to which MPAL
 * You can assign meaning that the more' suits.
 * The pointer msut be freed after use using the memory memory.
 */
#define mpalQueryDialogSelectList(nChoice)              \
		(uint32 *)mpalQueryHANDLE(MPQ_DIALOG_SELECTLIST, (uint32)(nChoice))

/**
 * Warns the library that the user has selected, in a certain choice of the current dialog,
 * corresponding option at a certain given.
 *
 * @param nChoice			Choice number of the choice that was in progress
 * @param dwData			Option that was selected by the user.
 * @returns		TRUE if all OK, FALSE on failure.
 * @remarks		After execution of this query, MPAL continue
 * Groups according to the execution of the dialogue. And necessary so the game
 * remains on hold again for another chosen by mpalQueryDialogWaitForChoice ().
 */
#define mpalQueryDialogSelection(nChoice, dwData)        \
		(bool)mpalQueryDWORD(MPQ_DIALOG_SELECTION, (uint32)(nChoice), (uint32)(dwData))

#define mpalQueryDialogSelectionDWORD(nChoice, dwData)        \
		mpalQueryDWORD(MPQ_DIALOG_SELECTION, (uint32)(nChoice), (uint32)(dwData))

/**
 * Warns the library an action was performed on a Object.
 * The library will call custom functions, if necessary.
 *
 * @param nAction			Action number
 * @param nItem				Item number
 * @param dwParam			Action parameter
 * @returns		Handle to the thread that is performing the action, or CORO_INVALID_PID_VALUE
 * if the action is not defined for the item, or the item is inactive.
 * @remarks		The parameter is used primarily to implement actions
 * as "U.S." involving two objects together. The action will be executed only
 * if the item is active, ie if its status is a positive number greater than 0.
 */
#define mpalQueryDoAction(nAction, nItem, dwParam)      \
		mpalQueryDWORD(MPQ_DO_ACTION, (uint32)(nAction), (uint32)(nItem), (uint32)(dwParam))

/**
 * Warns the library a dialogue was required.
 *
 * @param nDialog			Dialog number
 * @param nGroup			Group number to use
 * @returns		Handle to the thread that is running the box, or
 * CORO_INVALID_PID_VALUE if the dialogue does not exist.
 */
#define mpalQueryDoDialog(nDialog, nGroup)               \
		mpalQueryDWORD(MPQ_DO_DIALOG, (uint32)(nDialog), (uint32)(nGroup))

/**
 * @defgroup Functions exported to the main game
 */
//@{

/**
 * Initializes the MPAL library, and opens an .MPC file, which will be 'used for all queries
 * @param lpszMpcFileName	Name of the .MPC file, including extension
 * @param lpszMprFileName	Name of the .MPR file, including extension
 * @param lplpcfArray		Array of pointers to custom functions
 * @returns		TRUE if all OK, FALSE on failure
 */
bool mpalInit(const char *lpszFileName, const char *lpszMprFileName,
			  LPLPCUSTOMFUNCTION lplpcfArray, Common::String *lpcfStrings);

/**
 * Frees resources allocated by the MPAL subsystem
 */
void mpalFree();

/**
 * This is a general function to communicate with the library, to request information
 * about what is in the .MPC file
 *
 * @param wQueryType		Type of query. The list is in the QueryTypes enum.
 * @returns		4 bytes depending on the type of query
 * @remarks		This is the specialized version of the original single mpalQuery
 * method that returns numeric results.
 */
uint32 mpalQueryDWORD(uint wQueryType, ...);

/**
 * This is a general function to communicate with the library, to request information
 * about what is in the .MPC file
 *
 * @param wQueryType		Type of query. The list is in the QueryTypes enum.
 * @returns		4 bytes depending on the type of query
 * @remarks		This is the specialized version of the original single mpalQuery
 * method that returns a pointer or handle.
 */
MpalHandle mpalQueryHANDLE(uint wQueryType, ...);

/**
 * This is a general function to communicate with the library, to request information
 * about what is in the .MPC file
 *
 * @param wQueryType		Type of query. The list is in the QueryTypes enum.
 * @returns		4 bytes depending on the type of query
 * @remarks		This is the specialized version of the original single mpalQuery
 * method that needs to run within a co-routine context.
 */
void mpalQueryCORO(CORO_PARAM, uint16 wQueryType, uint32 *dwRet);

/**
 * Execute a script. The script runs on multitasking by a thread.
 *
 * @param nScript			Script number to run
 * @returns		TRUE if the script 'was launched, FALSE on failure
 */
bool mpalExecuteScript(int nScript);

/**
 * Returns the current MPAL error code
 *
 * @returns		Error code
 */
uint32 mpalGetError();

/**
 * Install a custom routine That will be called by MPAL every time the pattern
 * of an item has been changed.
 *
 * @param lpiifCustom		Custom function to install
 */
void mpalInstallItemIrq(LPITEMIRQFUNCTION lpiifCustom);

/**
 * Process the idle actions of the items on one location.
 *
 * @param nLoc				Number of the location whose items must be processed
 * for idle actions.
 * @returns		TRUE if all OK, and FALSE if it exceeded the maximum limit.
 * @remarks		The maximum number of locations that can be polled
 * simultaneously is defined defined by MAXPOLLINGFUNCIONS
 */
bool mpalStartIdlePoll(int nLoc);

/**
 * Stop processing the idle actions of the items on one location.
 *
 * @param nLo				Number of the location
 * @returns		TRUE if all OK, FALSE if the specified location was not
 * in the process of polling
 */
void mpalEndIdlePoll(CORO_PARAM, int nLoc, bool *result);

/**
 * Load a save state from a buffer.
 *
 * @param buf				Buffer where to store the state
 * @returns		Length of the state buffer in bytes
 */
int mpalLoadState(byte *buf);

/**
 * Store the save state into a buffer. The buffer must be
 * length at least the size specified with mpalGetSaveStateSize
 *
 * @param buf				Buffer where to store the state
 */
void mpalSaveState(byte *buf);

/**
 * Retrieve the length of a save state
 *
 * @returns		Length in bytes
 */
int mpalGetSaveStateSize();

/**
 * Locks the variables for access
 */
void lockVar();

/**
 * Unlocks variables after use
 */
void unlockVar();

} // end of namespace MPAL

} // end of namespace Tony

#endif

