-- | Value type system for Subject properties.
--
-- This module defines the `Value` type that supports all gram notation value types,
-- including standard types (integers, decimals, booleans, strings, symbols) and
-- extended types (tagged strings, arrays, maps, ranges, measurements).
--
-- The Value type enables property records to store rich, structured data that
-- matches the gram notation specification.
--
-- == Value Types
--
-- The Value type supports the following value types from gram notation:
--
-- * Standard types:
--   - Integers: @VInteger Integer@
--   - Decimals: @VDecimal Double@
--   - Booleans: @VBoolean Bool@
--   - Strings: @VString String@
--   - Symbols: @VSymbol String@
--
-- * Extended types:
--   - Tagged strings: @VTaggedString String String@ (tag and content)
--   - Arrays: @VArray [Value]@ (lists of values)
--   - Maps: @VMap (Map String Value)@ (key-value maps)
--   - Ranges: @VRange RangeValue@ (numeric ranges)
--   - Measurements: @VMeasurement String Double@ (e.g., "5kg" -> ("kg", 5.0))
--
-- == Examples
--
-- Standard value types:
--
-- >>> VInteger 42
-- >>> VDecimal 3.14
-- >>> VBoolean True
-- >>> VString "hello"
-- >>> VSymbol "mySymbol"
--
-- Extended value types:
--
-- >>> VTaggedString "url" "https://example.com"
-- >>> VArray [VInteger 1, VInteger 2, VInteger 3]
-- >>> VMap (fromList [("key1", VString "value1"), ("key2", VInteger 42)])
-- >>> VRange (RangeValue (Just 1) (Just 10))
-- >>> VMeasurement "kg" 5.0
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}

module Subject.Value
  ( Value (..)
  , RangeValue (..)
  ) where

import Data.Hashable (Hashable (..))
import Data.Map (Map)
import GHC.Generics (Generic)

-- | A range value representing numeric ranges.
--
-- Ranges can be:
-- * Closed: both lower and upper bounds specified
-- * Half-open: only lower or upper bound specified
-- * Open: neither bound specified (represents all numbers)
--
-- Examples:
--
-- >>> RangeValue (Just 1) (Just 10)  -- 1..10 (closed range)
-- >>> RangeValue (Just 1) Nothing    -- 1... (lower bound only)
-- >>> RangeValue Nothing (Just 10)   -- ...10 (upper bound only)
-- >>> RangeValue Nothing Nothing      -- ... (all numbers)
data RangeValue = RangeValue
  { RangeValue -> Maybe Double
lower :: Maybe Double  -- ^ Lower bound of the range (inclusive)
  , RangeValue -> Maybe Double
upper :: Maybe Double  -- ^ Upper bound of the range (inclusive)
  }
  deriving (RangeValue -> RangeValue -> Bool
(RangeValue -> RangeValue -> Bool)
-> (RangeValue -> RangeValue -> Bool) -> Eq RangeValue
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RangeValue -> RangeValue -> Bool
== :: RangeValue -> RangeValue -> Bool
$c/= :: RangeValue -> RangeValue -> Bool
/= :: RangeValue -> RangeValue -> Bool
Eq, Eq RangeValue
Eq RangeValue =>
(RangeValue -> RangeValue -> Ordering)
-> (RangeValue -> RangeValue -> Bool)
-> (RangeValue -> RangeValue -> Bool)
-> (RangeValue -> RangeValue -> Bool)
-> (RangeValue -> RangeValue -> Bool)
-> (RangeValue -> RangeValue -> RangeValue)
-> (RangeValue -> RangeValue -> RangeValue)
-> Ord RangeValue
RangeValue -> RangeValue -> Bool
RangeValue -> RangeValue -> Ordering
RangeValue -> RangeValue -> RangeValue
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: RangeValue -> RangeValue -> Ordering
compare :: RangeValue -> RangeValue -> Ordering
$c< :: RangeValue -> RangeValue -> Bool
< :: RangeValue -> RangeValue -> Bool
$c<= :: RangeValue -> RangeValue -> Bool
<= :: RangeValue -> RangeValue -> Bool
$c> :: RangeValue -> RangeValue -> Bool
> :: RangeValue -> RangeValue -> Bool
$c>= :: RangeValue -> RangeValue -> Bool
>= :: RangeValue -> RangeValue -> Bool
$cmax :: RangeValue -> RangeValue -> RangeValue
max :: RangeValue -> RangeValue -> RangeValue
$cmin :: RangeValue -> RangeValue -> RangeValue
min :: RangeValue -> RangeValue -> RangeValue
Ord, Int -> RangeValue -> ShowS
[RangeValue] -> ShowS
RangeValue -> String
(Int -> RangeValue -> ShowS)
-> (RangeValue -> String)
-> ([RangeValue] -> ShowS)
-> Show RangeValue
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RangeValue -> ShowS
showsPrec :: Int -> RangeValue -> ShowS
$cshow :: RangeValue -> String
show :: RangeValue -> String
$cshowList :: [RangeValue] -> ShowS
showList :: [RangeValue] -> ShowS
Show, (forall x. RangeValue -> Rep RangeValue x)
-> (forall x. Rep RangeValue x -> RangeValue) -> Generic RangeValue
forall x. Rep RangeValue x -> RangeValue
forall x. RangeValue -> Rep RangeValue x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. RangeValue -> Rep RangeValue x
from :: forall x. RangeValue -> Rep RangeValue x
$cto :: forall x. Rep RangeValue x -> RangeValue
to :: forall x. Rep RangeValue x -> RangeValue
Generic, Eq RangeValue
Eq RangeValue =>
(Int -> RangeValue -> Int)
-> (RangeValue -> Int) -> Hashable RangeValue
Int -> RangeValue -> Int
RangeValue -> Int
forall a. Eq a => (Int -> a -> Int) -> (a -> Int) -> Hashable a
$chashWithSalt :: Int -> RangeValue -> Int
hashWithSalt :: Int -> RangeValue -> Int
$chash :: RangeValue -> Int
hash :: RangeValue -> Int
Hashable)

-- | A value that can be stored in a Subject property record.
--
-- The Value type supports all gram notation value types, enabling property
-- records to store rich, structured data. Values can be nested (arrays and
-- maps can contain other values), enabling complex data structures.
--
-- === Standard Types
--
-- Standard value types represent basic data:
--
-- * @VInteger Integer@ - Integer numbers (e.g., @42@, @-10@)
-- * @VDecimal Double@ - Decimal numbers (e.g., @3.14@, @-0.5@)
-- * @VBoolean Bool@ - Boolean values (@True@ or @False@)
-- * @VString String@ - String literals (e.g., @\"hello\"@)
-- * @VSymbol String@ - Symbol identifiers (e.g., @mySymbol@)
--
-- === Extended Types
--
-- Extended value types represent structured or qualified data:
--
-- * @VTaggedString String String@ - Tagged strings with a type tag and content
--   (e.g., @url`https://example.com`@ becomes @VTaggedString "url" "https://example.com"@)
-- * @VArray [Value]@ - Arrays of values (e.g., @[1, 2, 3]@)
-- * @VMap (Map String Value)@ - Key-value maps (e.g., @{key: "value"}@)
-- * @VRange RangeValue@ - Numeric ranges (e.g., @1..10@, @1...@, @...10@)
-- * @VMeasurement String Double@ - Measurements with units (e.g., @5kg@ becomes @VMeasurement "kg" 5.0@)
--
-- === Nested Values
--
-- Values can be nested to create complex structures:
--
-- >>> VArray [VString "a", VString "b", VString "c"]
-- >>> VMap (fromList [("nested", VArray [VInteger 1, VInteger 2])])
--
-- === Examples
--
-- Creating values:
--
-- >>> VInteger 42
-- >>> VDecimal 3.14
-- >>> VBoolean True
-- >>> VString "hello"
-- >>> VSymbol "mySymbol"
-- >>> VTaggedString "url" "https://example.com"
-- >>> VArray [VInteger 1, VInteger 2, VInteger 3]
-- >>> VMap (fromList [("name", VString "Alice"), ("age", VInteger 30)])
-- >>> VRange (RangeValue (Just 1) (Just 10))
-- >>> VMeasurement "kg" 5.0
data Value
  = VInteger Integer
  | VDecimal Double
  | VBoolean Bool
  | VString String
  | VSymbol String
  | VTaggedString String String  -- ^ Tag and content
  | VArray [Value]
  | VMap (Map String Value)
  | VRange RangeValue
  | VMeasurement String Double   -- ^ Unit and numeric value (e.g., "kg" and 5.0 for "5kg")
  deriving (Value -> Value -> Bool
(Value -> Value -> Bool) -> (Value -> Value -> Bool) -> Eq Value
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Value -> Value -> Bool
== :: Value -> Value -> Bool
$c/= :: Value -> Value -> Bool
/= :: Value -> Value -> Bool
Eq, Eq Value
Eq Value =>
(Value -> Value -> Ordering)
-> (Value -> Value -> Bool)
-> (Value -> Value -> Bool)
-> (Value -> Value -> Bool)
-> (Value -> Value -> Bool)
-> (Value -> Value -> Value)
-> (Value -> Value -> Value)
-> Ord Value
Value -> Value -> Bool
Value -> Value -> Ordering
Value -> Value -> Value
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Value -> Value -> Ordering
compare :: Value -> Value -> Ordering
$c< :: Value -> Value -> Bool
< :: Value -> Value -> Bool
$c<= :: Value -> Value -> Bool
<= :: Value -> Value -> Bool
$c> :: Value -> Value -> Bool
> :: Value -> Value -> Bool
$c>= :: Value -> Value -> Bool
>= :: Value -> Value -> Bool
$cmax :: Value -> Value -> Value
max :: Value -> Value -> Value
$cmin :: Value -> Value -> Value
min :: Value -> Value -> Value
Ord, Int -> Value -> ShowS
[Value] -> ShowS
Value -> String
(Int -> Value -> ShowS)
-> (Value -> String) -> ([Value] -> ShowS) -> Show Value
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Value -> ShowS
showsPrec :: Int -> Value -> ShowS
$cshow :: Value -> String
show :: Value -> String
$cshowList :: [Value] -> ShowS
showList :: [Value] -> ShowS
Show, (forall x. Value -> Rep Value x)
-> (forall x. Rep Value x -> Value) -> Generic Value
forall x. Rep Value x -> Value
forall x. Value -> Rep Value x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Value -> Rep Value x
from :: forall x. Value -> Rep Value x
$cto :: forall x. Rep Value x -> Value
to :: forall x. Rep Value x -> Value
Generic, Eq Value
Eq Value =>
(Int -> Value -> Int) -> (Value -> Int) -> Hashable Value
Int -> Value -> Int
Value -> Int
forall a. Eq a => (Int -> a -> Int) -> (a -> Int) -> Hashable a
$chashWithSalt :: Int -> Value -> Int
hashWithSalt :: Int -> Value -> Int
$chash :: Value -> Int
hash :: Value -> Int
Hashable)