{-# LANGUAGE FlexibleContexts     #-}
{-# LANGUAGE TypeFamilies         #-}
{-# LANGUAGE UndecidableInstances #-}

-- |
-- Module      :  Data.Solidity.Prim.List
-- Copyright   :  Aleksandr Krupenkin 2016-2021
-- License     :  Apache-2.0
--
-- Maintainer  :  mail@akru.me
-- Stability   :  experimental
-- Portability :  noportable
--
-- Ethereum Abi dynamic and static size vectors based on linked lists.
--

module Data.Solidity.Prim.List
    (
    -- * Fixed size linked list
      ListN
    ) where

import           Basement.Nat           (NatWithinBound)
import           Basement.Sized.List    (ListN, toListN_, unListN)
import qualified Basement.Sized.List    as SL (mapM_, replicateM)
import           Control.Monad          (replicateM)
import           GHC.Exts               (IsList (..))
import           GHC.TypeLits           (KnownNat)

import           Data.Solidity.Abi      (AbiGet (..), AbiPut (..), AbiType (..))
import           Data.Solidity.Prim.Int (getWord256, putWord256)

instance AbiType [a] where
    isDynamic :: Proxy [a] -> Bool
isDynamic Proxy [a]
_ = Bool
True

instance AbiPut a => AbiPut [a] where
    abiPut :: Putter [a]
abiPut [a]
l = do Putter Word256
putWord256 Putter Word256 -> Putter Word256
forall a b. (a -> b) -> a -> b
$ Int -> Word256
forall a b. (Integral a, Num b) => a -> b
fromIntegral ([a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
l)
                  (a -> Put) -> Putter [a]
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap a -> Put
forall a. AbiPut a => Putter a
abiPut [a]
l

instance AbiGet a => AbiGet [a] where
    abiGet :: Get [a]
abiGet = do Int
len <- Word256 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word256 -> Int) -> Get Word256 -> Get Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word256
getWord256
                Int -> Get a -> Get [a]
forall (m :: * -> *) a. Applicative m => Int -> m a -> m [a]
replicateM Int
len Get a
forall a. AbiGet a => Get a
abiGet

instance AbiType (ListN n a) where
    isDynamic :: Proxy (ListN n a) -> Bool
isDynamic Proxy (ListN n a)
_ = Bool
False

instance AbiPut a => AbiPut (ListN n a) where
    abiPut :: Putter (ListN n a)
abiPut = (a -> Put) -> Putter (ListN n a)
forall (m :: * -> *) a b (n :: Nat).
Monad m =>
(a -> m b) -> ListN n a -> m ()
SL.mapM_ a -> Put
forall a. AbiPut a => Putter a
abiPut

instance (NatWithinBound Int n, KnownNat n, AbiGet a) => AbiGet (ListN n a) where
    abiGet :: Get (ListN n a)
abiGet = Get a -> Get (ListN n a)
forall (n :: Nat) (m :: * -> *) a.
(NatWithinBound Int n, Monad m, KnownNat n) =>
m a -> m (ListN n a)
SL.replicateM Get a
forall a. AbiGet a => Get a
abiGet

instance (NatWithinBound Int n, KnownNat n) => IsList (ListN n a) where
    type Item (ListN n a) = a
    fromList :: [Item (ListN n a)] -> ListN n a
fromList = [Item (ListN n a)] -> ListN n a
forall (n :: Nat) a.
(HasCallStack, NatWithinBound Int n, KnownNat n) =>
[a] -> ListN n a
toListN_
    toList :: ListN n a -> [Item (ListN n a)]
toList   = ListN n a -> [Item (ListN n a)]
forall (n :: Nat) a. ListN n a -> [a]
unListN