-- | This module defines 'Tweaks' revolving around the signers of a
-- transaction. They assume but do not ensure that the list of signers is free
-- of duplicates.
module Cooked.Tweak.Signers
  ( getSignersTweak,
    modifySignersTweak,
    setSignersTweak,
    signersSatisfyTweak,
    isSignerTweak,
    hasSignersTweak,
    addFirstSignerTweak,
    addSignersTweak,
    addLastSignerTweak,
    removeSignersTweak,
    removeSignerTweak,
    replaceFirstSignerTweak,
  )
where

import Cooked.Skeleton (txSkelSignersL)
import Cooked.Tweak.Common (MonadTweak, setTweak, viewTweak)
import Cooked.Wallet (Wallet)
import Data.List (delete, (\\))

-- | Returns the current list of signers
getSignersTweak :: (MonadTweak m) => m [Wallet]
getSignersTweak :: forall (m :: * -> *). MonadTweak m => m [Wallet]
getSignersTweak = Optic' A_Lens NoIx TxSkel [Wallet] -> m [Wallet]
forall (m :: * -> *) k (is :: IxList) a.
(MonadTweak m, Is k A_Getter) =>
Optic' k is TxSkel a -> m a
viewTweak Optic' A_Lens NoIx TxSkel [Wallet]
txSkelSignersL

-- | Apply a function to the list of signers and return the old ones
modifySignersTweak :: (MonadTweak m) => ([Wallet] -> [Wallet]) -> m [Wallet]
modifySignersTweak :: forall (m :: * -> *).
MonadTweak m =>
([Wallet] -> [Wallet]) -> m [Wallet]
modifySignersTweak [Wallet] -> [Wallet]
f = do
  [Wallet]
oldSigners <- m [Wallet]
forall (m :: * -> *). MonadTweak m => m [Wallet]
getSignersTweak
  Optic' A_Lens NoIx TxSkel [Wallet] -> [Wallet] -> m ()
forall (m :: * -> *) k (is :: IxList) a.
(MonadTweak m, Is k A_Setter) =>
Optic' k is TxSkel a -> a -> m ()
setTweak Optic' A_Lens NoIx TxSkel [Wallet]
txSkelSignersL ([Wallet] -> [Wallet]
f [Wallet]
oldSigners)
  [Wallet] -> m [Wallet]
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return [Wallet]
oldSigners

-- | Change the current signers and return the old ones
setSignersTweak :: (MonadTweak m) => [Wallet] -> m [Wallet]
setSignersTweak :: forall (m :: * -> *). MonadTweak m => [Wallet] -> m [Wallet]
setSignersTweak = ([Wallet] -> [Wallet]) -> m [Wallet]
forall (m :: * -> *).
MonadTweak m =>
([Wallet] -> [Wallet]) -> m [Wallet]
modifySignersTweak (([Wallet] -> [Wallet]) -> m [Wallet])
-> ([Wallet] -> [Wallet] -> [Wallet]) -> [Wallet] -> m [Wallet]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Wallet] -> [Wallet] -> [Wallet]
forall a b. a -> b -> a
const

-- | Check if the signers satisfy a certain predicate
signersSatisfyTweak :: (MonadTweak m) => ([Wallet] -> Bool) -> m Bool
signersSatisfyTweak :: forall (m :: * -> *). MonadTweak m => ([Wallet] -> Bool) -> m Bool
signersSatisfyTweak = (([Wallet] -> Bool) -> m [Wallet] -> m Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m [Wallet]
forall (m :: * -> *). MonadTweak m => m [Wallet]
getSignersTweak)

-- | Check if a wallet signs a transaction
isSignerTweak :: (MonadTweak m) => Wallet -> m Bool
isSignerTweak :: forall (m :: * -> *). MonadTweak m => Wallet -> m Bool
isSignerTweak = ([Wallet] -> Bool) -> m Bool
forall (m :: * -> *). MonadTweak m => ([Wallet] -> Bool) -> m Bool
signersSatisfyTweak (([Wallet] -> Bool) -> m Bool)
-> (Wallet -> [Wallet] -> Bool) -> Wallet -> m Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Wallet -> [Wallet] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem

-- | Check if the transaction has at least a signer
hasSignersTweak :: (MonadTweak m) => m Bool
hasSignersTweak :: forall (m :: * -> *). MonadTweak m => m Bool
hasSignersTweak = ([Wallet] -> Bool) -> m Bool
forall (m :: * -> *). MonadTweak m => ([Wallet] -> Bool) -> m Bool
signersSatisfyTweak (Bool -> Bool
not (Bool -> Bool) -> ([Wallet] -> Bool) -> [Wallet] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Wallet] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null)

-- | Add a signer to the transaction, at the head of the list of signers, and
-- return the old list of signers
addFirstSignerTweak :: (MonadTweak m) => Wallet -> m [Wallet]
addFirstSignerTweak :: forall (m :: * -> *). MonadTweak m => Wallet -> m [Wallet]
addFirstSignerTweak = ([Wallet] -> [Wallet]) -> m [Wallet]
forall (m :: * -> *).
MonadTweak m =>
([Wallet] -> [Wallet]) -> m [Wallet]
modifySignersTweak (([Wallet] -> [Wallet]) -> m [Wallet])
-> (Wallet -> [Wallet] -> [Wallet]) -> Wallet -> m [Wallet]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (:)

-- | Add signers at the end of the list of signers, and return the old list of
-- signers
addSignersTweak :: (MonadTweak m) => [Wallet] -> m [Wallet]
addSignersTweak :: forall (m :: * -> *). MonadTweak m => [Wallet] -> m [Wallet]
addSignersTweak = ([Wallet] -> [Wallet]) -> m [Wallet]
forall (m :: * -> *).
MonadTweak m =>
([Wallet] -> [Wallet]) -> m [Wallet]
modifySignersTweak (([Wallet] -> [Wallet]) -> m [Wallet])
-> ([Wallet] -> [Wallet] -> [Wallet]) -> [Wallet] -> m [Wallet]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Wallet] -> [Wallet] -> [Wallet]
forall a. Semigroup a => a -> a -> a
(<>)

-- | Add a signer to the transaction, at the end of the list of signers, and
-- return the old list of signers
addLastSignerTweak :: (MonadTweak m) => Wallet -> m [Wallet]
addLastSignerTweak :: forall (m :: * -> *). MonadTweak m => Wallet -> m [Wallet]
addLastSignerTweak = [Wallet] -> m [Wallet]
forall (m :: * -> *). MonadTweak m => [Wallet] -> m [Wallet]
addSignersTweak ([Wallet] -> m [Wallet])
-> (Wallet -> [Wallet]) -> Wallet -> m [Wallet]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Wallet -> [Wallet] -> [Wallet]
forall a. a -> [a] -> [a]
: [])

-- | Remove signers from the transaction and return the old list of signers
removeSignersTweak :: (MonadTweak m) => [Wallet] -> m [Wallet]
removeSignersTweak :: forall (m :: * -> *). MonadTweak m => [Wallet] -> m [Wallet]
removeSignersTweak = ([Wallet] -> [Wallet]) -> m [Wallet]
forall (m :: * -> *).
MonadTweak m =>
([Wallet] -> [Wallet]) -> m [Wallet]
modifySignersTweak (([Wallet] -> [Wallet]) -> m [Wallet])
-> ([Wallet] -> [Wallet] -> [Wallet]) -> [Wallet] -> m [Wallet]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Wallet] -> [Wallet] -> [Wallet]
forall a. Eq a => [a] -> [a] -> [a]
(\\)

-- | Remove a signer from the transaction and return the old list of signers
removeSignerTweak :: (MonadTweak m) => Wallet -> m [Wallet]
removeSignerTweak :: forall (m :: * -> *). MonadTweak m => Wallet -> m [Wallet]
removeSignerTweak = ([Wallet] -> [Wallet]) -> m [Wallet]
forall (m :: * -> *).
MonadTweak m =>
([Wallet] -> [Wallet]) -> m [Wallet]
modifySignersTweak (([Wallet] -> [Wallet]) -> m [Wallet])
-> (Wallet -> [Wallet] -> [Wallet]) -> Wallet -> m [Wallet]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Wallet -> [Wallet] -> [Wallet]
forall a. Eq a => a -> [a] -> [a]
delete

-- | Changes the first signer (adds it if there are no signers) and return the
-- old list of signers.
replaceFirstSignerTweak :: (MonadTweak m) => Wallet -> m [Wallet]
replaceFirstSignerTweak :: forall (m :: * -> *). MonadTweak m => Wallet -> m [Wallet]
replaceFirstSignerTweak =
  ([Wallet] -> [Wallet]) -> m [Wallet]
forall (m :: * -> *).
MonadTweak m =>
([Wallet] -> [Wallet]) -> m [Wallet]
modifySignersTweak
    (([Wallet] -> [Wallet]) -> m [Wallet])
-> (Wallet -> [Wallet] -> [Wallet]) -> Wallet -> m [Wallet]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ( \Wallet
newSigner -> \case
          [] -> [Wallet
newSigner]
          (Wallet
_ : [Wallet]
ss) -> Wallet
newSigner Wallet -> [Wallet] -> [Wallet]
forall a. a -> [a] -> [a]
: [Wallet]
ss
      )