/*
**	Command & Conquer Generals Zero Hour(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	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/>.
*/

////////////////////////////////////////////////////////////////////////////////
//																																						//
//  (c) 2001-2003 Electronic Arts Inc.																				//
//																																						//
////////////////////////////////////////////////////////////////////////////////

// FILE: TerrainVisual.h //////////////////////////////////////////////////////////////////////////
// Interface for visual representation of terrain on the client
// Author: Colin Day, April 2001
///////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __TERRAINVISUAL_H_
#define __TERRAINVISUAL_H_

#include "Common/Terrain.h"
#include "Common/Snapshot.h"
#include "Common/MapObject.h"

// FORWARD REFERENCES /////////////////////////////////////////////////////////////////////////////
class TerrainType;
class WaterHandle;
class Matrix3D;
class Object;
class Drawable;
class GeometryInfo;


class WorldHeightMap;
struct SeismicSimulationNode;
class SeismicSimulationFilterBase;




#define DEFAULT_SEISMIC_SIMULATION_MAGNITUDE (20.0f)
struct SeismicSimulationNode; // just a forward declaration folks, no cause for alarm
class SeismicSimulationFilterBase
{
public:
  enum SeismicSimStatusCode
  {
    SEISMIC_STATUS_INVALID,
    SEISMIC_STATUS_ACTIVE,
    SEISMIC_STATUS_ZERO_ENERGY,
  };
  
  virtual SeismicSimStatusCode filterCallback( WorldHeightMapInterfaceClass *heightMap, const SeismicSimulationNode *node ) = 0;
  virtual Real applyGravityCallback( Real velocityIn ) = 0;
};





struct SeismicSimulationNode
{
  SeismicSimulationNode()
  {
    m_center.x    = 0;
    m_center.y    = 0;
    m_radius      = 0;
    m_region.lo.x = 0;
    m_region.lo.y = 0;
    m_region.hi.x = 0;
    m_region.hi.y = 0;
    m_clean = FALSE;
    callbackFilter = NULL;
    m_life = 0;
    m_magnitude = DEFAULT_SEISMIC_SIMULATION_MAGNITUDE;

  }
  SeismicSimulationNode( const SeismicSimulationNode &ssn )
  {
    m_center.x    = ssn.m_center.x;
    m_center.y    = ssn.m_center.y;
    m_radius      = ssn.m_radius;
    m_region.lo.x = ssn.m_region.lo.x;
    m_region.lo.y = ssn.m_region.lo.y;
    m_region.hi.x = ssn.m_region.hi.x;
    m_region.hi.y = ssn.m_region.hi.y;
    m_clean       = ssn.m_clean;
    callbackFilter= ssn.callbackFilter;
    m_life        = ssn.m_life;
    m_magnitude   = ssn.m_magnitude;

  }
  SeismicSimulationNode( const Coord3D* ctr, Real rad, Real mag, SeismicSimulationFilterBase *cbf = NULL )
  {
    m_center.x    = REAL_TO_INT_FLOOR(ctr->x/MAP_XY_FACTOR);
    m_center.y    = REAL_TO_INT_FLOOR(ctr->y/MAP_XY_FACTOR);
    m_radius      = (rad-1)/MAP_XY_FACTOR;
    UnsignedInt regionSize = rad/MAP_XY_FACTOR;
    m_region.lo.x = m_center.x - regionSize;
    m_region.lo.y = m_center.y - regionSize;
    m_region.hi.x = m_center.x + regionSize;
    m_region.hi.y = m_center.y + regionSize;
    m_clean       = false;
    callbackFilter= cbf;
    m_life        = 0;
    m_magnitude   = mag;

  }

  SeismicSimulationFilterBase::SeismicSimStatusCode handleFilterCallback( WorldHeightMapInterfaceClass *heightMap )
  {
    if ( callbackFilter == NULL )
      return SeismicSimulationFilterBase::SEISMIC_STATUS_INVALID;

    ++m_life;

    return callbackFilter->filterCallback( heightMap, this );
  }

  Real applyGravity( Real velocityIn )
  {
    DEBUG_ASSERTCRASH( callbackFilter, ("SeismicSimulationNode::applyGravity() has no callback filter!") );

    if ( callbackFilter == NULL )
      return velocityIn;//oops, we have no callback!

    return callbackFilter->applyGravityCallback( velocityIn );
    
  }

  IRegion2D   m_region;
  ICoord2D    m_center;
  Bool        m_clean;
  Real        m_magnitude;
  UnsignedInt m_radius;
  UnsignedInt m_life;
  
  SeismicSimulationFilterBase *callbackFilter;
  
};
typedef std::list<SeismicSimulationNode> SeismicSimulationList;
typedef SeismicSimulationList::iterator SeismicSimulationListIt;

class DomeStyleSeismicFilter : public SeismicSimulationFilterBase
{
  virtual SeismicSimStatusCode filterCallback( WorldHeightMapInterfaceClass *heightMap, const SeismicSimulationNode *node );
  virtual Real applyGravityCallback( Real velocityIn );
};







//-------------------------------------------------------------------------------------------------
/** LOD values for terrain, keep this in sync with TerrainLODNames[] */
//-------------------------------------------------------------------------------------------------
typedef enum _TerrainLOD
{ 
	TERRAIN_LOD_INVALID								= 0,
	TERRAIN_LOD_MIN										= 1,  // note that this is less than max
	TERRAIN_LOD_STRETCH_NO_CLOUDS			= 2,
	TERRAIN_LOD_HALF_CLOUDS						= 3,
	TERRAIN_LOD_NO_CLOUDS							= 4,
	TERRAIN_LOD_STRETCH_CLOUDS				= 5,
	TERRAIN_LOD_NO_WATER							= 6,
	TERRAIN_LOD_MAX										= 7,  // note that this is larger than min
	TERRAIN_LOD_AUTOMATIC							= 8,
	TERRAIN_LOD_DISABLE								= 9,

	TERRAIN_LOD_NUM_TYPES								// keep this last

} TerrainLOD;
#ifdef DEFINE_TERRAIN_LOD_NAMES
static char * TerrainLODNames[] = 
{
	"NONE",
	"MIN",
	"STRETCH_NO_CLOUDS",
	"HALF_CLOUDS",
	"NO_CLOUDS",
	"STRETCH_CLOUDS",
	"NO_WATER",
	"MAX",
	"AUTOMATIC",
	"DISABLE",

	NULL
};
#endif  // end DEFINE_TERRAIN_LOD_NAMES

//-------------------------------------------------------------------------------------------------
/** Device independent implementation for visual terrain */
//-------------------------------------------------------------------------------------------------
class TerrainVisual : public Snapshot,
											public SubsystemInterface
{

public:

	enum {NumSkyboxTextures = 5};

	TerrainVisual();
	virtual ~TerrainVisual();

	virtual void init( void );
	virtual void reset( void );
	virtual void update( void );

	virtual Bool load( AsciiString filename );

	/// get color of texture on the terrain at location specified
	virtual void getTerrainColorAt( Real x, Real y, RGBColor *pColor ) = 0;

	/// get the terrain tile type at the world location in the (x,y) plane ignoring Z
	virtual TerrainType *getTerrainTile( Real x, Real y ) = 0;

	/** intersect the ray with the terrain, if a hit occurs TRUE is returned
	and the result point on the terrain is returned in "result" */
	virtual Bool intersectTerrain( Coord3D *rayStart, 
																 Coord3D *rayEnd, 
																 Coord3D *result ) { return FALSE; }

	//
	// water methods
	//
	virtual void enableWaterGrid( Bool enable ) = 0;
	/// set min/max height values allowed in water grid pointed to by waterTable
	virtual void setWaterGridHeightClamps( const WaterHandle *waterTable, Real minZ, Real maxZ ) = 0;
	/// adjust fallof parameters for grid change method
	virtual void setWaterAttenuationFactors( const WaterHandle *waterTable, Real a, Real b, Real c, Real range ) = 0;
	/// set the water table position and orientation in world space
	virtual void setWaterTransform( const WaterHandle *waterTable, Real angle, Real x, Real y, Real z ) = 0;
	virtual void setWaterTransform( const Matrix3D *transform ) = 0;
	/// get water transform parameters
	virtual void getWaterTransform( const WaterHandle *waterTable, Matrix3D *transform ) = 0;
	/// water grid resolution spacing
	virtual void setWaterGridResolution( const WaterHandle *waterTable, Real gridCellsX, Real gridCellsY, Real cellSize ) = 0;
	virtual void getWaterGridResolution( const WaterHandle *waterTable, Real *gridCellsX, Real *gridCellsY, Real *cellSize ) = 0;
	/// adjust the water grid in world coords by the delta
	virtual void changeWaterHeight( Real x, Real y, Real delta ) = 0;
	/// adjust the velocity at a water grid point corresponding to the world x,y
	virtual void addWaterVelocity( Real worldX, Real worldY, Real velocity, Real preferredHeight ) = 0;
	/// get height of water grid at specified position
	virtual Bool getWaterGridHeight( Real worldX, Real worldY, Real *height) = 0;

	/// set detail of terrain tracks.
	virtual void setTerrainTracksDetail(void)=0;
	virtual void setShoreLineDetail(void)=0;
		
	/// Add a bib for an object at location.  
	virtual void addFactionBib(Object *factionBuilding, Bool highlight, Real extra = 0)=0;
	/// Remove a bib.  
	virtual void removeFactionBib(Object *factionBuilding)=0;

	/// Add a bib for a drawable at location.  
	virtual void addFactionBibDrawable(Drawable *factionBuilding, Bool highlight, Real extra = 0)=0;
	/// Remove a bib.  
	virtual void removeFactionBibDrawable(Drawable *factionBuilding)=0;

	virtual void removeAllBibs(void)=0;
	virtual void removeBibHighlighting(void)=0;

	virtual void removeTreesAndPropsForConstruction(
		const Coord3D* pos, 
		const GeometryInfo& geom,
		Real angle
	) = 0;

	virtual void addProp(const ThingTemplate *tt, const Coord3D *pos, Real angle) = 0;

	//
	// Modify height.
	//
	virtual void setRawMapHeight(const ICoord2D *gridPos, Int height)=0;
	virtual Int getRawMapHeight(const ICoord2D *gridPos)=0;


  ////////////////////////////////////////////////////
  ////////////////////////////////////////////////////
  ////////////////////////////////////////////////////
#ifdef DO_SEISMIC_SIMULATIONS
  virtual void updateSeismicSimulations( void ) = 0; /// walk the SeismicSimulationList and, well, do it.
  virtual void addSeismicSimulation( const SeismicSimulationNode& sim ) = 0;
#endif
  virtual WorldHeightMap* getLogicHeightMap( void ) {return NULL;};
  virtual WorldHeightMap* getClientHeightMap( void ) {return NULL;};
  ////////////////////////////////////////////////////
  ////////////////////////////////////////////////////
  ////////////////////////////////////////////////////



	/// Replace the skybox texture
	virtual void replaceSkyboxTextures(const AsciiString *oldTexName[NumSkyboxTextures], const AsciiString *newTexName[NumSkyboxTextures])=0;

protected:

	// snapshot methods
	virtual void crc( Xfer *xfer );
	virtual void xfer( Xfer *xfer );
	virtual void loadPostProcess( void );

	AsciiString m_filenameString;							///< file with terrain data

};  // end class TerrainVisual

// EXTERNALS //////////////////////////////////////////////////////////////////////////////////////
extern TerrainVisual *TheTerrainVisual;  ///< singleton extern

#endif  // end __TERRAINVISUAL_H_
