{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE OverloadedStrings #-}

-- |
-- Module      :  Network.Ethereum.ABI.Prim.Address
-- Copyright   :  Alexander Krupenkin 2016-2018
-- License     :  BSD3
--
-- Maintainer  :  mail@akru.me
-- Stability   :  experimental
-- Portability :  noportable
--
-- Ethereum ABI address type.
--

module Network.Ethereum.ABI.Prim.Address (
    Address
  ) where

import           Control.Monad                 ((<=<))
import           Data.Aeson                    (FromJSON (..), ToJSON (..),
                                                Value (String))
import           Data.ByteArray                (Bytes, zero)
import           Data.ByteArray.Encoding       (Base (Base16), convertFromBase,
                                                convertToBase)
import           Data.ByteString               (ByteString)
import qualified Data.ByteString.Char8         as C8 (drop, pack, take, unpack)
import           Data.Monoid                   ((<>))
import           Data.String                   (IsString (..))
import           Data.Text.Encoding            (decodeUtf8, encodeUtf8)
import           Generics.SOP                  (Generic)
import qualified GHC.Generics                  as GHC (Generic)

import           Network.Ethereum.ABI.Class    (ABIGet (..), ABIPut (..),
                                                ABIType (..))
import           Network.Ethereum.ABI.Codec    (decode, encode)
import           Network.Ethereum.ABI.Prim.Int (UIntN)

-- | Ethereum account address
newtype Address = Address { unAddress :: UIntN 160 }
  deriving (Eq, Ord, GHC.Generic)

instance Generic Address

-- TODO: Address . drop 12 . sha3
{-
fromPublic :: ByteArrayAccess bin => bin -> Maybe Address
fromPublic = undefined
-}

fromHexString :: ByteString -> Either String Address
fromHexString = decode . align <=< convertFromBase Base16 . trim0x
  where trim0x s | C8.take 2 s == "0x" = C8.drop 2 s
                 | otherwise = s
        align = (zero 12 <>) :: Bytes -> Bytes

toHexString :: Address -> ByteString
toHexString = ("0x" <>) . convertToBase Base16 . C8.drop 12 . encode

instance Show Address where
    show = C8.unpack . toHexString

instance IsString Address where
    fromString = either error id . fromHexString . C8.pack

instance ABIType Address where
    isDynamic _ = False

instance ABIGet Address where
    abiGet = Address <$> abiGet

instance ABIPut Address where
    abiPut = abiPut . unAddress

instance FromJSON Address where
    parseJSON (String a) = either fail return $ fromHexString (encodeUtf8 a)
    parseJSON _          = fail "Address should be a string"

instance ToJSON Address where
    toJSON = String . decodeUtf8 . toHexString