-----------------------------------------------------------------------------
--
-- Module      :  $Headers
-- Copyright   :  (c) 2021 Brian W Bush
-- License     :  MIT
--
-- Maintainer  :  Brian W Bush <code@functionally.io>
-- Stability   :  Experimental
-- Portability :  Portable
--
-- | Asset utilities.
--
-----------------------------------------------------------------------------


{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications  #-}


module Mantra.Asset (
-- * Assets
  assetFingerprint
, assetFingerprintString
, assetFingerprintBytes
) where


import Cardano.Api (AssetId(..), AssetName(..), PolicyId(..), serialiseToRawBytes)
import Codec.Binary.Bech32 (HumanReadablePart, dataPartFromBytes, encodeLenient, humanReadablePartFromText)
import Crypto.Hash (hash)
import Crypto.Hash.Algorithms (Blake2b_160)
import Data.ByteArray (convert)
import Data.Text (Text)
import Mantra.Types (MantraM, foistMantraEither, throwMantra)

import qualified Data.ByteString.Char8  as BS     (ByteString, pack)
import qualified Data.ByteString.Base16 as Base16 (decode)


-- | The human-readable prefix for an asset.
assetPrefix :: HumanReadablePart
Right HumanReadablePart
assetPrefix = Text -> Either HumanReadablePartError HumanReadablePart
humanReadablePartFromText Text
"asset"


-- | Compute an asset fingerprint.
assetFingerprintBytes :: BS.ByteString -- ^ The bytes of the policy ID.
                      -> BS.ByteString -- ^ The bytes of the asset name.
                      -> Text
assetFingerprintBytes :: ByteString -> ByteString -> Text
assetFingerprintBytes ByteString
policyId ByteString
assetName =
  HumanReadablePart -> DataPart -> Text
encodeLenient HumanReadablePart
assetPrefix
    (DataPart -> Text)
-> (ByteString -> DataPart) -> ByteString -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> DataPart
dataPartFromBytes
    (ByteString -> DataPart)
-> (ByteString -> ByteString) -> ByteString -> DataPart
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Digest Blake2b_160 -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
convert
    (Digest Blake2b_160 -> ByteString)
-> (ByteString -> Digest Blake2b_160) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ByteArrayAccess ByteString, HashAlgorithm Blake2b_160) =>
ByteString -> Digest Blake2b_160
forall ba a.
(ByteArrayAccess ba, HashAlgorithm a) =>
ba -> Digest a
hash @_ @Blake2b_160
    (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ ByteString
policyId ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
assetName


-- | Compute an asset fingerprint.
assetFingerprintString :: Monad m
                       => String         -- ^ The policy ID string.
                       -> String         -- ^ The asset name string.
                       -> MantraM m Text -- ^ Action for computing the fingerprint.
assetFingerprintString :: String -> String -> MantraM m Text
assetFingerprintString String
policyId String
assetName =
  let
    assetName' :: ByteString
assetName' = String -> ByteString
BS.pack String
assetName
  in
    (ByteString -> Text) -> MantraM m ByteString -> MantraM m Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (ByteString -> ByteString -> Text
`assetFingerprintBytes` ByteString
assetName')
     (MantraM m ByteString -> MantraM m Text)
-> (ByteString -> MantraM m ByteString)
-> ByteString
-> MantraM m Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Either String ByteString -> MantraM m ByteString
forall (m :: * -> *) e a.
(Monad m, Show e) =>
Either e a -> MantraM m a
foistMantraEither
     (Either String ByteString -> MantraM m ByteString)
-> (ByteString -> Either String ByteString)
-> ByteString
-> MantraM m ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Either String ByteString
Base16.decode
     (ByteString -> MantraM m Text) -> ByteString -> MantraM m Text
forall a b. (a -> b) -> a -> b
$ String -> ByteString
BS.pack String
policyId


-- | Compute an asset fingerprint.
assetFingerprint :: Monad m
                 => AssetId        -- ^ The asset ID.
                 -> MantraM m Text -- ^ Action for computing the fingerprint.
assetFingerprint :: AssetId -> MantraM m Text
assetFingerprint (AssetId (PolicyId ScriptHash
scriptHash) (AssetName ByteString
assetName)) =
  Text -> MantraM m Text
forall (m :: * -> *) a. Monad m => a -> m a
return
    (Text -> MantraM m Text) -> Text -> MantraM m Text
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString -> Text
assetFingerprintBytes
      (ScriptHash -> ByteString
forall a. SerialiseAsRawBytes a => a -> ByteString
serialiseToRawBytes ScriptHash
scriptHash)
      ByteString
assetName
assetFingerprint AssetId
_ = String -> MantraM m Text
forall (m :: * -> *) a. Monad m => String -> MantraM m a
throwMantra String
"Non-token asset."