#pragma once

#include <wx/string.h>
#include <wx/colour.h>

//Defaults - these are also written into the seeded config file so the user sees editable starting values.
//static gives each including .cpp its own internal-linkage copy. The numeric consts get that automatically
//(a const-qualified type has internal linkage), but a "const char*" is a non-const pointer to const char, so
//without static those would have external linkage and collide when this header is included by more than one .cpp.
static const char*  CLIENTCONFIG_DEFAULT_LOCATION_NAME = "Greenview";		///< Default location name.
static const char*  CLIENTCONFIG_DEFAULT_IMAGES_FOLDER = "~/rumpi_images";	///< Default images folder.
static const long   CLIENTCONFIG_DEFAULT_ANIMATION_FPS = 20;				///< Default scene animation frame rate.
static const double CLIENTCONFIG_DEFAULT_CO2_FRESH_MAX = 800.0;				///< Default upper CO2 bound for "fresh".
static const double CLIENTCONFIG_DEFAULT_CO2_ELEVATED_MAX = 1200.0;			///< Default upper CO2 bound for "elevated".
static const double CLIENTCONFIG_DEFAULT_TEMP_WARM_ABOVE = 22.0;				///< Default temperature above which it reads "warm".
static const double CLIENTCONFIG_DEFAULT_TEMP_COOL_BELOW = 16.0;				///< Default temperature below which it reads "cool".
static const double CLIENTCONFIG_DEFAULT_DUSK_GLOW_OPACITY = 0.18;			///< Default dusk-glow overlay opacity.
static const double CLIENTCONFIG_DEFAULT_TEMP_TINT_OPACITY = 0.30;			///< Default temperature-tint overlay opacity.
static const double CLIENTCONFIG_DEFAULT_CLOUD_OPACITY = 0.85;				///< Default cloud overlay opacity.
static const double CLIENTCONFIG_DEFAULT_HAZE_OPACITY = 0.70;				///< Default haze overlay opacity.
static const double CLIENTCONFIG_DEFAULT_MIST_OPACITY = 0.35;				///< Default mist overlay opacity.
static const double CLIENTCONFIG_DEFAULT_WIND_OPACITY = 0.55;				///< Default wind overlay opacity.
static const char*  CLIENTCONFIG_DEFAULT_CO2_FRESH_COLOUR = "#4CAF50";		///< Default "fresh" CO2 indicator colour.
static const char*  CLIENTCONFIG_DEFAULT_CO2_ELEVATED_COLOUR = "#E0A93B";	///< Default "elevated" CO2 indicator colour.
static const char*  CLIENTCONFIG_DEFAULT_CO2_HIGH_COLOUR = "#D9534F";		///< Default "high" CO2 indicator colour.

/**
* Loads the user-editable client settings from an INI file (via wxFileConfig) once at startup and exposes them as
* typed getters with sensible defaults. A commented default file is seeded on first run so the user has something
* to edit, and missing keys fall back to the built-in defaults. Access the shared instance via GetInstance(); the
* file is read once, so edits take effect on the next app start.
*
* @author Eddie O'Hagan
* @date 8/13/2026
*/
class ClientConfig
{
private:
	/**
	* Builds the config, seeding every field with its built-in default, then loads any overrides from the INI file.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	//Constructed once by ClientConfig::GetInstance().
	* @endcode
	*/
	ClientConfig();
	ClientConfig(const ClientConfig&);
	ClientConfig& operator=(const ClientConfig&);

	/**
	* Seeds the config file if missing, then reads every setting from it, expanding a leading ~ in the images folder
	* and falling back to the built-in default for any missing key or malformed colour.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	//Called once by the constructor.
	* @endcode
	*/
	void Load();

	/**
	* Builds the path to the config file (~/rumpi_config.ini), alongside the ~/rumpi_logs and ~/rumpi_images folders.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	wxString path = ConfigFilePath();
	* @endcode
	*
	* @return The full path to the config file.
	*/
	wxString ConfigFilePath() const;

	/**
	* Writes the commented default config file to the given path if it doesn't already exist. If writing fails, the
	* getters still return the built-in defaults so the app keeps working.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	SeedDefaultFileIfMissing(ConfigFilePath());
	* @endcode
	*
	* @param [in] path The config file path to seed.
	*/
	void SeedDefaultFileIfMissing(const wxString& path) const;

	wxString myLocation;				///< The configured location name.
	wxString myImagesFolder;			///< The configured images folder (a leading ~ is expanded to home).
	int myAnimationFps;					///< The scene animation frame rate.
	double myCo2FreshMax;				///< Upper CO2 bound for the "fresh" band.
	double myCo2ElevatedMax;			///< Upper CO2 bound for the "elevated" band.
	double myTempWarmAbove;				///< Temperature above which it reads "warm".
	double myTempCoolBelow;				///< Temperature below which it reads "cool".
	double myDuskGlowOpacity;			///< Maximum dusk-glow overlay opacity.
	double myTemperatureTintOpacity;	///< Maximum temperature-tint overlay opacity.
	double myCloudOpacity;				///< Maximum cloud overlay opacity.
	double myHazeOpacity;				///< Maximum haze overlay opacity.
	double myMistOpacity;				///< Maximum mist overlay opacity.
	double myWindOpacity;				///< Maximum wind overlay opacity.
	wxColour myCo2FreshColour;			///< Indicator colour for the "fresh" CO2 band.
	wxColour myCo2ElevatedColour;		///< Indicator colour for the "elevated" CO2 band.
	wxColour myCo2HighColour;			///< Indicator colour for the "high" CO2 band.

public:
	/**
	* Gets the singleton instance, constructing (and loading) it on first call.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	ClientConfig& config = ClientConfig::GetInstance();
	* @endcode
	*
	* @return The single ClientConfig instance.
	*/
	static ClientConfig& GetInstance();

	//[General]
	/**
	* Gets the configured location name.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	wxString location = config.GetLocation();
	* @endcode
	*
	* @return The location name.
	*/
	const wxString& GetLocation() const;

	//[Scene]
	/**
	* Gets the configured images folder (with a leading ~ already expanded to the home directory).
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	wxString folder = config.GetImagesFolder();
	* @endcode
	*
	* @return The images folder path.
	*/
	const wxString& GetImagesFolder() const;

	/**
	* Gets the scene animation frame rate.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	int fps = config.GetAnimationFps();
	* @endcode
	*
	* @return The animation frame rate.
	*/
	int GetAnimationFps() const;

	//[Thresholds]
	/**
	* Gets the upper CO2 bound for the "fresh" band.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	double max = config.GetCo2FreshMax();
	* @endcode
	*
	* @return The "fresh" CO2 upper bound.
	*/
	double GetCo2FreshMax() const;

	/**
	* Gets the upper CO2 bound for the "elevated" band.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	double max = config.GetCo2ElevatedMax();
	* @endcode
	*
	* @return The "elevated" CO2 upper bound.
	*/
	double GetCo2ElevatedMax() const;

	/**
	* Gets the temperature above which it reads "warm".
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	double temp = config.GetTempWarmAbove();
	* @endcode
	*
	* @return The "warm" temperature threshold.
	*/
	double GetTempWarmAbove() const;

	/**
	* Gets the temperature below which it reads "cool".
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	double temp = config.GetTempCoolBelow();
	* @endcode
	*
	* @return The "cool" temperature threshold.
	*/
	double GetTempCoolBelow() const;

	//[Overlays] - maximum opacity of each atmospheric overlay (0.0 - 1.0).
	/**
	* Gets the maximum dusk-glow overlay opacity.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	double opacity = config.GetDuskGlowOpacity();
	* @endcode
	*
	* @return The dusk-glow overlay opacity (0.0 - 1.0).
	*/
	double GetDuskGlowOpacity() const;

	/**
	* Gets the maximum temperature-tint overlay opacity.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	double opacity = config.GetTemperatureTintOpacity();
	* @endcode
	*
	* @return The temperature-tint overlay opacity (0.0 - 1.0).
	*/
	double GetTemperatureTintOpacity() const;

	/**
	* Gets the maximum cloud overlay opacity.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	double opacity = config.GetCloudOpacity();
	* @endcode
	*
	* @return The cloud overlay opacity (0.0 - 1.0).
	*/
	double GetCloudOpacity() const;

	/**
	* Gets the maximum haze overlay opacity.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	double opacity = config.GetHazeOpacity();
	* @endcode
	*
	* @return The haze overlay opacity (0.0 - 1.0).
	*/
	double GetHazeOpacity() const;

	/**
	* Gets the maximum mist overlay opacity.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	double opacity = config.GetMistOpacity();
	* @endcode
	*
	* @return The mist overlay opacity (0.0 - 1.0).
	*/
	double GetMistOpacity() const;

	/**
	* Gets the maximum wind overlay opacity.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	double opacity = config.GetWindOpacity();
	* @endcode
	*
	* @return The wind overlay opacity (0.0 - 1.0).
	*/
	double GetWindOpacity() const;

	//[Colors] - status indicator colours, overridable for accessibility (colour blindness / contrast).
	/**
	* Gets the indicator colour for the "fresh" CO2 band.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	wxColour colour = config.GetCo2FreshColour();
	* @endcode
	*
	* @return The "fresh" CO2 indicator colour.
	*/
	wxColour GetCo2FreshColour() const;

	/**
	* Gets the indicator colour for the "elevated" CO2 band.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	wxColour colour = config.GetCo2ElevatedColour();
	* @endcode
	*
	* @return The "elevated" CO2 indicator colour.
	*/
	wxColour GetCo2ElevatedColour() const;

	/**
	* Gets the indicator colour for the "high" CO2 band.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	wxColour colour = config.GetCo2HighColour();
	* @endcode
	*
	* @return The "high" CO2 indicator colour.
	*/
	wxColour GetCo2HighColour() const;

};
