-- | This module defines pretty-printing options for
-- 'Cooked.Pretty.Class.prettyCookedOpt' and their default values.
module Cooked.Pretty.Options
  ( PrettyCookedOpts (..),
    PrettyCookedHashOpts (..),
    PCOptTxOutRefs (..),
    hashNamesFromList,
    defaultHashNames,
    addHashNames,
  )
where

import Cooked.Pretty.Hashable
import Cooked.Wallet
import Data.Bifunctor (first)
import Data.Default
import Data.Map (Map)
import Data.Map qualified as Map
import Plutus.Script.Utils.Scripts qualified as Script
import Plutus.Script.Utils.V1.Generators qualified as ScriptV1
import Plutus.Script.Utils.V2.Generators qualified as ScriptV2
import Plutus.Script.Utils.V3.Generators qualified as ScriptV3
import PlutusLedgerApi.V3 qualified as Api

-- | A set of option to pilot pretty printing in cooked-validators
data PrettyCookedOpts = PrettyCookedOpts
  { -- | Whether to print transaction ids of validated transactions. By
    -- default: False
    PrettyCookedOpts -> Bool
pcOptPrintTxHashes :: Bool,
    -- | Whether to print transaction outputs references. By default: hidden
    PrettyCookedOpts -> PCOptTxOutRefs
pcOptPrintTxOutRefs :: PCOptTxOutRefs,
    -- | Whether to print tx options that have not been modified from their
    -- default. By default: False
    PrettyCookedOpts -> Bool
pcOptPrintDefaultTxOpts :: Bool,
    -- | Whether to print big integers with numeric underscores. For example
    -- @53_000_000@ instead of @53000000@. By default: True
    PrettyCookedOpts -> Bool
pcOptNumericUnderscores :: Bool,
    -- | Options related to printing hashes
    PrettyCookedOpts -> PrettyCookedHashOpts
pcOptHashes :: PrettyCookedHashOpts,
    -- | Whether to display the log
    PrettyCookedOpts -> Bool
pcOptPrintLog :: Bool
  }
  deriving (PrettyCookedOpts -> PrettyCookedOpts -> Bool
(PrettyCookedOpts -> PrettyCookedOpts -> Bool)
-> (PrettyCookedOpts -> PrettyCookedOpts -> Bool)
-> Eq PrettyCookedOpts
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PrettyCookedOpts -> PrettyCookedOpts -> Bool
== :: PrettyCookedOpts -> PrettyCookedOpts -> Bool
$c/= :: PrettyCookedOpts -> PrettyCookedOpts -> Bool
/= :: PrettyCookedOpts -> PrettyCookedOpts -> Bool
Eq, Int -> PrettyCookedOpts -> ShowS
[PrettyCookedOpts] -> ShowS
PrettyCookedOpts -> String
(Int -> PrettyCookedOpts -> ShowS)
-> (PrettyCookedOpts -> String)
-> ([PrettyCookedOpts] -> ShowS)
-> Show PrettyCookedOpts
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PrettyCookedOpts -> ShowS
showsPrec :: Int -> PrettyCookedOpts -> ShowS
$cshow :: PrettyCookedOpts -> String
show :: PrettyCookedOpts -> String
$cshowList :: [PrettyCookedOpts] -> ShowS
showList :: [PrettyCookedOpts] -> ShowS
Show)

instance Default PrettyCookedOpts where
  def :: PrettyCookedOpts
def =
    PrettyCookedOpts
      { pcOptPrintTxHashes :: Bool
pcOptPrintTxHashes = Bool
False,
        pcOptPrintTxOutRefs :: PCOptTxOutRefs
pcOptPrintTxOutRefs = PCOptTxOutRefs
PCOptTxOutRefsHidden,
        pcOptPrintDefaultTxOpts :: Bool
pcOptPrintDefaultTxOpts = Bool
False,
        pcOptNumericUnderscores :: Bool
pcOptNumericUnderscores = Bool
True,
        pcOptHashes :: PrettyCookedHashOpts
pcOptHashes = PrettyCookedHashOpts
forall a. Default a => a
def,
        pcOptPrintLog :: Bool
pcOptPrintLog = Bool
True
      }

-- | Whether to print transaction outputs references.
data PCOptTxOutRefs
  = -- | Hide them
    PCOptTxOutRefsHidden
  | -- | Always show them.
    --
    -- Warning: this will disable printing similar UTxOs as a group (for
    -- instance @(×10) Lovelace: 100_000_000@)
    PCOptTxOutRefsFull
  | -- | Show them for UTxOs which are not grouped with similar others. This
    -- avoids the downside of 'PCOptTxOutRefsFull' which disables printing UTxOs
    -- as a group.
    PCOptTxOutRefsPartial
  deriving (PCOptTxOutRefs -> PCOptTxOutRefs -> Bool
(PCOptTxOutRefs -> PCOptTxOutRefs -> Bool)
-> (PCOptTxOutRefs -> PCOptTxOutRefs -> Bool) -> Eq PCOptTxOutRefs
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PCOptTxOutRefs -> PCOptTxOutRefs -> Bool
== :: PCOptTxOutRefs -> PCOptTxOutRefs -> Bool
$c/= :: PCOptTxOutRefs -> PCOptTxOutRefs -> Bool
/= :: PCOptTxOutRefs -> PCOptTxOutRefs -> Bool
Eq, Int -> PCOptTxOutRefs -> ShowS
[PCOptTxOutRefs] -> ShowS
PCOptTxOutRefs -> String
(Int -> PCOptTxOutRefs -> ShowS)
-> (PCOptTxOutRefs -> String)
-> ([PCOptTxOutRefs] -> ShowS)
-> Show PCOptTxOutRefs
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PCOptTxOutRefs -> ShowS
showsPrec :: Int -> PCOptTxOutRefs -> ShowS
$cshow :: PCOptTxOutRefs -> String
show :: PCOptTxOutRefs -> String
$cshowList :: [PCOptTxOutRefs] -> ShowS
showList :: [PCOptTxOutRefs] -> ShowS
Show)

-- | A set of options to pilot how hashes are pretty printed
data PrettyCookedHashOpts = PrettyCookedHashOpts
  { -- | Length of printed hash prefix. By default: 7
    PrettyCookedHashOpts -> Int
pcOptHashLength :: Int,
    -- | Association between hashes and given names to ease readability.  For
    -- example @Map.singleton (walletPKHash (wallet 1)) "Alice"@ By default:
    -- "defaultHashNames" which assigns Lovelace, Quick, and Permanent as names
    -- for the associated currency symbols
    PrettyCookedHashOpts -> Map BuiltinByteString String
pcOptHashNames :: Map Api.BuiltinByteString String,
    -- | When a given name exists for a hash, this flag also prints the original
    -- hash after the name. By default: @False@
    PrettyCookedHashOpts -> Bool
pcOptHashVerbose :: Bool
  }
  deriving (PrettyCookedHashOpts -> PrettyCookedHashOpts -> Bool
(PrettyCookedHashOpts -> PrettyCookedHashOpts -> Bool)
-> (PrettyCookedHashOpts -> PrettyCookedHashOpts -> Bool)
-> Eq PrettyCookedHashOpts
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PrettyCookedHashOpts -> PrettyCookedHashOpts -> Bool
== :: PrettyCookedHashOpts -> PrettyCookedHashOpts -> Bool
$c/= :: PrettyCookedHashOpts -> PrettyCookedHashOpts -> Bool
/= :: PrettyCookedHashOpts -> PrettyCookedHashOpts -> Bool
Eq, Int -> PrettyCookedHashOpts -> ShowS
[PrettyCookedHashOpts] -> ShowS
PrettyCookedHashOpts -> String
(Int -> PrettyCookedHashOpts -> ShowS)
-> (PrettyCookedHashOpts -> String)
-> ([PrettyCookedHashOpts] -> ShowS)
-> Show PrettyCookedHashOpts
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PrettyCookedHashOpts -> ShowS
showsPrec :: Int -> PrettyCookedHashOpts -> ShowS
$cshow :: PrettyCookedHashOpts -> String
show :: PrettyCookedHashOpts -> String
$cshowList :: [PrettyCookedHashOpts] -> ShowS
showList :: [PrettyCookedHashOpts] -> ShowS
Show)

instance Default PrettyCookedHashOpts where
  def :: PrettyCookedHashOpts
def =
    PrettyCookedHashOpts
      { pcOptHashLength :: Int
pcOptHashLength = Int
7,
        pcOptHashNames :: Map BuiltinByteString String
pcOptHashNames = Map BuiltinByteString String
defaultHashNames,
        pcOptHashVerbose :: Bool
pcOptHashVerbose = Bool
False
      }

-- | Default hash to names map that assigns Lovelace, Quick, and Permanent to
-- the associated currency symbols. This is used as the default for the
-- pretty-printing option and is recommended to use as a basis to extend with
-- custom names.
defaultHashNames :: Map Api.BuiltinByteString String
defaultHashNames :: Map BuiltinByteString String
defaultHashNames =
  [(CurrencySymbol, String)] -> Map BuiltinByteString String
forall a. ToHash a => [(a, String)] -> Map BuiltinByteString String
hashNamesFromList
    [ (BuiltinByteString -> CurrencySymbol
Api.CurrencySymbol BuiltinByteString
"", String
"Lovelace"),
      (CurrencySymbol
ScriptV1.alwaysSucceedCurrencySymbol, String
"QuickV1"),
      (CurrencySymbol
ScriptV2.alwaysSucceedCurrencySymbol, String
"QuickV2"),
      (MultiPurposeScript Any -> CurrencySymbol
forall script.
ToMintingPolicyHash script =>
script -> CurrencySymbol
Script.toCurrencySymbol MultiPurposeScript Any
forall {k} (a :: k). MultiPurposeScript a
ScriptV3.trueMintingMPScript, String
"QuickV3"),
      (CurrencySymbol
ScriptV1.alwaysFailCurrencySymbol, String
"PermanentV1"),
      (CurrencySymbol
ScriptV2.alwaysFailCurrencySymbol, String
"PermanentV2"),
      (MultiPurposeScript Any -> CurrencySymbol
forall script.
ToMintingPolicyHash script =>
script -> CurrencySymbol
Script.toCurrencySymbol MultiPurposeScript Any
forall {k} (a :: k). MultiPurposeScript a
ScriptV3.falseMPScript, String
"PermanentV3")
    ]
    Map BuiltinByteString String
-> Map BuiltinByteString String -> Map BuiltinByteString String
forall a. Semigroup a => a -> a -> a
<> [(Wallet, String)] -> Map BuiltinByteString String
forall a. ToHash a => [(a, String)] -> Map BuiltinByteString String
hashNamesFromList
      ((\Integer
i -> (Integer -> Wallet
wallet Integer
i, String
"wallet " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Integer -> String
forall a. Show a => a -> String
show Integer
i)) (Integer -> (Wallet, String)) -> [Integer] -> [(Wallet, String)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Integer
1 .. Integer
10])

-- | Smart constructor for maps to be used in the "pcOptHashNames"
-- pretty-printing option.
hashNamesFromList :: (ToHash a) => [(a, String)] -> Map Api.BuiltinByteString String
hashNamesFromList :: forall a. ToHash a => [(a, String)] -> Map BuiltinByteString String
hashNamesFromList = [(BuiltinByteString, String)] -> Map BuiltinByteString String
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(BuiltinByteString, String)] -> Map BuiltinByteString String)
-> ([(a, String)] -> [(BuiltinByteString, String)])
-> [(a, String)]
-> Map BuiltinByteString String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((a, String) -> (BuiltinByteString, String))
-> [(a, String)] -> [(BuiltinByteString, String)]
forall a b. (a -> b) -> [a] -> [b]
map ((a -> BuiltinByteString)
-> (a, String) -> (BuiltinByteString, String)
forall a b c. (a -> b) -> (a, c) -> (b, c)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first a -> BuiltinByteString
forall a. ToHash a => a -> BuiltinByteString
toHash)

-- | Adds some additional names to these pretty cooked options. This has two
-- practical use cases:
--
-- * Users can use it in conjuction to 'hashNamesFromList' without having to
-- remember to manually invoke 'defaultHashNames'
--
-- * We use it internally to account for names that have been registered during
-- mockchain runs, such as for names that depend on on-chain data, typically a
-- 'Api.TxOutRef'.
addHashNames :: Map Api.BuiltinByteString String -> PrettyCookedOpts -> PrettyCookedOpts
addHashNames :: Map BuiltinByteString String
-> PrettyCookedOpts -> PrettyCookedOpts
addHashNames Map BuiltinByteString String
names opts' :: PrettyCookedOpts
opts'@(PrettyCookedOpts Bool
_ PCOptTxOutRefs
_ Bool
_ Bool
_ PrettyCookedHashOpts
hashOpts Bool
_) =
  PrettyCookedOpts
opts' {pcOptHashes = hashOpts {pcOptHashNames = Map.union names (pcOptHashNames hashOpts)}}