packages feed

bishbosh-0.1.1.0: src-lib/BishBosh/Time/StopWatch.hs

{-
	Copyright (C) 2021 Dr. Alistair Ward

	This file is part of BishBosh.

	BishBosh 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.

	BishBosh 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 BishBosh.  If not, see <http://www.gnu.org/licenses/>.
-}
{- |
 [@AUTHOR@]	Dr. Alistair Ward

 [@DESCRIPTION@]	Defines a stop-watch for use by a single player; so two independent instances will be required to construct a game-clock.
-}

module BishBosh.Time.StopWatch(
-- * Types
-- ** Data-types
	StopWatch(
--		MkStopWatch,
--		deconstruct
	),
-- * Functions
	getElapsedTime,
-- ** Constructor
	mkStoppedWatch
 ) where

import			Control.Arrow((&&&), (|||))
import qualified	BishBosh.Data.Exception		as Data.Exception
import qualified	BishBosh.Property.ShowFloat	as Property.ShowFloat
import qualified	BishBosh.Property.Switchable	as Property.Switchable
import qualified	Control.DeepSeq
import qualified	Control.Exception
import qualified	Data.Default
import qualified	Data.Time.Clock

-- | The watch is either running & records the time at which it was started, or is stopped & records the elapsed duration.
newtype StopWatch	= MkStopWatch {
	deconstruct	:: Either Data.Time.Clock.UTCTime Data.Time.Clock.NominalDiffTime	-- ^ Either the start-time or the previously elapsed duration.
} deriving (Eq, Show)

instance Control.DeepSeq.NFData StopWatch where
	rnf MkStopWatch { deconstruct = e }	= Control.DeepSeq.rnf ||| Control.DeepSeq.rnf $ e

instance Data.Default.Default StopWatch where
	def	= mkStoppedWatch 0

instance Property.ShowFloat.ShowFloat StopWatch where
	showsFloat fromDouble	= fromDouble . getElapsedTime

instance Property.Switchable.Switchable StopWatch where
	on	= Property.Switchable.toggle Data.Default.def

	toggle MkStopWatch { deconstruct = e }	= do
		(diffUTCTime', addUTCTime')	<- fmap (Data.Time.Clock.diffUTCTime &&& flip Data.Time.Clock.addUTCTime) Data.Time.Clock.getCurrentTime	-- Partially apply the functions to the current time.

		return {-to IO-monad-} . MkStopWatch $ (Right . diffUTCTime' ||| Left . addUTCTime' . negate) e	-- Complete the function-application. N.B.: Right & Left are reflected.

	isOn MkStopWatch { deconstruct = Left _ }	= True
	isOn _						= False

-- | Constructor.
mkStoppedWatch :: Data.Time.Clock.NominalDiffTime -> StopWatch
mkStoppedWatch	= MkStopWatch . Right

-- | Extract the elapsed time from a stopped watch.
getElapsedTime :: Fractional f => StopWatch -> f
getElapsedTime MkStopWatch {
	deconstruct	= Right nominalDiffTime
}			= realToFrac nominalDiffTime
getElapsedTime _	= Control.Exception.throw $ Data.Exception.mkRequestFailure "BishBosh.Time.StopWatch.getElapsedTime:\tthe watch is still running."