{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric  #-}

-- |
-- Module      :  Network.Polkadot.Call
-- Copyright   :  Aleksandr Krupenkin 2016-2024
-- License     :  Apache-2.0
--
-- Maintainer  :  mail@akru.me
-- Stability   :  experimental
-- Portability :  unportable
--
--

module Network.Polkadot.Call where

import           Codec.Scale                   (Decode, Encode, Generic, decode)
import           Control.Monad.Fail            (MonadFail)
import           Data.List                     (findIndex)
import           Data.Text                     (Text)
import           Data.Word                     (Word8)
import qualified GHC.Generics                  as GHC (Generic)
import           Network.JsonRpc.TinyClient    (JsonRpc)

import           Network.Polkadot.Metadata     (MetadataVersioned (V13),
                                                metadata)
import           Network.Polkadot.Metadata.V13 (moduleCalls, moduleName,
                                                modules)
import           Network.Polkadot.Metadata.V9  (functionName)
import           Network.Polkadot.Rpc.State    (getMetadata)

-- | Call function of module using standard substrate extrionsic.
data Call a = Call !Word8 !Word8 !a
    deriving (Call a -> Call a -> Bool
(Call a -> Call a -> Bool)
-> (Call a -> Call a -> Bool) -> Eq (Call a)
forall a. Eq a => Call a -> Call a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall a. Eq a => Call a -> Call a -> Bool
== :: Call a -> Call a -> Bool
$c/= :: forall a. Eq a => Call a -> Call a -> Bool
/= :: Call a -> Call a -> Bool
Eq, Int -> Call a -> ShowS
[Call a] -> ShowS
Call a -> String
(Int -> Call a -> ShowS)
-> (Call a -> String) -> ([Call a] -> ShowS) -> Show (Call a)
forall a. Show a => Int -> Call a -> ShowS
forall a. Show a => [Call a] -> ShowS
forall a. Show a => Call a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall a. Show a => Int -> Call a -> ShowS
showsPrec :: Int -> Call a -> ShowS
$cshow :: forall a. Show a => Call a -> String
show :: Call a -> String
$cshowList :: forall a. Show a => [Call a] -> ShowS
showList :: [Call a] -> ShowS
Show, All SListI (Code (Call a))
All SListI (Code (Call a)) =>
(Call a -> Rep (Call a))
-> (Rep (Call a) -> Call a) -> Generic (Call a)
Rep (Call a) -> Call a
Call a -> Rep (Call a)
forall a. All SListI (Code (Call a))
forall a.
All SListI (Code a) =>
(a -> Rep a) -> (Rep a -> a) -> Generic a
forall a. Rep (Call a) -> Call a
forall a. Call a -> Rep (Call a)
$cfrom :: forall a. Call a -> Rep (Call a)
from :: Call a -> Rep (Call a)
$cto :: forall a. Rep (Call a) -> Call a
to :: Rep (Call a) -> Call a
Generic, (forall x. Call a -> Rep (Call a) x)
-> (forall x. Rep (Call a) x -> Call a) -> Generic (Call a)
forall x. Rep (Call a) x -> Call a
forall x. Call a -> Rep (Call a) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall a x. Rep (Call a) x -> Call a
forall a x. Call a -> Rep (Call a) x
$cfrom :: forall a x. Call a -> Rep (Call a) x
from :: forall x. Call a -> Rep (Call a) x
$cto :: forall a x. Rep (Call a) x -> Call a
to :: forall x. Rep (Call a) x -> Call a
GHC.Generic, Putter (Call a)
Putter (Call a) -> Encode (Call a)
forall a. Encode a => Putter (Call a)
forall a. Putter a -> Encode a
$cput :: forall a. Encode a => Putter (Call a)
put :: Putter (Call a)
Encode, Get (Call a)
Get (Call a) -> Decode (Call a)
forall a. Get a -> Decode a
forall a. Decode a => Get (Call a)
$cget :: forall a. Decode a => Get (Call a)
get :: Get (Call a)
Decode)

-- | Create 'Call' type from text-encoded module and function.
new_call :: (Encode a, Decode a, JsonRpc m, MonadFail m)
         => Text
         -- ^ Module name.
         -> Text
         -- ^ Function name.
         -> a
         -- ^ Tuple of arguments.
         -> m (Call a)
new_call :: forall a (m :: * -> *).
(Encode a, Decode a, JsonRpc m, MonadFail m) =>
Text -> Text -> a -> m (Call a)
new_call Text
modName Text
funName a
args = do
    Right (V13 Metadata
meta) <- ((Metadata -> MetadataVersioned)
-> Either String Metadata -> Either String MetadataVersioned
forall a b. (a -> b) -> Either String a -> Either String b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Metadata -> MetadataVersioned
metadata (Either String Metadata -> Either String MetadataVersioned)
-> (HexString -> Either String Metadata)
-> HexString
-> Either String MetadataVersioned
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HexString -> Either String Metadata
forall ba a.
(ByteArrayAccess ba, Decode a) =>
ba -> Either String a
decode) (HexString -> Either String MetadataVersioned)
-> m HexString -> m (Either String MetadataVersioned)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m HexString
forall (m :: * -> *). JsonRpc m => m HexString
getMetadata
    case (ModuleMetadata -> Bool) -> [ModuleMetadata] -> Maybe Int
forall a. (a -> Bool) -> [a] -> Maybe Int
findIndex ((Text
modName Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
==) (Text -> Bool)
-> (ModuleMetadata -> Text) -> ModuleMetadata -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ModuleMetadata -> Text
moduleName) (Metadata -> [ModuleMetadata]
modules Metadata
meta) of
        Maybe Int
Nothing -> String -> m (Call a)
forall a. String -> m a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> m (Call a)) -> String -> m (Call a)
forall a b. (a -> b) -> a -> b
$ String
"module " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Text -> String
forall a. Show a => a -> String
show Text
modName String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
" not found"
        Just Int
modIx ->
            case (FunctionMetadata -> Bool) -> [FunctionMetadata] -> Maybe Int
forall a. (a -> Bool) -> [a] -> Maybe Int
findIndex ((Text
funName Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
==) (Text -> Bool)
-> (FunctionMetadata -> Text) -> FunctionMetadata -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FunctionMetadata -> Text
functionName) ([FunctionMetadata] -> Maybe Int)
-> Maybe [FunctionMetadata] -> Maybe Int
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< ModuleMetadata -> Maybe [FunctionMetadata]
moduleCalls (Metadata -> [ModuleMetadata]
modules Metadata
meta [ModuleMetadata] -> Int -> ModuleMetadata
forall a. HasCallStack => [a] -> Int -> a
!! Int
modIx) of
                Maybe Int
Nothing -> String -> m (Call a)
forall a. String -> m a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> m (Call a)) -> String -> m (Call a)
forall a b. (a -> b) -> a -> b
$ String
"function " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Text -> String
forall a. Show a => a -> String
show Text
funName String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
" not found"
                Just Int
funIx -> Call a -> m (Call a)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Call a -> m (Call a)) -> Call a -> m (Call a)
forall a b. (a -> b) -> a -> b
$ Word8 -> Word8 -> a -> Call a
forall a. Word8 -> Word8 -> a -> Call a
Call (Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
modIx) (Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
funIx) a
args