////////////////////////////////////////////////////////////////////////////////
// Vehicle.h
// Base class for vehicles
// Copyright Bjoern Ganster
// Developed since 2007
////////////////////////////////////////////////////////////////////////////////

#ifndef Vehicle__h
#define Vehicle__h

class AbstractVehicleAI;

#include "GameObject.h"
#include "Mesh.h"
#include "MWBase.h"
#include "XMLTree.h"

////////////////////////////////////////////////////////////////////////////////

ENUM_CLASS CombatVehicleTypes {
	wheeled,
	tracked,
	hover,
	hydrofoil,
	displacementHull,
	submarine,
	vtol,
	turretVehicle
};

////////////////////////////////////////////////////////////////////////////////

enum VehicleStatus {
	shutDown,
	engineRunning,
	destroyed
};

////////////////////////////////////////////////////////////////////////////////

const int MaxGroupCount = 5;

struct MountedWeapon {
	WeaponType weaponType;

	// Timestamp when this weapon was last fired
	double lastFired;

	// The section where the weapon is mounted
	int section;

	// Where the weapon is located in the section
	std::vector<int> criticals;

	// Store if a weapon is in a certain group
	int group;

	// Position of the weapon, relative to the limb it is mounted in
	Point3D position;

	// Track how many shells have been fired already in the current shot
	int shellsFiredInCurrentShot;

	// The number of critical hits that this weapon has received
	bool destroyed;
};

struct MountedAmmo {
	// Weapon prototype
	WeaponType weaponType;

	// Where the ammo is located
	int section;
	int critical;

	unsigned int remainingShots;
};

struct CriticalLayout {
	// Coordinates of first critical
	Point3D start;

	// Direction for additional weapons
	Point3D direction;
};

////////////////////////////////////////////////////////////////////////////////

struct DisplayComponentCoords {
	double u1, v1, u2, v2;

	DisplayComponentCoords(double aU1 = 0.0, double aV1 = 0.0, double aU2 = 0.0, double aV2 = 0.0)
	: u1(aU1),
	  v1(aV1),
	  u2(aU2),
	  v2(aV2)
	{
	}
};

////////////////////////////////////////////////////////////////////////////////

struct VehicleSection {
	BGCPString sectionName;
	std::vector<WeaponType> criticals;
};

////////////////////////////////////////////////////////////////////////////////
// Vehicle base class

class Vehicle: public GameObject
{
public:
	Point3D cockpitCoords;
	int mActiveWeapon;
	DisplayComponentCoords heatDisplayCoords, jumpJetDisplayCoords, speedDisplayCoords, 
		weaponDisplayCoords, armorDisplayCoords, mapDisplayCoords, altimeterDisplayCoords,
		artificialHorizonDisplayCoords;
	BGCPString CFGFN, MVMKFN, textureFN, typeName;
	Point3D p_front, p_left, p_up;
	const char* mArmorDiagramSubtitle;

	// In-cockpit viewpanels that allow additional views of outside ...
	std::vector<double> mViewpanels;

	Point3D mJumpJetsActive;

	// Current vehicle movement
	Point3D mMovement, mAcceleration;

	double m_turnDelta, m_speedDelta;

	// Direction the unit is facing in degrees; important during spawning and for planning movemement
	double mHeading;

	// Can the vehicle be salvaged?
	bool mSalvage;

	// Remaining fule for the jump jets
	double mJumpJetFuel;

	// A flag that controls whether weapons are placed in lower arms or normal arms for a biped Mech
	bool preferLowerArms;

	// Maximum speed of the vehicle
	double mMetersPerSecond;

	// How far the vehicle should stay lifted above ground
	double mLift;

	// Constructor
	Vehicle(const char* className, BGClassID classID, RenderableObject* parent, int limbCount, const char* armorDiagramSubtitle);

	// Destructor
	~Vehicle() OVERRIDE;

	// Add geometry for a vehicle part
	void addGeometry(int geometryType, Mesh* geometry, const Matrix& m, Texture* texture);

	// Get section and critical information
	virtual int getSectionCount() const;
	virtual BGCPString getSectionName(int i) const;
	virtual int getCriticalCount(int section) const;
	virtual WeaponType getCriticals(int section, int criticalSlot) const;
	virtual void setCriticals(int section, int slot, WeaponType weaponType);

	// Query/set integer data
	static const int Info_Tonnage = 0;
	static const int Info_EngineRating = 1;
	static const int Info_EngineType = 2;
	static const int Info_JumpJets = 3;
	static const int Info_Heat_Sinks = 6;
	static const int Info_Heat_Sink_Type = 7;
	static const int Info_Internal_Structure_Type = 8;
	static const int Info_Armor_Type = 9;
	static const int Info_TechType = 10;
	virtual int getInteger(int info) const;
	virtual bool setInteger(int info, int newVal);

	// Query double data
	static const int Info_WalkingHexes = 4;
	static const int Info_RunningHexes = 5;
	virtual double getDouble(int info) const;

	static const int Armor_Front = 0;
	static const int Armor_Rear = 1;
	virtual DWORD getArmor(int section, int ArmorType) const;
	virtual bool setArmor(int section, DWORD newVal, int ArmorType);
	virtual void setInternalStructure(int section, DWORD newVal);
	virtual DWORD getInternalStructure(int section) const;
	virtual double getWeight() const;

	virtual double calcInternalStructureWeight() const;

	double calcArmorWeight() const;
	virtual int countArmorPoints() const;

	// Count number of criticals belonging to a weapon type in a section
	virtual int countWeaponCriticals(int currSection, WeaponType weaponNo) const;
	int countWeaponInAllSections(WeaponType weaponNo) const;

	// Add weapon
	virtual bool addWeapon(int currSection, WeaponType weaponNo);

	// Remove weapon - returns number of criticals removed
	virtual int removeWeapon(int currSection, int critNo, WeaponType weaponNo,
		int CritCount = -1);

	// Remove all equipment criticals of given type
	int removeAll(WeaponType eqptType);

	int autoAddCriticals(WeaponType weaponType, int count, int /*critsPerUnit*/);

	// Check for items that the user may not add or remove
	virtual bool userMayAddDirectly(int section, WeaponType weaponNo);
	virtual bool userMayRemoveDirectly(WeaponType weaponNo);

	// Load entire mech
	virtual void readSpecialAttributesFromRoot(XMLTreeConstIterator& iter);
	virtual void readSpecialAttributes(XMLTreeConstIterator& iter, const std::vector<RenderableObject*>& geometries, const PathChar* mechDir);
	virtual bool loadFromXml(XMLTreeConstIterator& iter, const PathChar* path);
	virtual bool loadConfig(const PathChar* cfgFN);

	bool loadFromMW2MEK(const MemoryBlock* mb);

	// Save Mech config to MEK file
	void saveToMW2MEK(const PathChar* mekFN);

	// Check - and try to correct - config problems 
	// Returns true on success, false otherwise, and sets an error description on failure
	virtual bool check(BGCPString& errors);

	virtual double calcRealWeight();

	// Set current animation matrices
	virtual void setMatrices(double speed, DWORD missionTime);

	// Get/set Mech comment
	const BGCPString& getComment() const;
	void setComment(const BGCPString& newComment);

	// Access weapon list
	size_t getWeaponListEntryCount() const;
	const char* getWeaponListEntryName(size_t entry) const;
	void setGroup(int entry, int group);
	int getGroup(int entry) const;

	// Set direction where the vehicle is aiming, by moving the turret (vehicles) or twisting and tilting the torso (mechs)
	virtual void setAim(double turn, double tilt);

	// Get tilt and turn for current aim direction
	virtual double getAimTilt() const;
	virtual double getAimTurn() const;

	WeaponType getHeatSinkType() const;

	void setHeatSinkType(WeaponType newType);

	WeaponType getInternalStructureType() const;

	void setInternalStructureType(WeaponType newType);

	void setActiveWeaponGroup(int groupNo);

	void fireWeapons(bool groupFire, DWORD now);
	void cycleWeapon(bool groupFire);
	void fire(MountedWeapon& weapon, DWORD now);
	const MWEquipment* getBaseWeapon(size_t i) const;
	WeaponType getBaseWeaponType(size_t i) const;
	double getLastFireTime(size_t i) const;

	// Fill ammo table (call once)
	void fillAmmoTable();

	// Count how much ammo is left for a shot of weaponType
	unsigned int ammoAvailable(WeaponType weaponType) const;

	// Call after a shot to mark ammo as used
	void expendAmmo(WeaponType weaponType, unsigned int ammoNeeded);

	int getEffectiveHeatSinks() const;

	// Add geometry for the loadout of a specific mech configuration
	virtual void updateLoadoutGeometry();

	// Query mounted weapons
	size_t getMountedWeaponCount() const;
	MountedWeapon* getMountedWeapon(size_t num);
	const MountedWeapon* getMountedWeapon(size_t num) const;
	MountedWeapon* getMountedWeapon(int section, int critical);
	const MountedWeapon* getMountedWeapon(int section, int critical) const;
	const MountedWeapon* getActiveWeapon() const;

	// Get fire arc for section
	virtual Point3D getFireArc(int section) const;

	// Return the number of the part corresponding to a section
	// used by updateLoadoutGeometry() to find the mesh corresponding to the mech section
	virtual int section2part(int section, bool allowLowerArms);

	// Return the section corresponding to a limb
	virtual int limb2section(int limb);

	// Get matrix used for stuff mounted on a vehicle part (or weapons fire)
	bool getPartMatrix(int partNo, Matrix& m);

	bool useCockpitDisplays();

	Texture* getTexture();

	bool loadTexture(const PathChar* textureFN);

	// Process weapon fire: render lasers, process laser damage, emit bullets and missiles
	virtual void processWeaponFire(DWORD now, RenderableObject* scene, double delta);

	// Access vehicle status
	VehicleStatus getStatus() const;
	virtual void setStatus(VehicleStatus newStatus);

	// Get vehicle AI
	AbstractVehicleAI* getAI();

	// Based on the vehicle type, connect the vehicle to a suitable AI
	virtual AbstractVehicleAI* createAI(bool isPlayer, int environment) = 0;

	const Point3D& getJumpJetActivity() const 
	{
		return mJumpJetActivity;
	}
	void setJumpJets(double x, double y, double z)
	{
		mJumpJetActivity = Point3D(x, y, z);
	}

	// Check if the given weapon is ready to fire
	bool canFireWeapon(int weapon, DWORD now);

	// Get number of engine hits
	int getEngineHits() const;

	// Mobility may be reduced by damage, it starts at 1.0
	double getMobility() const;

	// Get maximum speed in meters per second currently supported by the vehicle
	virtual double getMaxSpeed() const;

	// Function to notify the GameObject that it is taking damage
	void receiveDamage(RenderableObject* limb, double damage, GameObject* sender, const Point3D& pointOfImpact) OVERRIDE;

	// Check if this vehicle differentiates between front and armor for a section
	virtual bool separateFrontAndRearArmor(int section) const;

	// Query damage to armor and internal structure
	virtual double getArmorDamage(int section, int type) const;
	virtual double getInternalDamage(int section) const;

	// Check if the critical in section is damaged
	virtual bool criticalDamaged(int section, int critical) const;

	// Show or hide the vehicle's windshiled (cockpit view may require it to be hidden)
	virtual void windshieldVisible(bool newVal) = 0;

	// Move aim towards given angles by at most delta
	virtual void adjustAim(double desiredTorsoTwist, double desiredTorsoTilt, double delta);

	// Get max armor allowed for a section
	virtual int getMaxArmor(int section);

	// Get mesh used for the cockpit
	virtual Mesh* getCockpitMesh();

	// Check if the vehicle's cockpit has a reticle that moves horizontally or vertically
	virtual bool reticleMovesHorizontally() const;
	virtual bool reticleMovesVertically() const;

	// Is the game object destroyed?
	bool isDestroyed() const OVERRIDE;

	// Check for a collision with a polygon and handle it
	virtual void checkCollision(const MeshPolygon* poly, Point3D& movement) = 0;

	// Assuming that groundPoly intersects with the Mech/vehicle, calculate how far the Mech/vehicle should be moved up to resolve the collision
	double calcMoveUp(const MeshPolygon* groundPoly, const Point3D& movedP) const;

	// Check if the vehicle has ground contact
	bool hasGroundContact() const;

	// Set/clear flag indicating whether the vehicle has ground contact
	void setHasGroundContact(bool newVal);

	// How many degrees does the vehicle turn per second?
	double getMaxTurnPerSecond() const;

	void nextWeapon();

	// Access current vehicle speed
	void setSpeed(double newVal);
	double getSpeed() const;

	// After loading a config, use this function to set min/max speeds using vehicle-type-specific rules
	virtual void setMinMaxSpeed();

	// Calculate heat for next frame
	virtual void calcNextFrameHeat(DWORD now, double deltaT) = 0;

	// Get current heat value for vehicle
	virtual double getHeat() const NOEXCEPT = 0;

	// Calculate movement for next frame
	virtual void calcNextFrameMovement(double deltaT, RenderableObject* scene, DWORD now, double gravity) = 0;

	void addMovement(const Point3D& newVal);

	// Check how many jump jets are active
	virtual int getActiveJumpJets() const;

	// Deactivate jump jets, e.g. if fuel is used up
	virtual void deactivateJumpJets();

	// Get speed percent - used for showing the speed bar in the instrument panel
	double getSpeedPercent() const;

	// Test if the given group name is flagged as a logo group (used for faction logos)
	bool isFactionLogoGroup(const BGCPString& groupName);

	// The point to attack may differ per vehicle class, the center of the volume isn't necessarily a good spot to attack
	virtual Point3D getAttackSpot(bool onGround) const;

	// Get range of currently active weapon
	double getActiveWeaponRange () const;

	// Get sensor damage - 0 equals no damage, 1 equals sensors completely destroyed
	virtual double getSensorDamage();

	// Provide a vehicle summary for display
	void getSummary(BGCPString& str);

protected:
	WeaponType HeatSinkType, InternalStructureType;
	int techType, ArmorType, EngineType;
	int tonnage, jumpJets, heatSinks, engineRating;
	Texture* mVehicleTexture; // known to all the vehicle submeshes, but this class deletes it
	BGCPString comment;
	std::vector<MountedWeapon> weapons;
	std::vector<MountedAmmo> ammo;
	std::vector<CriticalLayout> criticalLayouts;
	std::vector<Point3D> m_joint;
	bool mUseCockpitDisplays;
	VehicleStatus vehicleStatus;
	AbstractVehicleAI* mAI;
	Point3D mJumpJetActivity;
	std::vector<VehicleSection> mSections;
	std::vector<VirtualClass*> mThingsToDeleteInDestructor;

	// Does the vehicle have ground contact?
	bool mHasGroundContact;

	// Factor that reduces a Mech's speed
	double mMobility;

	// Count critical hits
	int mEngineHits;

	// Count heat sink critical hits
	int mHeatSinkCriticalHits;

	// Current vehicle speed in meters per second
	double mSpeed;

	// Min/max speed the vehicle can handle
	double mMinSpeed, mMaxSpeed;

	// How many degrees does the vehicle turn per second?
	double mMaxTurnPerSecond;

	int calcMASCTonsAndSlots() const;
	WeaponType decodeMW2Critical(uint16_t ID, int HeatSinkType, bool subType31stCC);
	void checkBasicMechProperties(const MemoryBlock* mb, bool& subType31stCC);

	// Check how many criticals in a MEK file are of type eqptID or between minID and maxID
	size_t countCriticals(const MemoryBlock* mb, WORD minID, WORD maxID);
	size_t countCriticals(const MemoryBlock* mb, WORD eqptID);

	// Count number of criticals stored in the current config
	int countCriticals(WeaponType type);

	int findCriticalInRange(const MemoryBlock* mb, int start, int end);

	// Check if a section may mount jump jets
	virtual bool mayMountJJs(int section);

	// Parse display component coords from XML
	void parseDisplayCoords(XMLTreeConstIterator& iter, DisplayComponentCoords& coords);

	// Copy mech part from previously loaded scene graph
	bool copyGeometry(const std::vector<RenderableObject*>& geometries, XMLTreeConstIterator& iter, int partNumber, Texture* texture);

	// Load mech limb
	void loadPart (XMLTreeConstIterator& iter, int partNumber, const PathChar* path);

	void placePart(XMLTreeConstIterator& iter, const std::vector<RenderableObject*>& geometries, int partNumber, const PathChar* mechDir);

	double animate(int part, double phase, double speed, double phaseShift);

	// Create shot for a projectile weapon fired
	void createShot(MountedWeapon& mountedWeapon, DWORD now);

	// Help function for parsing that translates a section name to a number
	virtual int getSectionByName(const BGCPString& str);

	// Parse weapon and add it to section
	bool addWeaponByName(int section, const BGCPString& weaponName);

	// Add a logo to the main texture
	void processLogoTag(XMLTreeConstIterator&iter);

	// Compute additional values after loading a config (like heat sinks)
	virtual void postLoadConfig();

	// Construct transform matrix for a limb and assign it to the limb
	Matrix setLimbMatrix(int limb, const Matrix& baseMatrix, double angle, 
		float axis_x, float axis_y, float axis_z);
};

////////////////////////////////////////////////////////////////////////////////

Vehicle* loadVehicle(const BGCPString& name, BGCPString& partPath,
	BGCPString& mvmkFN, BGCPString& textureFN, const BGCPString& faction);

Vehicle* loadVehicle2(const BGCPString& chassisName, const BGCPString& configFN, 
	const BGCPString& textureFN, const BGCPString& faction);

// check if o belongs to a vehicle and if so, return it, or null otherwise
Vehicle* getVehicle(RenderableObject* o);

// Given a piece of a vehicle, find its parent ...
Vehicle* findVehicleParent(RenderableObject* hitObject);

// Given an MVMK and config file, determine the market price of that vehicle in C-Bills
int calcVehiclePrice(const BGCPString& mvmkPath, const BGCPString& configPath);

#endif
