{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Hcompta.Date.Interval where

import qualified Data.Fixed
import qualified Data.List
import           Data.Maybe (fromJust)
import qualified Data.Time.Calendar    as Time
import qualified Data.Time.Clock       as Time
import qualified Data.Time.Format      as Time ()
import qualified Data.Time.LocalTime   as Time

import           Hcompta.Date hiding (year, month, dom, hour, minute, second)
import qualified Hcompta.Date as Date
import           Hcompta.Lib.Interval (Interval)
import qualified Hcompta.Lib.Interval as Interval


-- *  Gregorian

year :: Year -> Interval Date
year y =
	let ly = Time.addGregorianYearsClip (y - 1970) (day nil) in
	let hy = Time.addGregorianYearsClip 1 ly in
	fromJust $ (Interval.<=..<)
	 nil{Time.utctDay=ly}
	 nil{Time.utctDay=hy}

month :: Year -> Month -> Interval Date
month y m =
	let ly = Time.addGregorianYearsClip (y - 1970) (day nil) in
	let lm = Time.addGregorianMonthsClip (toInteger m - 1) ly in
	let hm = Time.addGregorianMonthsClip 1 lm in
	fromJust $ (Interval.<=..<)
	 nil{Time.utctDay=lm}
	 nil{Time.utctDay=hm}

dom :: Year -> Month -> DoM -> Interval Date
dom y m d =
	let ly = Time.addGregorianYearsClip (y - 1970) (day nil) in
	let lm = Time.addGregorianMonthsClip (toInteger m - 1) ly in
	let ld = Time.addDays (toInteger d - 1) lm in
	let hd = Time.addDays 1 ld in
	fromJust $ (Interval.<=..<)
	 nil{Time.utctDay=ld}
	 nil{Time.utctDay=hd}

hour :: Year -> Month -> DoM -> Hour -> Interval Date
hour y m d h =
	let ly = Time.addGregorianYearsClip (y - 1970) (day nil) in
	let lm = Time.addGregorianMonthsClip (toInteger m - 1) ly in
	let ld = nil{Time.utctDay=Time.addDays (toInteger d - 1) lm} in
	let lh = Time.addUTCTime (fromInteger $ toInteger (h * 3600)) ld in
	let hh = Time.addUTCTime 3600 lh in
	fromJust $ (Interval.<=..<) lh hh

minute :: Year -> Month -> DoM -> Hour -> Minute -> Interval Date
minute y m d h n =
	let ly = Time.addGregorianYearsClip (y - 1970) (day nil) in
	let lm = Time.addGregorianMonthsClip (toInteger m - 1) ly in
	let ld = nil{Time.utctDay=Time.addDays (toInteger d - 1) lm} in
	let lh = Time.addUTCTime (fromInteger $ toInteger (h * 3600)) ld in
	let ln = Time.addUTCTime (fromInteger $ toInteger (n * 60)) lh in
	let hn = Time.addUTCTime 60 ln in
	fromJust $ (Interval.<=..<) ln hn

second :: Year -> Month -> DoM -> Hour -> Minute -> Second -> Interval Date
second y m d h n (Data.Fixed.MkFixed s) =
	let ly = Time.addGregorianYearsClip (y - 1970) (day nil) in
	let lm = Time.addGregorianMonthsClip (toInteger m - 1) ly in
	let ld = nil{Time.utctDay=Time.addDays (toInteger d - 1) lm} in
	let lh = Time.addUTCTime (fromInteger $ toInteger (h * 3600)) ld in
	let ln = Time.addUTCTime (fromInteger $ toInteger (n * 60)) lh in
	let ls = Time.addUTCTime (fromInteger $ s) ln in
	let hs = Time.addUTCTime 1 ls in
	fromJust $ (Interval.<=..<) ls hs

-- * Slice

slice :: (Date -> Interval Date) -> Interval Date -> [Interval Date]
slice slicer i =
	Data.List.unfoldr
	 (\last_slice -> case Interval.intersection i last_slice of
		 Nothing -> Nothing
		 Just n  -> Just (n, slicer (Interval.limit $ Interval.high last_slice))
		) $
	slicer (Interval.limit $ Interval.low i)

year_slice :: Date -> Interval Date
year_slice d =
	let (y, _, _) = Date.gregorian d in
	year y

month_slice :: Date -> Interval Date
month_slice d =
	let (y, m, _) = Date.gregorian d in
	month y m

dom_slice :: Date -> Interval Date
dom_slice d =
	let (y, m, o) = Date.gregorian d in
	dom y m o

hour_slice :: Date -> Interval Date
hour_slice d =
	let (y, m, o) = Date.gregorian d in
	let Time.TimeOfDay h _ _ = tod d in
	hour y m o h

minute_slice :: Date -> Interval Date
minute_slice d =
	let (y, m, o) = Date.gregorian d in
	let Time.TimeOfDay h n _ = tod d in
	minute y m o h n

second_slice :: Date -> Interval Date
second_slice d =
	let (y, m, o) = Date.gregorian d in
	let Time.TimeOfDay h n s = tod d in
	second y m o h n s