{-# LANGUAGE RankNTypes #-}
module Main where

import Control.Monad.Fix
import Control.Monad.Trans
import qualified Control.Exception as E
import Data.Bits
import Data.Text ()
import Data.Time
import Data.Time.Clock.POSIX

import Test.Tasty
import Test.Tasty.HUnit

import Reflex.Host.Class
import Reflex.Test

import Reflex
import Reflex.Time.UTCTime

assert :: Bool -> String -> IO ()
assert True _ = return ()
assert False err = E.throwIO . E.AssertionFailed $ err

main :: IO ()
main = do
    defaultMain tests

afterTimeSpec :: (Reflex t, MonadHold t m, MonadFix m)
              => TimeBits t -> UTCTime -> m (Dynamic t Bool)
afterTimeSpec (curTime, _) tgtTime = do
  n <- sample . current $ curTime
  case tgtTime <= n of
    True -> return . constDyn $ True
    False ->
      holdDyn False =<< (headE . ffilter (==True) . updated $ (((<) tgtTime) <$> curTime))

tests :: TestTree
tests = testGroup "reflex-time" $
    [ timeUpdateTests
    ]

testAlONCase :: (Eq b, Show b)
             => TestName
             -> (forall t. (ReflexHost t) => Event t a -> HostFrame t (Dynamic t b))
             -> b -> [(Maybe a, Maybe b, b)]
             -> TestTree
testAlONCase tnm frm initVal cgen = testCase tnm $ eventTrace cgen initVal frm

sameBehavior :: (Reflex t, Eq a, MonadSample t m, MonadIO m, Show a) => Behavior t a -> Behavior t a -> m ()
sameBehavior ba bb = do
  va <- sample ba
  vb <- sample bb
  liftIO $ va @=? vb

timeUpdateTests :: TestTree
timeUpdateTests =
    testGroup "AlON update tests" $
      [ testAlONCase "test utc2TimeBits" (\e -> do
                                             (t, tbs) <- utc2TimeBits <$> holdDyn (posixSecondsToUTCTime 0) e
                                             -- We drop 12 because POSIX time is in terms of seconds.
                                             let dbs = sequenceA . take 28 . drop 12 $ tbs
                                             return $ (,) <$> t <*> dbs)
        (posixSecondsToUTCTime 0, take 28 . repeat $ 0) $
          let second1  = [1000000000000 `shiftR` b | b <- [12..39]]
              second2  = [2000000000000 `shiftR` b | b <- [12..39]]
              second60 = [60000000000000 `shiftR` b | b <- [12..39]]
          in [ (Nothing, Nothing
                       , (posixSecondsToUTCTime 0, take 28 . repeat $ 0))
             , (Just (posixSecondsToUTCTime 1), Just (posixSecondsToUTCTime 1, second1)
                                              , (posixSecondsToUTCTime 1, second1))
             , (Just (posixSecondsToUTCTime 2), Just (posixSecondsToUTCTime 2, second2)
                                              , (posixSecondsToUTCTime 2, second2))
             , (Just (posixSecondsToUTCTime 60), Just (posixSecondsToUTCTime 60, second60)
                                               , (posixSecondsToUTCTime 60, second60))
             ]
      , testAlONCase "test afterTimeSpec" (\e -> do
                                          tbs <- utc2TimeBits <$> holdDyn (posixSecondsToUTCTime 0) e
                                          afterTimeSpec tbs . posixSecondsToUTCTime $ 4)
        False
        [ (Nothing, Nothing, False)
        , (Just . posixSecondsToUTCTime $ 1, Nothing, False)
        , (Just . posixSecondsToUTCTime $ 4, Nothing, False) -- Why doesn't it fire here?
        , (Just . posixSecondsToUTCTime $ 5, Just True, True)
        , (Just . posixSecondsToUTCTime $ 6, Nothing, True)
        , (Just . posixSecondsToUTCTime $ 3, Nothing, True)
        ]
      , testAlONCase "test afterTimeSpec skipping" (\e -> do
                                          tbs <- utc2TimeBits <$> holdDyn (posixSecondsToUTCTime 0) e
                                          afterTimeSpec tbs . posixSecondsToUTCTime $ 4)
        False
        [ (Nothing, Nothing, False)
        , (Just . posixSecondsToUTCTime $ 1, Nothing, False)
        , (Just . posixSecondsToUTCTime $ 5, Just True, True)
        , (Just . posixSecondsToUTCTime $ 6, Nothing, True)
        , (Just . posixSecondsToUTCTime $ 3, Nothing, True)
        ]
      , testAlONCase "test afterTimeSpec starting after" (\e -> do
                                          tbs <- utc2TimeBits <$> holdDyn (posixSecondsToUTCTime 5) e
                                          afterTimeSpec tbs . posixSecondsToUTCTime $ 4)
        True
        [ (Just . posixSecondsToUTCTime $ 5, Nothing, True)
        , (Just . posixSecondsToUTCTime $ 6, Nothing, True)
        , (Just . posixSecondsToUTCTime $ 3, Nothing, True)
        ]
      , testAlONCase "test afterTime" (\e -> do
                                          tbs <- utc2TimeBits <$> holdDyn (posixSecondsToUTCTime 0) e
                                          afterTime tbs . posixSecondsToUTCTime $ 4)
        False
        [ (Nothing, Nothing, False)
        , (Just . posixSecondsToUTCTime $ 1, Nothing, False)
        , (Just . posixSecondsToUTCTime $ 4, Just True, True)
        , (Just . posixSecondsToUTCTime $ 5, Nothing, True)
        , (Just . posixSecondsToUTCTime $ 6, Nothing, True)
        , (Just . posixSecondsToUTCTime $ 3, Nothing, True)
        ]
      , testAlONCase "test afterTime skipping" (\e -> do
                                          tbs <- utc2TimeBits <$> holdDyn (posixSecondsToUTCTime 0) e
                                          afterTime tbs . posixSecondsToUTCTime $ 4)
        False
        [ (Nothing, Nothing, False)
        , (Just . posixSecondsToUTCTime $ 1, Nothing, False)
        , (Just . posixSecondsToUTCTime $ 5, Just True, True)
        , (Just . posixSecondsToUTCTime $ 6, Nothing, True)
        , (Just . posixSecondsToUTCTime $ 3, Nothing, True)
        ]
      , testAlONCase "test afterTimeSpec starting after" (\e -> do
                                          tbs <- utc2TimeBits <$> holdDyn (posixSecondsToUTCTime 5) e
                                          afterTime tbs . posixSecondsToUTCTime $ 4)
        True
        [ (Just . posixSecondsToUTCTime $ 5, Nothing, True)
        , (Just . posixSecondsToUTCTime $ 6, Nothing, True)
        , (Just . posixSecondsToUTCTime $ 3, Nothing, True)
        ]
      ]
