content: {-# LANGUAGE GADTs #-}
  
  {- |
  Module                  : Toml.Type.UValue
  Copyright               : (c) 2018-2022 Kowainik
  SPDX-License-Identifier : MPL-2.0
  Maintainer              : Kowainik <xrom.xkov@gmail.com>
  Stability               : Stable
  Portability             : Portable
  
  Intermediate untype value representation used for parsing.
  
  @since 0.0.0
  -}
  
  module Toml.Type.UValue
         ( UValue (..)
         , typeCheck
         ) where
  
  import Data.Text (Text)
  import Data.Time (Day, LocalTime, TimeOfDay, ZonedTime, zonedTimeToUTC)
  import Data.Type.Equality ((:~:) (..))
  
  import Toml.Type.AnyValue (AnyValue (..))
  import Toml.Type.Value (TypeMismatchError, Value (..), sameValue)
  
  
  {- | Untyped value of @TOML@. You shouldn't use this type in your
  code. Use 'Value' instead.
  
  @since 0.0.0
  -}
  data UValue
      = UBool !Bool
      | UInteger !Integer
      | UDouble !Double
      | UText !Text
      | UZoned !ZonedTime
      | ULocal !LocalTime
      | UDay !Day
      | UHours !TimeOfDay
      | UArray ![UValue]
      deriving stock (Show)
  
  -- | @since 0.0.0
  instance Eq UValue where
      (UBool b1)    == (UBool b2)    = b1 == b2
      (UInteger i1) == (UInteger i2) = i1 == i2
      (UDouble f1)  == (UDouble f2)
          | isNaN f1 && isNaN f2 = True
          | otherwise = f1 == f2
      (UText s1)    == (UText s2)    = s1 == s2
      (UZoned a)    == (UZoned b)    = zonedTimeToUTC a == zonedTimeToUTC b
      (ULocal a)    == (ULocal b)    = a == b
      (UDay a)      == (UDay b)      = a == b
      (UHours a)    == (UHours b)    = a == b
      (UArray a1)   == (UArray a2)   = a1 == a2
      _             == _             = False
  
  {- | Ensures that 'UValue's represents type-safe version of @toml@.
  
  @since 0.0.0
  -}
  typeCheck :: UValue -> Either TypeMismatchError AnyValue
  typeCheck (UBool b)    = rightAny $ Bool b
  typeCheck (UInteger n) = rightAny $ Integer n
  typeCheck (UDouble f)  = rightAny $ Double f
  typeCheck (UText s)    = rightAny $ Text s
  typeCheck (UZoned d)   = rightAny $ Zoned d
  typeCheck (ULocal d)   = rightAny $ Local d
  typeCheck (UDay d)     = rightAny $ Day d
  typeCheck (UHours d)   = rightAny $ Hours d
  typeCheck (UArray a)   = case a of
      []   -> rightAny $ Array []
      x:xs -> do
          AnyValue v <- typeCheck x
          AnyValue . Array <$> checkElem v xs
    where
      checkElem :: Value t -> [UValue] -> Either TypeMismatchError [Value t]
      checkElem v []     = Right [v]
      checkElem v (x:xs) = do
          AnyValue vx <- typeCheck x
          Refl <- sameValue v vx
          (v :) <$> checkElem vx xs
  
  rightAny :: Value t -> Either l AnyValue
  rightAny = Right . AnyValue
  