module Bitcoin.Internal.Util
( module Bitcoin.Internal.Util
, module X
, cs
, fmapL
, (<>)
, module Ctrl
)
where


import Bitcoin.Internal.Types as X
import Data.Serialize       as Bin
import Data.Serialize.Get   as BinGet
import qualified Data.ByteString                as B
import qualified Data.Aeson.Types               as JSON
import qualified Data.ByteString.Base16         as B16

import qualified Data.ByteString.Char8          as C
import qualified Data.Text                      as T
import           Data.Text.Encoding         (decodeUtf8, encodeUtf8)
import           Data.Typeable              (Typeable, typeOf)
import qualified Network.Haskoin.Crypto     as HC

import           Data.String.Conversions    (cs)
--import           Data.Either               (lefts)
import           Data.EitherR               (fmapL)
import           Data.Monoid                    ((<>))
import           Control.Monad              as Ctrl (forM, mapM, (>=>), (<=<))


toInt :: (Integral a, Num b) => a -> b
toInt = fromIntegral


getIndexSafe :: [a] -> Int -> Maybe a
getIndexSafe l i = if i < 0 || i+1 > length l then Nothing else Just $ l !! i

toHexString :: B.ByteString -> String
toHexString =  C.unpack . B16.encode

toHexBS :: B.ByteString -> B.ByteString
toHexBS =  B16.encode

fromHexBS :: B.ByteString -> B.ByteString
fromHexBS = fst . B16.decode

fromHexString :: String -> B.ByteString
fromHexString hexStr =
    case (B16.decode . C.pack) hexStr of
        (bs,e) ->
            if B.length e /= 0 then B.empty else bs

serialize :: Bin.Serialize a => a -> B.ByteString
serialize = Bin.encode

deserEither :: forall a. (Typeable a, Bin.Serialize a) => B.ByteString -> Either String a
deserEither bs = do
    let eitherRes' = BinGet.runGetPartial (Bin.get :: BinGet.Get a) bs
    handleResult eitherRes'
    where
        handleResult eitherRes = case eitherRes of
            BinGet.Done val _           -> Right val
            BinGet.Partial feedFunc     -> handleResult $ feedFunc B.empty
            BinGet.Fail e leftoverBS    -> Left $
                "Type: " ++ show (typeOf (undefined :: a)) ++
                ". Error: " ++ e ++
                ". Data consumed (" ++ show offset ++ " bytes): " ++
                    toHexString (B.take offset bs) ++
                ". Unconsumed data: (" ++ show ( B.length bs - fromIntegral offset ) ++
                " bytes): " ++
                    toHexString leftoverBS
                        where offset = B.length bs - B.length leftoverBS

deserHex :: (Typeable a, Bin.Serialize a) => T.Text -> JSON.Parser a
deserHex = either
   (fail . ("failed to decode hex: " ++)) return .
   deserEither . fromHexBS . encodeUtf8

serHex :: Bin.Serialize a => a -> T.Text
serHex = decodeUtf8 . toHexBS . Bin.encode


-- dummyPubkey :: HC.PubKeyC
-- dummyPubkey = mkDummyPubkey "0323dcd0a7481cb90e3583923701b14d0b6757ebd76bf5b49e696c61f193d7a489"

mkDummyPubkey :: B.ByteString -> HC.PubKeyC
mkDummyPubkey hexStr =
    either (error "mkDummyPubkey: Invalid dummy pubkey data") id (Bin.decode pkBS)
        where pkBS = fst $ B16.decode hexStr


mkDummySig :: B.ByteString -> HC.Signature
mkDummySig hexStr =
    fromMaybe (error "dummySig: Invalid dummy sig data") (HC.decodeDerSig sigBS)
        where sigBS = fst $ B16.decode hexStr

dummySig :: HC.Signature
dummySig = mkDummySig "304402204202cdb61cb702aa62de312a8e5eada817d90c4e26c8b696780b14d1576f204f02203c134d0acb057d917508ca9baab241a4f66ebea32f7acceeaf621a334927e17701"