packages feed

weekdaze-0.0.0.1: src-lib/WeekDaze/Data/Resource.hs

{-# LANGUAGE CPP #-}
{-
	Copyright (C) 2013-2016 Dr. Alistair Ward

	This file is part of WeekDaze.

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

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

 [@DESCRIPTION@]

	* An abstract description of the properties common to /student/s, /teacher/s or /location/s.

	* Currently just their /availability/, but in principle other common facets of these entities may be added as they're discovered.

	* The simultaneous /availability/ of largely arbitrary collections of tuples can also be determined.
-}

module WeekDaze.Data.Resource(
-- * Type-classes
	Resource(..),
-- * Types
-- ** Type-synonyms
	ResourceMap,
-- * Functions
--	findCommonAvailability,
	countDaysPerWeekAvailable,
	isAvailable,
	extractAvailableResources
) where

import qualified	Data.Foldable
import qualified	Data.Functor
import qualified	Data.Map
import qualified	Data.Set
import qualified	WeekDaze.Size			as Size
import qualified	WeekDaze.Temporal.Availability	as Temporal.Availability
import qualified	WeekDaze.Temporal.Day		as Temporal.Day

-- | Describes a finite resource, or group of finite resources.
class Resource resource where
	getAvailability :: resource -> Temporal.Availability.Availability	-- ^ The set of /day/s on which the /resource/s are all regularly /available/ to be booked.

	isAvailableOn :: Temporal.Day.Day -> resource -> Bool			-- ^ True if on the specified /day/, all /resource/s are regularly scheduled to be /available/.
	isAvailableOn day	= Temporal.Availability.isAvailableOn day . getAvailability	-- Default implementation.

-- | The number of /day/s in any week, on which all the /resource/s are simultaneously /available/.
countDaysPerWeekAvailable :: Resource resource => resource -> Size.NDays
countDaysPerWeekAvailable	= Temporal.Availability.countDaysPerWeekAvailable . getAvailability

-- | True if on any /day/, all the /resource/s are regularly scheduled to be simultaneously /available/.
isAvailable :: Resource resource => resource -> Bool
isAvailable	= not . Temporal.Availability.isUnavailable . getAvailability

-- Properties of homogeneous resources ...

-- | Returns the /day/s on which all the specified /resource/s are /available/.
findCommonAvailability :: (Data.Foldable.Foldable f, Data.Functor.Functor f, Resource resource) => f resource -> Temporal.Availability.Availability
findCommonAvailability	= Temporal.Availability.findIntersections . fmap getAvailability

instance Resource resource => Resource [resource] where
	getAvailability		= findCommonAvailability
	isAvailableOn day	= all (isAvailableOn day)

instance Resource resource => Resource (Data.Map.Map k resource) where
	getAvailability		= findCommonAvailability
	isAvailableOn day	= Data.Foldable.all (isAvailableOn day)

instance (
#if !MIN_VERSION_containers(0,5,2)
	Ord		resource,	-- Not required after "Data.Set-7.8.1".
#endif
	Resource	resource
 ) => Resource (Data.Set.Set resource) where
	getAvailability		= Temporal.Availability.findIntersections . Data.Set.map getAvailability
	isAvailableOn day	= Data.Foldable.all (isAvailableOn day)

-- Properties of heterogenous resources ...

instance (Resource a, Resource b) => Resource (a, b) where
	getAvailability (x, y)		= getAvailability x `Temporal.Availability.findIntersection` getAvailability y
	isAvailableOn day (x, y)	= all (Temporal.Availability.isAvailableOn day) [getAvailability x, getAvailability y]

instance (Resource a, Resource b, Resource c) => Resource (a, b, c) where
	getAvailability (x, y, z)	= Temporal.Availability.findIntersections [getAvailability x, getAvailability y, getAvailability z]
	isAvailableOn day (x, y, z)	= all (Temporal.Availability.isAvailableOn day) [getAvailability x, getAvailability y, getAvailability z]

-- | Describes a map indexed by unique /resource/-identifiers.
type ResourceMap resourceId resource	= Data.Map.Map resourceId resource

-- | Extract those /resource/s from the map provided, which are /available/ on the specified /day/.
extractAvailableResources :: Resource resource => Temporal.Day.Day -> ResourceMap resourceId resource -> ResourceMap resourceId resource
extractAvailableResources day	= Data.Map.filter (isAvailableOn day)