module FuzzyTimings.TimeSlice (TimeSlice(..), 
                          tsDuration,
                          mkTimeSlice,
                          overlaps,
                          addSecs,
                          inTimeSlice,
                          intersectTimeSlice,
                          deleteTimeSlice,
                          cutTimeSlice) where

import Data.Time.LocalTime
import Data.Time.Calendar
import Data.Time.Clock
import Data.Maybe
import Data.List


data TimeSlice a = TimeSlice {
    tsStart    :: LocalTime,
    tsEnd      :: LocalTime,
    tsValue    :: a
} deriving (Show)

instance Eq (TimeSlice a) where
    t1 == t2 = tsStart t1 == tsStart t2 
             && tsEnd t1 == tsEnd t2

instance Ord (TimeSlice a) where
    compare t1 t2 = compare (tsStart t1) (tsStart t2)

tsDuration :: TimeSlice a -> Int
tsDuration t = floor $ (localTimeToUTC utc $ tsEnd t)
                       `diffUTCTime`
                       (localTimeToUTC utc $ tsStart t)

addSecs :: Int -> LocalTime -> LocalTime
addSecs s lt = LocalTime {
        localDay = addDays (fromIntegral (days + extraDay)) 
                           (localDay lt),
        localTimeOfDay = dayFractionToTimeOfDay endTodFrac
    }
    where
        days = s `div` 86400
        rest = (fromIntegral (s `mod` 86400)) / 86400
        tod = localTimeOfDay lt
        endTodFrac' = (timeOfDayToDayFraction tod) + rest
        (extraDay, endTodFrac)
            | endTodFrac' >= 1 = (1, endTodFrac' - 1)
            | otherwise = (0, endTodFrac') 
       
mkTimeSlice :: LocalTime -> Int -> a -> TimeSlice a
mkTimeSlice start duration value  = TimeSlice {
        tsStart = start,
        tsEnd = addSecs duration start,
        tsValue = value
    }

inTimeSlice :: TimeSlice a -> LocalTime -> Bool
inTimeSlice ts lt = lt >= tsStart ts && lt < tsEnd ts
overlaps :: TimeSlice a -> TimeSlice b -> Bool
overlaps t1 t2 
   | tsStart t1 == tsEnd t1 = False
   | tsStart t2 == tsEnd t2 = False
   | otherwise = (tsStart t1 >= tsStart t2 && tsStart t1 < tsEnd t2)
                 || (tsStart t2 >= tsStart t1 && tsStart t2 < tsEnd t1)

intersectTimeSlice :: TimeSlice a -> TimeSlice b -> Maybe (TimeSlice a)
intersectTimeSlice t1 t2
    | overlaps t1 t2 = Just (t1 {
            tsStart = max (tsStart t1) (tsStart t2),
            tsEnd = min (tsEnd t1) (tsEnd t2)
        })
    | otherwise = Nothing

deleteTimeSlice :: TimeSlice a -> TimeSlice b -> [TimeSlice a]
deleteTimeSlice t1 t2 = deleteBy $ intersectTimeSlice t1 t2
    where
        deleteBy (Just t3) 
            | tsStart t3 > tsStart t1 && tsEnd t3 < tsEnd t1 = 
                [ t1 { tsEnd = tsStart t3 }, t1 { tsStart = tsEnd t3 } ]
            | tsStart t3 > tsStart t1 = [ t1 { tsEnd = tsStart t3 }]
            | otherwise = [ t1 { tsStart = tsEnd t3 } ]
        deleteBy Nothing = [t1]
        

cutTimeSlice :: [LocalTime] -> TimeSlice a -> [TimeSlice a]
cutTimeSlice times ts = let
    times' = tsStart ts : filter (inTimeSlice ts) times ++ [tsEnd ts]
    cut (t1:t2:lts) = ts {
            tsStart = t1,
            tsEnd = t2
        }:cut (t2:lts)
    cut _ = []
    in (cut . sort . nub) times'