content: {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE RecordWildCards #-}
  {-# LANGUAGE UndecidableInstances #-}
  
  -- |
  -- Module      :  Data.World
  -- Copyright   :  (c) Arista Networks, 2022-2023
  -- License     :  Apache License 2.0, see LICENSE
  --
  -- Stability   :  stable
  -- Portability :  non-portable (GHC extensions)
  --
  -- Representation of model states; the \"world\" a model lives in.
  --
  -- @since 1.0.0
  module Data.World
    ( -- * Worlds
      World (World),
  
      -- ** Construction
      makeWorld,
  
      -- ** Lenses
      fingerprint,
      worldValues,
  
      -- ** Pretty Printing
      ppWorldListed,
    )
  where
  
  import Data.Hashable (Hashable (hashWithSalt))
  import Lens.Micro (Lens', SimpleGetter, lens, to)
  import Prettyprinter (Doc, annotate, indent, pretty)
  import Prettyprinter.Render.Terminal (AnsiStyle, Color (White, Yellow), color, colorDull)
  
  import Data.Fingerprint (Fingerprint (Fingerprint), fingerprintRec)
  import Data.Type.Rec (HasDict, Rec, ppRecListed)
  
  -- ---------------------------------------------------------------------------------------------------------------------
  
  -- | The 'World' data type is a 'Rec', which is used to represent the concrete values of a model's state, paired with
  -- it's 'Fingerprint' which has much faster preformance charateristics for comparison.
  --
  -- @since 1.0.0
  data World ctx = World
    { _worldFingerprint :: {-# UNPACK #-} !Fingerprint
    , _worldValues :: Rec ctx
    }
  
  -- | @since 1.0.0
  instance Eq (World ctx) where
    World fp1 _ == World fp2 _ = fp1 == fp2
    {-# INLINE (==) #-}
  
  -- | @since 1.0.0
  instance Ord (World ctx) where
    World fp1 _ `compare` World fp2 _ = fp1 `compare` fp2
    {-# INLINE compare #-}
  
  -- | @since 1.0.0
  instance Show (Rec ctx) => Show (World ctx) where
    show (World fp w) = "<<" ++ show fp ++ ":" ++ show w ++ ">>"
    {-# INLINE show #-}
  
  -- | @since 1.0.0
  instance Hashable (World ctx) where
    hashWithSalt salt (World (Fingerprint fp) _) = hashWithSalt salt fp
    {-# INLINE hashWithSalt #-}
  
  -- | Constructs a 'World' type from the given 'Rec'.
  --
  -- @since 1.0.0
  makeWorld :: Hashable (Rec ctx) => Rec ctx -> World ctx
  makeWorld w = World (fingerprintRec w) w
  
  -- | Lens focusing on a 'World's fingerprint.
  --
  -- @since 1.0.0
  fingerprint :: Lens' (World ctx) Fingerprint
  fingerprint = lens _worldFingerprint \World {..} x -> World {_worldFingerprint = x, ..}
  
  -- | Lens focusing on the 'Rec' holding the concrete values of a 'World'.
  --
  -- @since 1.0.0
  worldValues :: SimpleGetter (World ctx) (Rec ctx)
  worldValues = to _worldValues
  
  -- | @'ppWorldListed' world@ lays out a list of documents for each state variable in @world@ using the world fingerprint
  -- as a header.
  --
  -- @since 1.0.0
  ppWorldListed :: HasDict Show ctx => World ctx -> [Doc AnsiStyle]
  ppWorldListed (World hash values) =
    let hashDoc = annotate (colorDull Yellow) (pretty hash)
        fieldDocs = annotate (color White) <$> ppRecListed values
     in hashDoc : map (indent 2) fieldDocs
  