-- | Conversion between different "octet stream" formats for convenience.
--
-- TODO: utf8 handling for text

{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
module Bitcoin.Misc.OctetStream where

--------------------------------------------------------------------------------

import Control.Monad ( liftM )

import Data.Char ( ord , chr )
import Data.Word

import qualified Data.ByteString as B

import Bitcoin.Misc.BigInt

--------------------------------------------------------------------------------

class OctetStream a where

  toByteString     :: a -> B.ByteString
  toWord8List      :: a -> [Word8]
  unsafeToCharList :: a -> [Char]
  toIntegerLE      :: a -> Integer
  toIntegerBE      :: a -> Integer

  fromByteString :: B.ByteString -> a
  fromWord8List  :: [Word8] -> a
  fromCharList   :: [Char]  -> a      
  fromIntegerLE  :: Integer -> a
  fromIntegerBE  :: Integer -> a

  toByteString     = B.pack . toWord8List
  toWord8List      = B.unpack . toByteString
  unsafeToCharList = map word8_to_char . toWord8List 
  toIntegerLE      = littleEndianRollInteger . toWord8List
  toIntegerBE      = bigEndianRollInteger    . toWord8List   

  fromByteString = fromWord8List  . B.unpack
  fromWord8List  = fromByteString . B.pack
  fromCharList   = fromWord8List . map char_to_word8
  fromIntegerLE  = fromWord8List . littleEndianUnrollInteger
  fromIntegerBE  = fromWord8List . bigEndianUnrollInteger

--------------------------------------------------------------------------------

instance OctetStream B.ByteString where
  toByteString   = id
  fromByteString = id
  toWord8List    = B.unpack
  fromWord8List  = B.pack
  
instance OctetStream [Word8] where
  toWord8List   = id
  fromWord8List = id
  toByteString   = B.pack
  fromByteString = B.unpack

-- | Note: we treat String as an ASCII string here, no fancy utf8 here (it's in the TODO)
instance OctetStream String where
  unsafeToCharList = id
  fromCharList     = id
  toWord8List      = map char_to_word8
  fromWord8List    = map word8_to_char
  toByteString     = B.pack . map char_to_word8
  fromByteString   = map word8_to_char . B.unpack

--------------------------------------------------------------------------------

{-# INLINE word8_to_char #-}
word8_to_char :: Word8 -> Char
word8_to_char = chr . fromIntegral

{-# INLINE char_to_word8 #-}
char_to_word8 :: Char -> Word8
char_to_word8 = fromIntegral . ord

--------------------------------------------------------------------------------