{-# LANGUAGE NamedFieldPuns #-}

module Cardano.CLI.Mary.RenderValue
  ( RenderAdaAssetId (..)
  , RenderIndentation (..)
  , RenderPrettyValueOptions (..)
  , RenderValueOptions (..)
  , defaultRenderPrettyValueOptions
  , defaultRenderValueOptions
  , renderPrettyValue
  , renderValue
  ) where

import           Prelude

import qualified Data.ByteString as BS
import           Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text

import           Cardano.Api (AssetId (..), AssetName (..), PolicyId (..), Quantity, Value,
                   serialiseToRawBytesHexText, valueToList)

-- | Whether the ADA asset ID should be rendered.
data RenderAdaAssetId
  = RenderAdaAssetId
    -- ^ Render the ADA asset ID.
  | RenderNoAdaAssetId
    -- ^ Do not render the ADA asset ID.
  deriving (Int -> RenderAdaAssetId -> ShowS
[RenderAdaAssetId] -> ShowS
RenderAdaAssetId -> String
(Int -> RenderAdaAssetId -> ShowS)
-> (RenderAdaAssetId -> String)
-> ([RenderAdaAssetId] -> ShowS)
-> Show RenderAdaAssetId
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [RenderAdaAssetId] -> ShowS
$cshowList :: [RenderAdaAssetId] -> ShowS
show :: RenderAdaAssetId -> String
$cshow :: RenderAdaAssetId -> String
showsPrec :: Int -> RenderAdaAssetId -> ShowS
$cshowsPrec :: Int -> RenderAdaAssetId -> ShowS
Show, RenderAdaAssetId -> RenderAdaAssetId -> Bool
(RenderAdaAssetId -> RenderAdaAssetId -> Bool)
-> (RenderAdaAssetId -> RenderAdaAssetId -> Bool)
-> Eq RenderAdaAssetId
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: RenderAdaAssetId -> RenderAdaAssetId -> Bool
$c/= :: RenderAdaAssetId -> RenderAdaAssetId -> Bool
== :: RenderAdaAssetId -> RenderAdaAssetId -> Bool
$c== :: RenderAdaAssetId -> RenderAdaAssetId -> Bool
Eq)

-- | Options which detail how a representation of a 'Value' should be
-- rendered.
newtype RenderValueOptions = RenderValueOptions RenderAdaAssetId
  deriving Int -> RenderValueOptions -> ShowS
[RenderValueOptions] -> ShowS
RenderValueOptions -> String
(Int -> RenderValueOptions -> ShowS)
-> (RenderValueOptions -> String)
-> ([RenderValueOptions] -> ShowS)
-> Show RenderValueOptions
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [RenderValueOptions] -> ShowS
$cshowList :: [RenderValueOptions] -> ShowS
show :: RenderValueOptions -> String
$cshow :: RenderValueOptions -> String
showsPrec :: Int -> RenderValueOptions -> ShowS
$cshowsPrec :: Int -> RenderValueOptions -> ShowS
Show

defaultRenderValueOptions :: RenderValueOptions
defaultRenderValueOptions :: RenderValueOptions
defaultRenderValueOptions = RenderAdaAssetId -> RenderValueOptions
RenderValueOptions RenderAdaAssetId
RenderAdaAssetId

-- | Render a textual representation of a 'Value'.
--
-- Note that this textual representation can be parsed by 'parseValue'.
renderValue :: RenderValueOptions -> Value -> Text
renderValue :: RenderValueOptions -> Value -> Text
renderValue (RenderValueOptions RenderAdaAssetId
renderAdaAssetId) Value
v =
    Text -> [Text] -> Text
Text.intercalate
      Text
" + "
      (((AssetId, Quantity) -> Text) -> [(AssetId, Quantity)] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (RenderAdaAssetId -> (AssetId, Quantity) -> Text
renderAssetIdQuantityPair RenderAdaAssetId
renderAdaAssetId) [(AssetId, Quantity)]
vals)
  where
    vals :: [(AssetId, Quantity)]
    vals :: [(AssetId, Quantity)]
vals = Value -> [(AssetId, Quantity)]
valueToList Value
v

-- | How a \"prettified\" representation of a 'Value' should be indented.
data RenderIndentation
  = IndentTab
    -- ^ Indent with a tab character (\'\\t\').
  | IndentSpaces !Int
    -- ^ Indent with a provided number of spaces.
  deriving Int -> RenderIndentation -> ShowS
[RenderIndentation] -> ShowS
RenderIndentation -> String
(Int -> RenderIndentation -> ShowS)
-> (RenderIndentation -> String)
-> ([RenderIndentation] -> ShowS)
-> Show RenderIndentation
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [RenderIndentation] -> ShowS
$cshowList :: [RenderIndentation] -> ShowS
show :: RenderIndentation -> String
$cshow :: RenderIndentation -> String
showsPrec :: Int -> RenderIndentation -> ShowS
$cshowsPrec :: Int -> RenderIndentation -> ShowS
Show

-- | Options which detail how a \"prettified\" representation of a 'Value'
-- should be rendered.
data RenderPrettyValueOptions = RenderPrettyValueOptions
  { RenderPrettyValueOptions -> RenderIndentation
rpvoIndentation :: !RenderIndentation
  , RenderPrettyValueOptions -> RenderAdaAssetId
rpvoRenderAdaAssetId :: !RenderAdaAssetId
  } deriving Int -> RenderPrettyValueOptions -> ShowS
[RenderPrettyValueOptions] -> ShowS
RenderPrettyValueOptions -> String
(Int -> RenderPrettyValueOptions -> ShowS)
-> (RenderPrettyValueOptions -> String)
-> ([RenderPrettyValueOptions] -> ShowS)
-> Show RenderPrettyValueOptions
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [RenderPrettyValueOptions] -> ShowS
$cshowList :: [RenderPrettyValueOptions] -> ShowS
show :: RenderPrettyValueOptions -> String
$cshow :: RenderPrettyValueOptions -> String
showsPrec :: Int -> RenderPrettyValueOptions -> ShowS
$cshowsPrec :: Int -> RenderPrettyValueOptions -> ShowS
Show

defaultRenderPrettyValueOptions :: RenderPrettyValueOptions
defaultRenderPrettyValueOptions :: RenderPrettyValueOptions
defaultRenderPrettyValueOptions =
  RenderPrettyValueOptions :: RenderIndentation -> RenderAdaAssetId -> RenderPrettyValueOptions
RenderPrettyValueOptions
    { rpvoIndentation :: RenderIndentation
rpvoIndentation = Int -> RenderIndentation
IndentSpaces Int
4
    , rpvoRenderAdaAssetId :: RenderAdaAssetId
rpvoRenderAdaAssetId = RenderAdaAssetId
RenderAdaAssetId
    }

-- | Render a \"prettified\" textual representation of a 'Value'.
renderPrettyValue :: RenderPrettyValueOptions -> Value -> Text
renderPrettyValue :: RenderPrettyValueOptions -> Value -> Text
renderPrettyValue RenderPrettyValueOptions
opts Value
v =
    Text -> [Text] -> Text
Text.intercalate
      (Text
"\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> RenderIndentation -> Text
renderIndentation RenderIndentation
rpvoIndentation Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"+ ")
      (((AssetId, Quantity) -> Text) -> [(AssetId, Quantity)] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (RenderAdaAssetId -> (AssetId, Quantity) -> Text
renderAssetIdQuantityPair RenderAdaAssetId
rpvoRenderAdaAssetId) [(AssetId, Quantity)]
vals)
  where
    RenderPrettyValueOptions
      { RenderIndentation
rpvoIndentation :: RenderIndentation
rpvoIndentation :: RenderPrettyValueOptions -> RenderIndentation
rpvoIndentation
      , RenderAdaAssetId
rpvoRenderAdaAssetId :: RenderAdaAssetId
rpvoRenderAdaAssetId :: RenderPrettyValueOptions -> RenderAdaAssetId
rpvoRenderAdaAssetId
      } = RenderPrettyValueOptions
opts

    vals :: [(AssetId, Quantity)]
    vals :: [(AssetId, Quantity)]
vals = Value -> [(AssetId, Quantity)]
valueToList Value
v

------------------------------------------------------------------------------
-- Helpers
------------------------------------------------------------------------------

renderIndentation :: RenderIndentation -> Text
renderIndentation :: RenderIndentation -> Text
renderIndentation RenderIndentation
IndentTab = Text
"\t"
renderIndentation (IndentSpaces Int
n) = Int -> Text -> Text
Text.replicate Int
n Text
" "

renderPolicyId :: PolicyId -> Text
renderPolicyId :: PolicyId -> Text
renderPolicyId (PolicyId ScriptHash
scriptHash) = ScriptHash -> Text
forall a. SerialiseAsRawBytes a => a -> Text
serialiseToRawBytesHexText ScriptHash
scriptHash

renderAssetId :: RenderAdaAssetId -> AssetId -> Text
renderAssetId :: RenderAdaAssetId -> AssetId -> Text
renderAssetId RenderAdaAssetId
RenderAdaAssetId AssetId
AdaAssetId = Text
"lovelace"
renderAssetId RenderAdaAssetId
RenderNoAdaAssetId AssetId
AdaAssetId = Text
forall a. Monoid a => a
mempty
renderAssetId RenderAdaAssetId
_ (AssetId PolicyId
polId (AssetName ByteString
assetName))
  | ByteString -> Bool
BS.null ByteString
assetName = PolicyId -> Text
renderPolicyId PolicyId
polId
  | Bool
otherwise = PolicyId -> Text
renderPolicyId PolicyId
polId Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"." Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> ByteString -> Text
Text.decodeUtf8 ByteString
assetName

renderAssetIdQuantityPair :: RenderAdaAssetId -> (AssetId, Quantity) -> Text
renderAssetIdQuantityPair :: RenderAdaAssetId -> (AssetId, Quantity) -> Text
renderAssetIdQuantityPair RenderAdaAssetId
renderAdaAssetId (AssetId
aId, Quantity
quant) =
  String -> Text
Text.pack (Quantity -> String
forall a. Show a => a -> String
show Quantity
quant) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> RenderAdaAssetId -> AssetId -> Text
renderAssetId RenderAdaAssetId
renderAdaAssetId AssetId
aId