{-# OPTIONS --cubical-compatible --guardedness #-}
module System.Clock where
open import Level using (Level; 0ℓ; Lift; lower)
open import Data.Bool.Base using (if_then_else_)
open import Data.Fin.Base using (Fin; toℕ)
open import Data.Nat.Base as ℕ using (ℕ; zero; suc; _+_; _∸_; _^_; _<ᵇ_)
import Data.Nat.Show as ℕ using (show)
open import Data.Nat.DivMod using (_/_)
import Data.Nat.Properties as ℕₚ
open import Data.String.Base using (String; _++_; padLeft)
open import IO.Base
open import Function.Base using (_$_; _∘′_)
open import Foreign.Haskell using (_,_)
open import Relation.Nullary.Decidable using (False; fromWitnessFalse; toWitnessFalse)
private
  variable
    a : Level
    A : Set a
open import System.Clock.Primitive as Prim
  public
  using ( Clock
        
        ; monotonic
        
        ; realTime
        
        ; processCPUTime
        
        ; threadCPUTime
        
        ; monotonicRaw
        
        
        ; bootTime
        
        ; monotonicCoarse
        
        ; realTimeCoarse
        )
record Time : Set where
  constructor mkTime
  field seconds     : ℕ
        nanoseconds : ℕ
open Time public
getTime : Clock → IO Time
getTime c = do
  (a , b) ← lift (Prim.getTime c)
  pure $ mkTime a b
diff : Time → Time → Time
diff (mkTime ss sns) (mkTime es ens) =
  if ens <ᵇ sns
  then mkTime (es ∸ suc ss) ((1000000000 + ens) ∸ sns)
  else mkTime (es ∸ ss) (ens ∸ sns)
record Timed (A : Set a) : Set a where
  constructor mkTimed
  field value : A
        time  : Time
time : IO A → IO (Timed A)
time io = do
  start ← lift! $ getTime realTime
  a     ← io
  end   ← lift! $ getTime realTime
  pure $ mkTimed a $ diff (lower start) (lower end)
time′ : IO {0ℓ} A → IO Time
time′ io = Timed.time <$> time io
show : Time →   
       Fin 10 → 
                
       String
show (mkTime s ns) prec = secs ++ "s" ++ padLeft '0' decimals nsecs where
  decimals = toℕ prec
  secs     = ℕ.show s
  prf      = ℕₚ.m^n≢0 10 (9 ∸ decimals)
  nsecs    = ℕ.show ((ns / (10 ^ (9 ∸ decimals))) {{prf}})