{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module Blockchain.ExtWord (
  Word128,
  Word160,
  Word256,
  Word512,
  word64ToBytes,
  bytesToWord64,
  word128ToBytes,
  bytesToWord128,
  word160ToBytes,
  bytesToWord160,
  word256ToBytes,
  bytesToWord256
  ) where

import Data.Binary
import Data.Bits
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import Legacy.Haskoin.V0102.Network.Haskoin.Crypto.BigWord (Word128, Word160, Word256, Word512)

import Data.Ix

import Blockchain.Data.RLP


instance Ix Word256 where
    range (x, y) | x == y = [x]
    range (x, y) = x:range (x+1, y)
    index (x, y) z | z < x || z > y = error $ "Ix{Word256}.index: Index (" ++ show z ++ ") out of range ((" ++ show x ++ "," ++ show y ++ "))"
    index (x, _) z = fromIntegral $ z - x
    inRange (x, y) z | z >= x && z <= y = True
    inRange _ _ = False

instance RLPSerializable Word512 where
    rlpEncode val = RLPString $ BL.toStrict $ encode val

    rlpDecode (RLPString s) | B.length s == 64 = decode $ BL.fromStrict s
    rlpDecode x = error ("Missing case in rlp2Word512: " ++ show x)

word64ToBytes::Word64->[Word8]
word64ToBytes word = map (fromIntegral . (word `shiftR`)) [64-8, 64-16..0]

bytesToWord64::[Word8]->Word64
bytesToWord64 bytes | length bytes == 8 =
  sum $ map (\(shiftBits, byte) -> fromIntegral byte `shiftL` shiftBits) $ zip [64-8,64-16..0] bytes
bytesToWord64 _ = error "bytesToWord64 was called with the wrong number of bytes"

word128ToBytes::Word128->[Word8]
word128ToBytes word = map (fromIntegral . (word `shiftR`)) [128-8, 128-16..0]

bytesToWord128::[Word8]->Word128
bytesToWord128 bytes | length bytes == 16 =
  sum $ map (\(shiftBits, byte) -> fromIntegral byte `shiftL` shiftBits) $ zip [128-8,128-16..0] bytes
bytesToWord128 _ = error "bytesToWord128 was called with the wrong number of bytes"

word160ToBytes::Word160->[Word8]
word160ToBytes word = map (fromIntegral . (word `shiftR`)) [160-8, 160-16..0]

bytesToWord160::[Word8]->Word160
bytesToWord160 bytes | length bytes == 20 =
  sum $ map (\(shiftBits, byte) -> fromIntegral byte `shiftL` shiftBits) $ zip [160-8,160-16..0] bytes
bytesToWord160 _ = error "bytesToWord128 was called with the wrong number of bytes"

word256ToBytes::Word256->[Word8]
word256ToBytes word = map (fromIntegral . (word `shiftR`)) [256-8, 256-16..0]

instance RLPSerializable Word128 where
    rlpEncode val = RLPString $ BL.toStrict $ encode val

    rlpDecode (RLPString s) | B.null s = 0
    rlpDecode (RLPString s) | B.length s <= 16 = decode $ BL.fromStrict s
    rlpDecode x = error ("Missing case in rlp2Word128: " ++ show x)


instance RLPSerializable Word32 where
    rlpEncode val = RLPString $ BL.toStrict $ encode val

    rlpDecode (RLPString s) | B.null s = 0
    rlpDecode (RLPString s) | B.length s <= 4 = decode $ BL.fromStrict s
    rlpDecode x = error ("Missing case in rlp2Word32: " ++ show x)

instance RLPSerializable Word16 where
    rlpEncode val = RLPString $ BL.toStrict $ encode val

    rlpDecode (RLPString s) | B.null s = 0
    rlpDecode (RLPString s) | B.length s <= 2 = decode $ BL.fromStrict s
    rlpDecode x = error ("Missing case in rlp2Word16: " ++ show x)



bytesToWord256::[Word8]->Word256
bytesToWord256 bytes | length bytes == 32 =
  sum $ map (\(shiftBits, byte) -> fromIntegral byte `shiftL` shiftBits) $ zip [256-8,256-16..0] bytes
bytesToWord256 _ = error "bytesToWord256 was called with the wrong number of bytes"