module Data.Astro.Time.Sidereal
(
  GreenwichSiderealTime
  , LocalSiderealTime
  , dhToGST
  , dhToLST
  , gstToDH
  , lstToDH
  , hmsToGST
  , hmsToLST
  , utToGST
  , gstToUT
  , gstToLST
  , lstToGST
  , lstToGSTwDC
)
where
import Data.Astro.Types (DecimalHours(..), fromHMS)
import Data.Astro.Time.JulianDate (JulianDate(..), TimeBaseType, numberOfCenturies, splitToDayAndTime)
import Data.Astro.Time.Epoch (j2000)
import Data.Astro.Utils (reduceToZeroRange)
import qualified Data.Astro.Types as C
newtype GreenwichSiderealTime = GST TimeBaseType deriving (Show, Eq)
newtype LocalSiderealTime = LST TimeBaseType deriving (Show, Eq)
dhToGST :: DecimalHours -> GreenwichSiderealTime
dhToGST (DH t) = GST t
dhToLST :: DecimalHours -> LocalSiderealTime
dhToLST (DH t) = LST t
gstToDH :: GreenwichSiderealTime -> DecimalHours
gstToDH (GST t) = DH t
lstToDH :: LocalSiderealTime -> DecimalHours
lstToDH (LST t) = DH t
hmsToGST :: Int -> Int -> TimeBaseType -> GreenwichSiderealTime
hmsToGST h m s = dhToGST $ fromHMS h m s
hmsToLST :: Int -> Int -> TimeBaseType -> LocalSiderealTime
hmsToLST h m s = dhToLST $ fromHMS h m s
utToGST :: JulianDate -> GreenwichSiderealTime
utToGST jd =
  let (JD day, JD time) = splitToDayAndTime jd
      t = solarSiderealTimesDiff day
      time' = reduceToZeroRange 24 $ time*24/siderealDayLength + t
  in GST $ time'
gstToUT :: JulianDate -> GreenwichSiderealTime -> JulianDate
gstToUT jd gst =
  let (day, time) = dayTime jd gst
      t = solarSiderealTimesDiff day
      time' = (reduceToZeroRange 24 (time-t)) * siderealDayLength
  in JD $ day + time'/24
  where dayTime jd (GST gst)
          | gst < 0   = (day-1, gst+24)
          | gst >= 24 = (day+1, gst-24)
          | otherwise = (day,   gst)
            where (JD day, _) = splitToDayAndTime jd
gstToLST :: C.DecimalDegrees -> GreenwichSiderealTime -> LocalSiderealTime
gstToLST longitude (GST gst) =
  let C.DH dhours = C.toDecimalHours longitude
      lst = reduceToZeroRange 24 $ gst + dhours
  in LST lst
lstToGST :: C.DecimalDegrees -> LocalSiderealTime -> GreenwichSiderealTime
lstToGST longitude (LST lst) =
  let C.DH dhours = C.toDecimalHours longitude
      gst = reduceToZeroRange 24 $ lst - dhours
  in GST gst
lstToGSTwDC :: C.DecimalDegrees -> LocalSiderealTime -> GreenwichSiderealTime
lstToGSTwDC longitude (LST lst) =
  let C.DH dhours = C.toDecimalHours longitude
      gst = lst - dhours
  in GST gst
siderealDayLength :: TimeBaseType
siderealDayLength = hours/24
  where C.DH hours = fromHMS 23 56 04.0916
solarSiderealTimesDiff :: TimeBaseType -> TimeBaseType
solarSiderealTimesDiff d =
  let t = numberOfCenturies j2000 (JD d)
  in reduceToZeroRange 24 $ 6.697374558 + 2400.051336*t + 0.000025862*t*t