module Data.Blockchain.Crypto.HashTree
    ( HashTreeRoot
    , unHashTreeRoot
    , hashTreeRoot
    ) where

import qualified Data.Aeson                  as Aeson

import           Data.Blockchain.Crypto.Hash

newtype HashTreeRoot a = HashTreeRoot { unHashTreeRoot :: Hash a }
  deriving (Aeson.FromJSON, Aeson.ToJSON, Eq, Ord, Show)

-- Note: hash tree constructed with extra leaves at end of tree.
-- This is NOT compatible with the Bitcoin implementation.
--      ┌───┴──┐       ┌────┴───┐         ┌─────┴─────┐
--   ┌──┴──┐   │    ┌──┴──┐     │      ┌──┴──┐     ┌──┴──┐
-- ┌─┴─┐ ┌─┴─┐ │  ┌─┴─┐ ┌─┴─┐ ┌─┴─┐  ┌─┴─┐ ┌─┴─┐ ┌─┴─┐   │
--    (5-leaf)         (6-leaf)             (7-leaf)

hashTreeRoot :: forall a. ToHash a => [a] -> HashTreeRoot a
hashTreeRoot = HashTreeRoot . buildHash
  where
    buildHash :: [a] -> Hash a
    buildHash []  = mempty
    buildHash [x] = hash x
    buildHash xs  = mappend (buildHash left) (buildHash right)
      where
        (left, right) = splitAt i xs
        i = until (\x -> x * 2 >= length xs) (*2) 1
