module Bitcoin.BIP32.DetDerive
( module Bitcoin.BIP32.DetDerive
, module Bitcoin.BIP32.Types
)
where

import Bitcoin.Internal.Types
import Bitcoin.BIP32.Types
import Data.Serialize                     as Bin
import Data.Serialize.Get                 as BinGet
import qualified Network.Haskoin.Crypto   as HC
import Network.Haskoin.Crypto             (DerivPathI(..))


class DerivationSeed a where
    toDerivSeed :: a -> ByteString

-- | Deterministically derive a ChildKey from a sourceKey and a 'DerivationSeed'
detDerive
    :: forall a sourceKey t k derivPath.
    ( DerivationSeed a
    , DerivPathElem derivPath
    , IsChildKey sourceKey t k derivPath
    )
    => sourceKey
    -> a
    -> t k
detDerive sourceKey seed =
    mkChild sourceKey path
  where
    path :: derivPath
    path = toPath (to128bitSeed seed)
    toPath (w1,w2,w3,w4) = derivBegin w1 /:| w2 /:| w3 /:| w4

to128bitSeed
    :: DerivationSeed a
    => a
    -> (Word32, Word32, Word32, Word32)
to128bitSeed a = either (\e -> error $ "to128bitSeed bug: " ++ e) id $
    BinGet.runGet getWords (Bin.encode . HC.hash256 . toDerivSeed $ a)
  where
    getWords = do
        w1 <- BinGet.getWord32be
        w2 <- BinGet.getWord32be
        w3 <- BinGet.getWord32be
        w4 <- BinGet.getWord32be
        return (w1,w2,w3,w4)

-- ########
-- ### Internal

--newtype KeyIndex = KeyIndex Word32


class DerivPathElem derivPath where
    derivBegin :: Word32 -> derivPath
    (/:|)  :: derivPath -> Word32 -> derivPath

instance DerivPathElem HC.SoftPath where
    derivBegin i = Deriv :/ cap31Bit i
    (/:|) p i = p :/ cap31Bit i

instance DerivPathElem HC.HardPath where
    derivBegin i = Deriv :| cap31Bit i
    (/:|) p i = p :| cap31Bit i

cap31Bit :: Word32 -> Word32
cap31Bit = (`mod` (0x80000000 :: Word32))

--class AsDerivePath t derivPath where
--    toDerivPath :: t -> derivPath

--instance DerivPathElem i derivPath => AsDerivePath (i, i, i, i) derivPath where
--    toDerivPath (w1,w2,w3,w4) = derivBegin w1 /:| w2 /:| w3 /:| w4