/* 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/>.
 *
 *
 * Based on the original sources
 *   Faery Tale II -- The Halls of the Dead
 *   (c) 1993-1996 The Wyrmkeep Entertainment Co.
 */

#ifndef SAGA2_TARGET_H
#define SAGA2_TARGET_H

namespace Saga2 {

struct StandingTileInfo;

const   int     kMaxObjDist = kPlatformWidth * kTileUVSize * 8;
const   int     kMaxTileDist = kPlatformWidth * kTileUVSize * 2;
const   int     kMaxMetaDist = kPlatformWidth * kTileUVSize * 8;

enum TargetType {
	kLocationTarget,
	kSpecificTileTarget,
	kTilePropertyTarget,
	kSpecificMetaTileTarget,
	kMetaTilePropertyTarget,
	kSpecificObjectTarget,
	kObjectPropertyTarget,
	kSpecificActorTarget,
	kActorPropertyTarget
};

/* ===================================================================== *
   Misc. function prototypes
 * ===================================================================== */

class Target;

//  Deletes targets allocated on the heap using new
void deleteTarget(Target *t);

void readTarget(void *mem, Common::InSaveFile *in);
void writeTarget(const Target *t, Common::MemoryWriteStreamDynamic *out);
int32 targetArchiveSize(const Target *t);

/* ===================================================================== *
   TargetLocationArray structure
 * ===================================================================== */

//  This structure is used to query the target class for multiple target
//  locations
struct TargetLocationArray {
	const int16         size;   //  number of allocated elements in array
	int16               locs;   //  number of array elements with valid data

	//  These arrays must be allocated by calling function
	TilePoint *const   locArray;    //  pointer to location array
	int16 *const       distArray;   //  pointer to distance array

	//  Constructor
	TargetLocationArray(int16 s, TilePoint *arr, int16 *dArr) :
		size(s),
		locs(0),
		locArray(arr),
		distArray(dArr) {
	}
};

/* ===================================================================== *
   TargetObjectArray structure
 * ===================================================================== */

//  This structure is used to query the target class for multiple target
//  objects
struct TargetObjectArray {
	const int16         size;   //  number of allocated elements in array
	int16               objs;   //  number of array elements with valid data

	//  These arrays must be allocated by calling function
	GameObject **const objArray;    //  pointer to object pointer array
	int16 *const       distArray;   //  pointer to distance array

	//  Constructor
	TargetObjectArray(int16 s, GameObject **arr, int16 *dArr) :
		size(s),
		objs(0),
		objArray(arr),
		distArray(dArr) {
	}
};

/* ===================================================================== *
   TargetActorArray structure
 * ===================================================================== */

//  This structure is used to query the target class for multiple target
//  actors
struct TargetActorArray {
	const int16     size;       //  number of allocated elements in array
	int16           actors;     //  number of array elements with valid data

	//  These arrays must be allocated by calling function
	Actor **const  actorArray;  //  pointer to actor pointer array
	int16 *const   distArray;   //  pointer to distance array

	//  Constructor
	TargetActorArray(int16 s, Actor **arr, int16 *dArr) :
		size(s),
		actors(0),
		actorArray(arr),
		distArray(dArr) {
	}
};

/* ===================================================================== *
   Target class
 * ===================================================================== */

class Target {
public:
	//  virtual destructor
	virtual ~Target() {}

	//  Return the number of bytes needed to archive this object in
	//  a buffer
	virtual int32 archiveSize() const = 0;

	virtual void write(Common::MemoryWriteStreamDynamic *out) const = 0;

	//  Return an integer representing the type of target
	virtual int16 getType() const = 0;

	//  Virtual function returning the sizeof this target
	virtual size_t size() const = 0;

	//  Create a copy of this target at the specified address
	virtual void clone(void *mem) const = 0;

	//  Determine if the specified target is equivalent to this target
	virtual bool operator == (const Target &t) const = 0;
	bool operator != (const Target &t) const {
		return !operator == (t);
	}

	//  Overloaded memory management functions for constructing and
	//  destructing Targets in place.
	void *operator new (size_t, void *p) {
		return p;
	}

	virtual bool isObjectTarget() const;
	virtual bool isActorTarget() const;

	//  Return location of closest instance of target
	virtual TilePoint where(GameWorld *world, const TilePoint &tp) const = 0;
	//  Fill array with locations of closest instances of target
	virtual int16 where(
	    GameWorld *world,
	    const TilePoint &tp,
	    TargetLocationArray &tla) const = 0;
};

/* ===================================================================== *
   LocationTarget class
 * ===================================================================== */

class LocationTarget : public Target {
	TilePoint   _loc;

public:
	//  Constructor -- initial construction
	LocationTarget(const TilePoint &tp) : _loc(tp) {}

	LocationTarget(Common::SeekableReadStream *stream);

	//  Return the number of bytes needed to archive this object in
	//  a buffer
	int32 archiveSize() const;

	void write(Common::MemoryWriteStreamDynamic *out) const;

	//  Return an integer representing the type of target
	int16 getType() const;

	//  Virtual function returning the sizeof this target
	size_t size() const;

	//  Create a copy of this target at the specified address
	void clone(void *mem) const;

	//  Determine if the specified target is equivalent to this target
	bool operator == (const Target &t) const;

	//  Determine if the specified location target is equivalent to this
	//  location target
	bool operator == (const LocationTarget &lt) const {
		return _loc == lt._loc;
	}
	bool operator != (const LocationTarget &lt) const {
		return _loc != lt._loc;
	}

	TilePoint where(GameWorld *world, const TilePoint &tp) const;
	int16 where(
	    GameWorld *world,
	    const TilePoint &tp,
	    TargetLocationArray &tla) const;
};

/* ===================================================================== *
   TileTarget class
 * ===================================================================== */

class TileTarget : public Target {
public:
	virtual bool isTarget(StandingTileInfo &sti) const = 0;

	TilePoint where(GameWorld *world, const TilePoint &tp) const;
	int16 where(
	    GameWorld *world,
	    const TilePoint &tp,
	    TargetLocationArray &tla) const;
};

/* ===================================================================== *
   SpecificTileTarget class
 * ===================================================================== */

class SpecificTileTarget : public TileTarget {
	TileID  _tile;

public:
	//  Constructor -- initial construction
	SpecificTileTarget(TileID t) : _tile(t) {}

	SpecificTileTarget(Common::SeekableReadStream *stream);

	//  Return the number of bytes needed to archive this object in
	//  a buffer
	int32 archiveSize() const;

	void write(Common::MemoryWriteStreamDynamic *out) const;

	//  Return an integer representing the type of target
	int16 getType() const;

	//  Virtual function returning the sizeof this target
	size_t size() const;

	//  Create a copy of this target at the specified address
	void clone(void *mem) const;

	//  Determine if the specified target is equivalent to this target
	bool operator == (const Target &t) const;

	bool isTarget(StandingTileInfo &sti) const;
};

/* ===================================================================== *
   TilePropertyTarget class
 * ===================================================================== */

class TilePropertyTarget : public TileTarget {
	TilePropertyID  _tileProp;

public:
	//  Constructor -- initial construction
	TilePropertyTarget(TilePropertyID tProp) : _tileProp(tProp) {}

	TilePropertyTarget(Common::SeekableReadStream *stream);

	//  Return the number of bytes needed to archive this object in
	//  a buffer
	int32 archiveSize() const;

	void write(Common::MemoryWriteStreamDynamic *out) const;

	//  Return an integer representing the type of target
	int16 getType() const;

	//  Virtual function returning the sizeof this target
	size_t size() const;

	//  Create a copy of this target at the specified address
	void clone(void *mem) const;

	//  Determine if the specified target is equivalent to this target
	bool operator == (const Target &t) const;

	bool isTarget(StandingTileInfo &sti) const;
};

/* ===================================================================== *
   MetaTileTarget class
 * ===================================================================== */

class MetaTileTarget : public Target {
public:
	virtual bool isTarget(
	    MetaTile *mt,
	    int16 mapNum,
	    const TilePoint &tp) const = 0;

	TilePoint where(GameWorld *world, const TilePoint &tp) const;
	int16 where(
	    GameWorld *world,
	    const TilePoint &tp,
	    TargetLocationArray &tla) const;
};

/* ===================================================================== *
   SpecificMetaTileTarget class
 * ===================================================================== */

class SpecificMetaTileTarget : public MetaTileTarget {
	MetaTileID  _meta;

public:
	//  Constructor -- initial construction
	SpecificMetaTileTarget(MetaTileID mt) : _meta(mt) {}

	SpecificMetaTileTarget(Common::SeekableReadStream *stream);

	//  Return the number of bytes needed to archive this object in
	//  a buffer
	int32 archiveSize() const;

	void write(Common::MemoryWriteStreamDynamic *out) const;

	//  Return an integer representing the type of target
	int16 getType() const;

	//  Virtual function returning the sizeof this target
	size_t size() const;

	//  Create a copy of this target at the specified address
	void clone(void *mem) const;

	//  Determine if the specified target is equivalent to this target
	bool operator == (const Target &t) const;

	bool isTarget(MetaTile *mt, int16 mapNum, const TilePoint &tp) const;
};

/* ===================================================================== *
   MetaTilePropertyTarget class
 * ===================================================================== */

class MetaTilePropertyTarget : public MetaTileTarget {
	MetaTilePropertyID  _metaProp;

public:
	//  Constructor -- initial construction
	MetaTilePropertyTarget(MetaTilePropertyID mtProp) :
		_metaProp(mtProp) {
	}

	MetaTilePropertyTarget(Common::SeekableReadStream *stream);

	//  Return the number of bytes needed to archive this object in
	//  a buffer
	int32 archiveSize() const;

	void write(Common::MemoryWriteStreamDynamic *out) const;

	//  Return an integer representing the type of target
	int16 getType() const;

	//  Virtual function returning the sizeof this target
	size_t size() const;

	//  Create a copy of this target at the specified address
	void clone(void *mem) const;

	//  Determine if the specified target is equivalent to this target
	bool operator == (const Target &t) const;

	bool isTarget(MetaTile *mt, int16 mapNum, const TilePoint &tp) const;
};

/* ===================================================================== *
   ObjectTarget class
 * ===================================================================== */

class ObjectTarget : public Target {

	//  These are recursive functions used to fill a target array
	//  by inspecting an object and all of the objects contained in
	//  that object
	void searchObject(
	    GameObject *obj,
	    int16 dist,
	    TargetObjectArray &toa) const;
	void searchObject(
	    GameObject *obj,
	    const TilePoint &tp,
	    int16 dist,
	    TargetLocationArray &tla) const;

public:
	bool isObjectTarget() const;

	TilePoint where(GameWorld *world, const TilePoint &tp) const;
	int16 where(
	    GameWorld *world,
	    const TilePoint &tp,
	    TargetLocationArray &tla) const;

	//  Determine if the specified object meets the target criterion
	virtual bool isTarget(GameObject *testObj) const = 0;

	//  Return closest instance of target object
	virtual GameObject *object(GameWorld *world, const TilePoint &tp) const;
	//  Fill array with closest instances of target object
	virtual int16 object(
	    GameWorld *world,
	    const TilePoint &tp,
	    TargetObjectArray &toa) const;
};

/* ===================================================================== *
   SpecificObjectTarget class
 * ===================================================================== */

class SpecificObjectTarget : public ObjectTarget {
	ObjectID    _obj;

public:
	//  Constructors -- initial construction
	SpecificObjectTarget(ObjectID id) :
		_obj(id) {
		assert(isObject(_obj));
	}
	SpecificObjectTarget(GameObject *ptr) : _obj((assert(isObject(ptr)), ptr->thisID())) {}

	SpecificObjectTarget(Common::SeekableReadStream *stream);

	//  Return the number of bytes needed to archive this object in
	//  a buffer
	int32 archiveSize() const;

	void write(Common::MemoryWriteStreamDynamic *out) const;

	//  Return an integer representing the type of target
	int16 getType() const;

	//  Virtual function returning the sizeof this target
	size_t size() const;

	//  Create a copy of this target at the specified address
	void clone(void *mem) const;

	//  Determine if the specified target is equivalent to this target
	bool operator == (const Target &t) const;

	bool isTarget(GameObject *testObj) const;

	TilePoint where(GameWorld *world, const TilePoint &tp) const;
	int16 where(
	    GameWorld *world,
	    const TilePoint &tp,
	    TargetLocationArray &tla) const;

	GameObject *object(GameWorld *world, const TilePoint &tp) const;
	int16 object(
	    GameWorld *world,
	    const TilePoint &tp,
	    TargetObjectArray &toa) const;

	//  Return a pointer to the target object, unconditionally
	GameObject *getTargetObject() const {
		return GameObject::objectAddress(_obj);
	}
};

/* ===================================================================== *
   ObjectPropertTarget class
 * ===================================================================== */

class ObjectPropertyTarget : public ObjectTarget {
	ObjectPropertyID    _objProp;

public:
	//  Constructor -- initial construction
	ObjectPropertyTarget(ObjectPropertyID prop) : _objProp(prop) {}

	ObjectPropertyTarget(Common::SeekableReadStream *stream);

	//  Return the number of bytes needed to archive this object in
	//  a buffer
	int32 archiveSize() const;

	void write(Common::MemoryWriteStreamDynamic *out) const;

	//  Return an integer representing the type of target
	int16 getType() const;

	//  Virtual function returning the sizeof this target
	size_t size() const;

	//  Create a copy of this target at the specified address
	void clone(void *mem) const;

	//  Determine if the specified target is equivalent to this target
	bool operator == (const Target &t) const;

	bool isTarget(GameObject *testObj) const;
};

/* ===================================================================== *
   ActorTarget class
 * ===================================================================== */

class ActorTarget : public ObjectTarget {

public:
	bool isActorTarget() const;

	bool isTarget(GameObject *testObj) const;
	virtual bool isTarget(Actor *testActor) const = 0;

	virtual Actor *actor(GameWorld *world, const TilePoint &tp) const;
	virtual int16 actor(
	    GameWorld *world,
	    const TilePoint &tp,
	    TargetActorArray &taa) const;
};

/* ===================================================================== *
   SpecificActorTarget class
 * ===================================================================== */

class SpecificActorTarget : public ActorTarget {
	Actor   *_a;

public:
	//  Constructor -- initial construction
	SpecificActorTarget(Actor *actor_) : _a(actor_) {}

	SpecificActorTarget(Common::SeekableReadStream *stream);

	//  Return the number of bytes needed to archive this object in
	//  a buffer
	int32 archiveSize() const;

	void write(Common::MemoryWriteStreamDynamic *out) const;

	//  Return an integer representing the type of target
	int16 getType() const;

	//  Virtual function returning the sizeof this target
	size_t size() const;

	//  Create a copy of this target at the specified address
	void clone(void *mem) const;

	//  Determine if the specified target is equivalent to this target
	bool operator == (const Target &t) const;

	bool isTarget(Actor *testActor) const;

	TilePoint where(GameWorld *world, const TilePoint &tp) const;
	int16 where(
	    GameWorld *world,
	    const TilePoint &tp,
	    TargetLocationArray &tla) const;

	GameObject *object(GameWorld *world, const TilePoint &tp) const;
	int16 object(
	    GameWorld *world,
	    const TilePoint &tp,
	    TargetObjectArray &toa) const;

	Actor *actor(GameWorld *world, const TilePoint &tp) const;
	int16 actor(
	    GameWorld *world,
	    const TilePoint &tp,
	    TargetActorArray &taa) const;

	//  Return a pointer to the target actor, unconditionally
	Actor *getTargetActor() const {
		return _a;
	}
};

/* ===================================================================== *
   ActorPropertyTarget class
 * ===================================================================== */

class ActorPropertyTarget : public ActorTarget {
	ActorPropertyID     _actorProp;

public:
	//  Constructor -- initial construction
	ActorPropertyTarget(ActorPropertyID aProp) : _actorProp(aProp) {}

	ActorPropertyTarget(Common::SeekableReadStream *stream);

	//  Return the number of bytes needed to archive this object in
	//  a buffer
	int32 archiveSize() const;

	void write(Common::MemoryWriteStreamDynamic *out) const;

	//  Return an integer representing the type of target
	int16 getType() const;

	//  Virtual function returning the sizeof this target
	size_t size() const;

	//  Create a copy of this target at the specified address
	void clone(void *mem) const;

	//  Determine if the specified target is equivalent to this target
	bool operator == (const Target &t) const;

	bool isTarget(Actor *testActor) const;
};

/* ===================================================================== *
   Constants
 * ===================================================================== */

//	This const and typedef are used to define an area of memory large enough
//	to contain any target.  The sizeof( LocationTarget ) is used because
//	the LocationTarget takes the most memory.

const size_t    kTargetBytes = sizeof(LocationTarget);

typedef uint8 TargetPlaceHolder[kTargetBytes];

} // end of namespace Saga2

#endif
