{-# LANGUAGE AllowAmbiguousTypes    #-}
{-# LANGUAGE EmptyDataDecls         #-}
{-# LANGUAGE FlexibleContexts       #-}
{-# LANGUAGE FlexibleInstances      #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses  #-}
{-# LANGUAGE TypeSynonymInstances   #-}

module Blockchain.Database.KeyVal (
    PointedKeyValDB(..),
    KeyValMPLevelDB,
    KeyValMPMap
  ) where

import           Blockchain.Database.MerklePatricia
import           Blockchain.Database.MerklePatriciaMem
import           Control.Monad.Trans
import           Control.Monad.Trans.Resource
import           Control.Monad.Trans.State

{-
  A general interface for a key value "database" with a distinguished element
  representing a "snapshot" or "summary" of the database. The "snapshot" can also
  include the database handle or connection string.

  Below, a and b represent key and value types respectively, and c is the type of the
  "snapshot".

  The unfortunate parameter t represents in some cases a file path for the on disk
  db.
-}

class (Monad m) => PointedKeyValDB m a b c t | c -> t where
    getKV :: Monad m => a-> m (Maybe b)
    putKV :: Monad m => (a,b) -> m c
    deleteKV :: Monad m => a -> m c
    emptyKV :: Monad m => t -> m c

type KeyValMPLevelDB m = StateT MPDB (ResourceT m)
type KeyValMPMap m = StateT MPMem m

data Void

instance (
           Monad m,
           MonadBaseControl IO m,
           MonadThrow m,
           MonadIO m
         )
        => PointedKeyValDB (KeyValMPLevelDB m) Key Val MPDB String where

    getKV key = do
        db <- get
        runResourceT $ getKeyVal db key

    putKV kvPair = do
        db <- get
        runResourceT $ putKeyVal db (fst kvPair) (snd kvPair)

    deleteKV key = do
        db <- get
        runResourceT $ deleteKey db key

    emptyKV path = liftIO $ runResourceT $ openMPDB path  -- fix

instance (
           Monad m
         )
        => PointedKeyValDB (KeyValMPMap m) Key Val MPMem Void where

    getKV key = do
        db <- get
        getKeyValMem db key

    putKV kvPair = do
        db <- get
        putKeyValMem db (fst kvPair) (snd kvPair)

    deleteKV key = do
        db <- get
        deleteKeyMem db key

    emptyKV _ = return initializeBlankMem             -- fix