#pragma once

#include <wx/panel.h>
#include <wx/bmpbndl.h>
#include <wx/bitmap.h>
#include <wx/timer.h>
#include <wx/string.h>
#include <wx/colour.h>
#include <RumPi/Data/SelectedSensorReadings.h>

class wxGraphicsContext;
class wxPaintDC;

/**
* The Home tab's stylized, condition-reactive backdrop. It draws a day/night base scene (loaded once as scalable
* SVG art, crossfaded by local time and cached so the SVG is not re-rasterized every frame) and paints procedural
* overlays on top - clouds, haze, temperature tint, wind streaks, mist - plus a set of glanceable info pills. The
* overlays are driven by the latest SelectedSensorReadings; a wxTimer advances the motion (drifting wind and
* clouds) between snapshots. Tunable constants (location, thresholds, overlay opacities, frame rate) come from
* ClientConfig at construction.
*
* @author Eddie O'Hagan
* @date 8/13/2026
*/
class HomeScenePanel : public wxPanel
{
protected:
	//Overlay/pill positions are authored in the SVG's 1920x1080 space and scaled to the real panel; the
	//kiosk is 16:9, so a single uniform scale (panel width / DESIGN_WIDTH) keeps proportions.
	static constexpr double DESIGN_WIDTH = 1920.0;	///< The SVG authoring width overlays are positioned against.

	double mySceneAspect;	///< The scene's aspect ratio.

	//Base art, loaded once as resolution-independent SVG bundles.
	wxBitmapBundle myDayScene;		///< Day base art (SVG bundle).
	wxBitmapBundle myNightScene;	///< Night base art (SVG bundle).

	//The latest curated readings that drive the overlays (set from the UI thread via Update()).
	RumPi::SelectedSensorReadings myReadings;

	//Cached day/night-crossfaded base, rebuilt only when the draw size or the (bucketed) blend changes.
	wxBitmap myBaseCache;			///< Cached crossfaded base image.
	int myBaseCacheWidth;			///< Width the base cache was built at.
	int myBaseCacheHeight;			///< Height the base cache was built at.
	int myBaseCacheBlendBucket;		///< Bucketed day/night blend the base cache was built at.

	//Reused off-screen buffer for flicker-free compositing (recreated only on resize).
	wxBitmap myFrameBuffer;

	//Animation phases advanced by the timer: the readings set how fast/how much, the phase sets where.
	double myWindPhase;				///< Current wind animation phase.
	double myCloudPhase;			///< Current cloud animation phase.
	wxTimer myAnimationTimer;		///< Timer that advances the animation phases between snapshots.

	//Loaded from ClientConfig at construction.
	wxString myLocationName;			///< Configured location name.
	double myCo2FreshMax;				///< Upper CO2 bound for the "fresh" band.
	double myCo2ElevatedMax;			///< Upper CO2 bound for the "elevated" band.
	double myTempWarmAbove;				///< Temperature above which the tint reads "warm".
	double myTempCoolBelow;				///< Temperature below which the tint 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.

	/**
	* Gets the current local time as a fractional number of hours (e.g. 15.5 for 15:30).
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	double hours = LocalTimeInHours();
	* @endcode
	*
	* @return The local time, in hours.
	*/
	double LocalTimeInHours() const;

	/**
	* Computes how "night" it is now, from 0.0 (full day) to 1.0 (full night), using a sun-elevation proxy that peaks
	* at noon.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	double night = NightAmount();
	* @endcode
	*
	* @return The night amount, from 0.0 (day) to 1.0 (night).
	*/
	double NightAmount() const;

	/**
	* Picks the status-dot colour for a CO2 reading, using the configured fresh/elevated thresholds and (accessibility-
	* overridable) colours.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	wxColour colour = Co2DotColour(650.0f);
	* @endcode
	*
	* @param [in] co2 The CO2 reading, in ppm.
	*
	* @return The indicator colour for the reading's band.
	*/
	wxColour Co2DotColour(float co2) const;

	/**
	* Picks the status-dot colour for an air-quality level, collapsing the six levels onto the same good/warning/bad
	* palette as the CO2 dot so there is a single palette to retune for accessibility.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	wxColour colour = AirQualityDotColour(RumPi::E_AirQualityLevel::AIR_QUALITY_POOR);
	* @endcode
	*
	* @param [in] airQualityLevel The air-quality level.
	*
	* @return The indicator colour for the level.
	*/
	wxColour AirQualityDotColour(RumPi::E_AirQualityLevel airQualityLevel) const;

	/**
	* Loads a scene image bundle, preferring a user-supplied file (svg, then raster) in the images folder and falling
	* back to the embedded SVG art.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	wxBitmapBundle day = LoadSceneBundle(imagesFolder, "scene_day", SCENE_DAY_SVG);
	* @endcode
	*
	* @param [in] folder      The images folder to look in.
	* @param [in] baseName    The base file name (without extension) to look for.
	* @param [in] embeddedSvg The embedded SVG to fall back to.
	*
	* @return The loaded bundle, or an empty bundle if the inputs were invalid.
	*/
	wxBitmapBundle LoadSceneBundle(const wxString& folder, const wxString& baseName, const char* embeddedSvg) const;

	/**
	* Seeds the images folder with editable copies of the default day/night SVG art, but only when the folder does not
	* exist yet, so a user's own art is never clobbered.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	CreateDefaultScenesIfMissing(imagesFolder);
	* @endcode
	*
	* @param [in] folder The images folder to seed.
	*/
	void CreateDefaultScenesIfMissing(const wxString& folder) const;

	/**
	* Rebuilds the cached day/night-crossfaded base image, but only when the draw size or the bucketed blend has
	* changed, so the SVG art is not re-rasterized every frame.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	EnsureBaseCache(coverWidth, coverHeight, blendBucket);
	* @endcode
	*
	* @param [in] width       The draw width to build the cache at.
	* @param [in] height      The draw height to build the cache at.
	* @param [in] blendBucket The bucketed night amount (0 - 100) to crossfade at.
	*/
	void EnsureBaseCache(int width, int height, int blendBucket);

	/**
	* Draws the procedural sun (day) or moon (night) arcing across the sky by real local time, clipped to the sky so
	* it slips behind the horizon as it rises and sets.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	DrawCelestialBody(graphicsContext, width, height);
	* @endcode
	*
	* @param [in] graphicsContext The graphics context to draw into.
	* @param [in] width           The draw width, in pixels.
	* @param [in] height          The draw height, in pixels.
	*/
	void DrawCelestialBody(wxGraphicsContext* graphicsContext, int width, int height);

	/**
	* Draws the reading-driven atmospheric overlays: dawn/dusk glow, temperature tint, humidity clouds, air-quality
	* haze, humidity mist, and wind streaks, each mapped from its reading through a response curve.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	DrawAtmosphere(graphicsContext, width, height);
	* @endcode
	*
	* @param [in] graphicsContext The graphics context to draw into.
	* @param [in] width           The draw width, in pixels.
	* @param [in] height          The draw height, in pixels.
	*/
	void DrawAtmosphere(wxGraphicsContext* graphicsContext, int width, int height);

	/**
	* Draws the default scene's lamp posts, lit by the TSL2591 light level (brighter the darker it gets) and guarded on
	* a present light sensor so they only appear when there is a real reading.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	DrawStreetlights(graphicsContext, width, height);
	* @endcode
	*
	* @param [in] graphicsContext The graphics context to draw into.
	* @param [in] width           The draw width, in pixels.
	* @param [in] height          The draw height, in pixels.
	*/
	void DrawStreetlights(wxGraphicsContext* graphicsContext, int width, int height);

	/**
	* Draws the glanceable info pills: the location and clock along the top, and a bottom strip of reading pills
	* (temperature, humidity, air quality, CO2, wind) with colour-coded status dots on the air-quality and CO2 pills.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	DrawPills(graphicsContext, width, height);
	* @endcode
	*
	* @param [in] graphicsContext The graphics context to draw into.
	* @param [in] width           The draw width, in pixels.
	* @param [in] height          The draw height, in pixels.
	*/
	void DrawPills(wxGraphicsContext* graphicsContext, int width, int height);

	/**
	* Paint handler: cover-scales and blits the cached base, then composites the celestial body, atmosphere,
	* streetlights, and info pills into a reused off-screen buffer and blits it once (flicker-free).
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	//Invoked by wxWidgets when the panel needs repainting.
	* @endcode
	*
	* @param [in] event The paint event.
	*/
	void OnPaint(wxPaintEvent& event);

	/**
	* Composites one frame (base art, celestial body, atmosphere, streetlights, pills) into the off-screen buffer
	* and blits it. Split out of OnPaint so the paint handler can wrap it in a try/catch - a bad_alloc while
	* compositing full-screen on a low-RAM Pi must not escape the wx paint handler through GTK and terminate the app.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	//Called by OnPaint().
	* @endcode
	*
	* @param [in] drawContext The paint DC the finished frame is blitted to.
	*/
	void RenderScene(wxPaintDC& drawContext);

	/**
	* Size handler: schedules a repaint so the scene re-scales to the new panel size.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	//Invoked by wxWidgets when the panel is resized.
	* @endcode
	*
	* @param [in] event The size event.
	*/
	void OnSize(wxSizeEvent& event);

	/**
	* Timer handler: advances the wind and cloud animation phases (a baseline drift plus an amount proportional to wind
	* speed) and schedules a repaint.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	//Invoked on each animation-timer tick.
	* @endcode
	*
	* @param [in] event The timer event.
	*/
	void OnTimer(wxTimerEvent& event);

public:
	/**
	* Builds the scene panel: loads the tunable settings from ClientConfig, seeds and loads the day/night SVG art
	* (falling back to embedded defaults), binds the paint/size/timer events, and starts the animation timer.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	HomeScenePanel* panel = new HomeScenePanel(notebook);
	* @endcode
	*
	* @param [in] parent The parent window.
	*/
	HomeScenePanel(wxWindow* parent);

	/**
	* Stores the latest sensor snapshot (on the UI thread) and schedules a repaint so the overlays reflect it.
	*
	* @author Eddie O'Hagan
	* @date 8/13/2026
	*
	* @code
	*	panel->Update(readings);
	* @endcode
	*
	* @param [in] readings The latest curated sensor readings.
	*/
	void Update(const RumPi::SelectedSensorReadings& readings);
};
