module Language.Fluent.Value where

import Data.List.NonEmpty (NonEmpty)
import Data.Scientific (Scientific, fromFloatDigits)
import Data.String (IsString (..))
import Data.Text (Text)
import Data.Text qualified as Text
import Data.Time (UTCTime)
import Language.Fluent.Locale (Locale (..))
import Language.Fluent.Number (NumberOptions, numberOptions)
import Language.Fluent.Time (TimeOptions, timeOptions)
import Prelude

data CustomValue = forall v. (Value v) => CustomValue v

class Value v where
    value :: v -> SomeValue
    format :: (Locale l) => NonEmpty l -> v -> Either String Text
    format NonEmpty l
locale = NonEmpty l -> SomeValue -> Either String Text
forall l. Locale l => NonEmpty l -> SomeValue -> Either String Text
forall v l.
(Value v, Locale l) =>
NonEmpty l -> v -> Either String Text
format NonEmpty l
locale (SomeValue -> Either String Text)
-> (v -> SomeValue) -> v -> Either String Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. v -> SomeValue
forall v. Value v => v -> SomeValue
value

data SomeValue
    = StringValue Text
    | NumberValue Scientific NumberOptions
    | TimeValue UTCTime TimeOptions
    | SomeValue CustomValue

instance Value Text where
    value :: Text -> SomeValue
value = Text -> SomeValue
StringValue
    format :: forall l. Locale l => NonEmpty l -> Text -> Either String Text
format NonEmpty l
_ = Text -> Either String Text
forall a b. b -> Either a b
Right

instance Value String where value :: String -> SomeValue
value = Text -> SomeValue
StringValue (Text -> SomeValue) -> (String -> Text) -> String -> SomeValue
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
Text.pack

instance Value Scientific where value :: Scientific -> SomeValue
value = (Scientific -> NumberOptions -> SomeValue)
-> NumberOptions -> Scientific -> SomeValue
forall a b c. (a -> b -> c) -> b -> a -> c
flip Scientific -> NumberOptions -> SomeValue
NumberValue NumberOptions
numberOptions

instance Value Int where value :: Int -> SomeValue
value = Scientific -> SomeValue
forall v. Value v => v -> SomeValue
value (Scientific -> SomeValue)
-> (Int -> Scientific) -> Int -> SomeValue
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (Integral a, Num b) => a -> b
fromIntegral @Int @Scientific

instance Value Integer where value :: Integer -> SomeValue
value = Scientific -> SomeValue
forall v. Value v => v -> SomeValue
value (Scientific -> SomeValue)
-> (Integer -> Scientific) -> Integer -> SomeValue
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (Integral a, Num b) => a -> b
fromIntegral @Integer @Scientific

instance Value Word where value :: Word -> SomeValue
value = Scientific -> SomeValue
forall v. Value v => v -> SomeValue
value (Scientific -> SomeValue)
-> (Word -> Scientific) -> Word -> SomeValue
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (Integral a, Num b) => a -> b
fromIntegral @Word @Scientific

instance Value Double where value :: Double -> SomeValue
value = Scientific -> SomeValue
forall v. Value v => v -> SomeValue
value (Scientific -> SomeValue)
-> (Double -> Scientific) -> Double -> SomeValue
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Double -> Scientific
forall a. RealFloat a => a -> Scientific
fromFloatDigits

instance Value UTCTime where value :: UTCTime -> SomeValue
value = (UTCTime -> TimeOptions -> SomeValue)
-> TimeOptions -> UTCTime -> SomeValue
forall a b c. (a -> b -> c) -> b -> a -> c
flip UTCTime -> TimeOptions -> SomeValue
TimeValue TimeOptions
timeOptions

instance Value CustomValue where
    value :: CustomValue -> SomeValue
value = CustomValue -> SomeValue
SomeValue

    format :: forall l.
Locale l =>
NonEmpty l -> CustomValue -> Either String Text
format NonEmpty l
locale (CustomValue v
v) = NonEmpty l -> v -> Either String Text
forall l. Locale l => NonEmpty l -> v -> Either String Text
forall v l.
(Value v, Locale l) =>
NonEmpty l -> v -> Either String Text
format NonEmpty l
locale v
v

instance IsString SomeValue where fromString :: String -> SomeValue
fromString = String -> SomeValue
forall v. Value v => v -> SomeValue
value

instance Show SomeValue where
    show :: SomeValue -> String
show (StringValue Text
t) = String
"StringValue " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Text -> String
Text.unpack Text
t
    show (NumberValue Scientific
n NumberOptions
_) = String
"NumberValue " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Scientific -> String
forall a. Show a => a -> String
show Scientific
n
    show (TimeValue UTCTime
t TimeOptions
_) = String
"TimeValue " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> UTCTime -> String
forall a. Show a => a -> String
show UTCTime
t
    show SomeValue{} = String
"SomeValue"

instance Value SomeValue where
    value :: SomeValue -> SomeValue
value = SomeValue -> SomeValue
forall a. a -> a
id
    format :: forall l. Locale l => NonEmpty l -> SomeValue -> Either String Text
format NonEmpty l
locales (StringValue Text
s) = NonEmpty l -> Text -> Either String Text
forall l. Locale l => NonEmpty l -> Text -> Either String Text
forall v l.
(Value v, Locale l) =>
NonEmpty l -> v -> Either String Text
format NonEmpty l
locales Text
s
    format NonEmpty l
locales (NumberValue Scientific
n NumberOptions
o) = NonEmpty l -> Scientific -> NumberOptions -> Either String Text
forall l.
Locale l =>
NonEmpty l -> Scientific -> NumberOptions -> Either String Text
formatNumber NonEmpty l
locales Scientific
n NumberOptions
o
    format NonEmpty l
locales (TimeValue UTCTime
t TimeOptions
o) = NonEmpty l -> UTCTime -> TimeOptions -> Either String Text
forall l.
Locale l =>
NonEmpty l -> UTCTime -> TimeOptions -> Either String Text
formatTime NonEmpty l
locales UTCTime
t TimeOptions
o
    format NonEmpty l
locales (SomeValue CustomValue
v) = NonEmpty l -> CustomValue -> Either String Text
forall l.
Locale l =>
NonEmpty l -> CustomValue -> Either String Text
forall v l.
(Value v, Locale l) =>
NonEmpty l -> v -> Either String Text
format NonEmpty l
locales CustomValue
v