module BuildBox.Aspect.Comparison
	( 
	-- * Comparisons
	  Comparison	(..)
	, makeComparison
	, appSwing
	
	-- * Comparisons of Statistics
	, StatsComparison(..)
	, makeStatsComparison
	, makeStatsComparisonNew
	, predSwingStatsComparison)
where
import BuildBox.Aspect.Stats
import BuildBox.Pretty
import Text.Printf


-- | The comparison of two values.
data Comparison a	
	-- | Comparison of a recent value with a baseline.
	= Comparison
	{ comparisonBaseline	:: a
	, comparisonRecent	:: a
	, comparisonSwing	:: Double }
	
	-- | A new value that doesn't have a baseline.
	| ComparisonNew
	{ comparisonNew		:: a }
	
	deriving (Read, Show)

instance Pretty a => Pretty (Comparison a) where
	ppr (Comparison _ recent ratio)
		| abs ratio < 0.01	
		= text $ printf "%s (----)"
				(render $ ppr recent)

		| otherwise		
		= text $ printf "%s (%+4.0f)"
				(render $ ppr recent)
				(ratio * 100)

	ppr (ComparisonNew new)
		= (padL 10 $ ppr new)
		

-- | Make a comparison from two values.
makeComparison :: Real a => a -> a -> Comparison a
makeComparison base recent
	= Comparison base recent swing
	
	where	dBase	= fromRational $ toRational base
		dRecent	= fromRational $ toRational recent
		swing = ((dRecent - dBase) / dBase)


-- | Apply a function to the swing of a comparison.
appSwing :: a -> (Double -> a) -> Comparison b -> a
appSwing def f aa
 = case aa of
	Comparison _ _ swing	-> f swing
	ComparisonNew{}		-> def
	

-- StatsComparison --------------------------------------------------------------------------------
-- | Comparisons of statistics
data StatsComparison a
	= StatsComparison (Stats (Comparison a))
	deriving (Read, Show)

instance Pretty a => Pretty (StatsComparison a) where
	ppr (StatsComparison stats) = ppr stats

-- | Make a comparison of two `Stats`.
makeStatsComparison :: Real a => Stats a -> Stats a -> StatsComparison a
makeStatsComparison x y = StatsComparison (liftStats2 makeComparison x y)
	

-- | Make a `ComparisonNew`.
makeStatsComparisonNew :: Stats a -> StatsComparison a
makeStatsComparisonNew x
	= StatsComparison (liftStats ComparisonNew x)
	

-- | Return `True` if any of the swings in the `StatsComparison` match the given function.
predSwingStatsComparison :: (Double -> Bool) -> StatsComparison a -> Bool
predSwingStatsComparison f (StatsComparison ss)
	= (predStats . (appSwing False)) f ss