module Utils.Schedule.Week.TimeSlot
  ( TimeSlot(..)
  , timeSlots, timeSlotsFromTo, timeSlotsAll
  , isInTimeSlot
  , nextTimeSlot
  , timeSlotToUTCTime
  , formatTimeSlotW
  ) where

import Import

import Handler.Utils.DateTime (formatTimeRangeW, localTimeToUTCSimple)

import Utils.Schedule.Week.Types.TimeSlot


-- TODO: This module needs major refactoring


timeSlots :: Bool -- ^ Only slots between from/to?
          -> NominalDiffTime -- ^ Step
          -> NominalDiffTime -- ^ From
          -> NominalDiffTime -- ^ To
          -> [TimeSlot]
timeSlots onlyFromTo (abs -> slotStep) f t
  | t < f = timeSlots onlyFromTo slotStep t f
  | slotStep <= 0 = error "Invalid slotStep"
  | otherwise = reverse [ TimeSlot{..}
                        | tsTo <- [f,f - slotStep..0]
                        , let tsFrom = tsTo - slotStep
                        , not onlyFromTo || tsFrom >= f
                        , tsFrom >= 0
                        ]
             ++         [ TimeSlot{..}
                        | tsFrom <- [f,f + slotStep..nominalDay]
                        , let tsTo = tsFrom + slotStep
                        , not onlyFromTo || tsTo <= t
                        , tsTo <= nominalDay
                        ]

timeSlotsFromTo :: NominalDiffTime -- ^ Step
                -> NominalDiffTime -- ^ From
                -> NominalDiffTime -- ^ To
                -> [TimeSlot]
timeSlotsFromTo = timeSlots True

timeSlotsAll :: NominalDiffTime -- ^ Step
             -> NominalDiffTime -- ^ From
             -> [TimeSlot]
timeSlotsAll step f = timeSlots False step f f -- @t@ is unused in `timeSlots`, iff @onlyFromTo@ is `False`

-- | Check whether a given time of day lies within a given TimeSlot
isInTimeSlot :: TimeOfDay -> TimeSlot -> Bool
isInTimeSlot (timeOfDayToNominalTime -> time) TimeSlot{..} = tsFrom <= time && time < tsTo

-- | Get the successor of a TimeSlot
nextTimeSlot :: NominalDiffTime -> TimeSlot -> TimeSlot
nextTimeSlot slotStep TimeSlot{..} = TimeSlot{ tsFrom = tsTo, tsTo = tsTo + slotStep }

-- | Convert a TimeSlot to UTCTime for a given TimeZone
timeSlotToUTCTime :: Day -> TimeSlot -> (UTCTime, UTCTime)
timeSlotToUTCTime d TimeSlot{..} = (nominalDiffTimeToUTC tsFrom, nominalDiffTimeToUTC tsTo)
  where nominalDiffTimeToUTC = localTimeToUTCSimple . LocalTime d . nominalTimeToTimeOfDay

-- | Format a given TimeSlot as time range
formatTimeSlotW :: TimeSlot -> Widget
formatTimeSlotW TimeSlot{..} = formatTimeRangeW SelFormatTime (nominalTimeToTimeOfDay tsFrom) $ Just (nominalTimeToTimeOfDay tsTo)
