{-# LANGUAGE ForeignFunctionInterface #-}
module Crypto.OpenSSL.BN where

import           Crypto.OpenSSL.BN.Foreign
import           Crypto.OpenSSL.Misc
import           Foreign.Ptr
import           Foreign.ForeignPtr

import           Data.Bits
import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as B

withIntegerAsBN :: Integer -> (Ptr BIGNUM -> IO a) -> IO a
withIntegerAsBN i f = do
    bn <- withForeignPtr fptr $ \bsPtr ->
            ssl_bn_bin2 (castPtr (bsPtr `plusPtr` o)) (fromIntegral len) nullPtr
    foreignBn <- newForeignPtr ssl_bn_free bn
    withForeignPtr foreignBn f
  where (fptr, o, len) = B.toForeignPtr bs
        bs = B.reverse $ B.unfoldr fdivMod256 i
        fdivMod256 0 = Nothing
        fdivMod256 n = Just (fromIntegral a,b) where (b,a) = divMod256 n
        divMod256 :: Integer -> (Integer, Integer)
        divMod256 n = (n `shiftR` 8, n .&. 0xff)

bnToInt :: Ptr BIGNUM -> IO Integer
bnToInt bn = do
    bytes <- ssl_bn_num_bytes bn
    bs    <- B.create (fromIntegral bytes) $ \bufPtr ->
                check $ ssl_bn_2bin bn (castPtr bufPtr)
    return $ os2ip bs
  where os2ip = B.foldl' (\a b -> (256 * a) .|. (fromIntegral b)) 0

withBnCtxNew :: (Ptr BN_CTX -> IO a) -> IO a
withBnCtxNew f = do
    -- UGLY, can do something more clever than this ..
    fptr <- ssl_bn_ctx_new >>= newForeignPtr ssl_bn_ctx_free
    withForeignPtr fptr f

withBnNew :: (Ptr BIGNUM -> IO a) -> IO a
withBnNew f = do
    fptr <- ssl_bn_new >>= newForeignPtr ssl_bn_free
    withForeignPtr fptr f