{-# LANGUAGE TypeFamilies, RecordWildCards, RankNTypes, MultiParamTypeClasses,FlexibleInstances,FlexibleContexts,FunctionalDependencies,DefaultSignatures,GADTs,UnicodeSyntax,OverloadedStrings #-}
module Database.KVS (
  KVS (..), EnumeratableKVS(..), WipableKVS (..),
  BucketKVS(..), WrappedKVS,
  lookupWithDefault,
  wrap, wrapBinary, wrapShow, wrapJSON) where

import Prelude hiding(lookup)

import Control.Monad.Trans (lift)
import Control.Monad.Trans.Resource
import Data.Maybe (fromJust, fromMaybe)
import Data.Default
import qualified Data.ByteString.Lazy as LBS (ByteString,append)
import qualified Data.ByteString.Char8 as BS8 (ByteString,pack,unpack)
import qualified Data.Binary as BIN (Binary, encode, decode)
import Data.Conduit (Source, ($=), await, yield)
import qualified Data.Conduit.List as C (map)

import qualified Data.Aeson as AE (ToJSON, encode, FromJSON, decode)



{-

-}
class (Monad s) => KVS c s k v | c -> s, c -> k, c -> v where
  insert :: c -> k -> v -> s ()
  
  lookup :: c -> k -> s (Maybe v)
  lookup c k = accept c k (return Nothing) (return . Just)
  
  -- | Delete specified key-value pair from container.
  delete :: c                   -- ^ Container
            -> k                -- ^ Key
            -> s (Maybe Bool)   -- ^ success, failed or unknown

  -- | Lookup value
  accept :: c                   -- ^ Container
            -> k                -- ^ Key
            -> s b              -- ^ Action for key not found
            -> (v -> s b)       -- ^ Action for key found (with Lock)
            -> s b
  
class Monad s => EnumeratableKVS c s k v | c -> s, c -> k, c-> v where
  keys :: c -> Source (ResourceT s) k
  keys c = elemsWithKey c $= C.map fst
  elems :: c -> Source (ResourceT s) v
  elems c = elemsWithKey c $= C.map snd
  elemsWithKey :: c -> Source (ResourceT s) (k,v)
  default elemsWithKey :: KVS c s k v => c -> Source (ResourceT s) (k,v)
  elemsWithKey c = keys c $= go where
    go = do 
      k <- await
      case k of
        Nothing -> return ()
        Just k' -> do
          v <- lift $ lift $ lookup c k'
          case v of
            Nothing -> go
            Just v' -> yield (k',v') >> go

class (Monad s) => WipableKVS c s | c -> s where
  wipe :: c -> s ()



lookupWithDefault :: (Monad s, Default v, KVS c s k v) => c -> k -> s v
lookupWithDefault c k = lookup c k >>= return . fromMaybe def
{-# INLINE lookupWithDefault #-}  


{-
-}
data BucketKVS c s v where
  BucketKVS :: KVS c s LBS.ByteString v  LBS.ByteString  c  BucketKVS c s v

wrapNamespace  LBS.ByteString  LBS.ByteString  LBS.ByteString
wrapNamespace ns k = ns `LBS.append` "::" `LBS.append` k


instance Monad s  KVS (BucketKVS c s v) s LBS.ByteString v where
  insert (BucketKVS b x) k v = insert x (wrapNamespace b k) v
  delete (BucketKVS b x) = delete x . wrapNamespace b
  accept (BucketKVS b x) = accept x . wrapNamespace b

instance (Monad s,WipableKVS c s)  WipableKVS (BucketKVS c s v) s where
  wipe (BucketKVS _ x) = wipe x
--instance (Monad s, EnumeratableKVS c s k v) ⇒ EnumeratableKVS (BucketKVS c s k) s k v where -- キーからプレフィックスを剥がすのが面倒いですよ
  
  

{-
-}
data WrappedKVS c k' v' (s :: * -> *) k v
  = WrappedKVS {
    fk :: k -> k',
    fk' :: k' -> k,
    fv :: v -> v',
    fv' :: v' -> v,
    wrapped :: c 
  }

instance (KVS c s k' v', Monad s) => KVS (WrappedKVS c k' v' s k v) s k v where
  insert (WrappedKVS {..}) k v = insert wrapped (fk k) (fv v)
  lookup (WrappedKVS {..}) k =
    lookup wrapped (fk k) >>= return . maybe Nothing (Just . fv')
  delete (WrappedKVS {..}) = delete wrapped . fk
  accept (WrappedKVS {..}) k f g = accept wrapped (fk k) f (g . fv')

instance (Monad s, WipableKVS c s) => WipableKVS (WrappedKVS c k' v' s k v) s where
  wipe (WrappedKVS {..}) = wipe wrapped
  
instance (Monad s, EnumeratableKVS c s k' v') => EnumeratableKVS (WrappedKVS c k' v' s k v) s k v where
  elems (WrappedKVS {..}) = elems wrapped $= C.map fv'
  elemsWithKey (WrappedKVS {..}) = elemsWithKey wrapped $= C.map (\(x,y) -> (fk' x, fv' y))
  

wrap :: KVS a s k' v' => (k -> k') -> (k' -> k) -> (v -> v') -> (v' -> v) -> a -> WrappedKVS a k' v' s k v
wrap = WrappedKVS

wrapBinary :: (KVS a s LBS.ByteString LBS.ByteString, BIN.Binary k, BIN.Binary v) => a -> WrappedKVS a LBS.ByteString LBS.ByteString s k v
wrapBinary = WrappedKVS BIN.encode BIN.decode BIN.encode BIN.decode

wrapShow :: (KVS a s LBS.ByteString LBS.ByteString, Show k, Show v, Read k, Read v) => a -> WrappedKVS a BS8.ByteString BS8.ByteString s k v
wrapShow = WrappedKVS (BS8.pack . show) (read . BS8.unpack) (BS8.pack . show) (read . BS8.unpack)

wrapJSON :: (KVS a s LBS.ByteString LBS.ByteString, AE.FromJSON k, AE.ToJSON k, AE.FromJSON v, AE.ToJSON v) => a -> WrappedKVS a LBS.ByteString LBS.ByteString s k v
wrapJSON = WrappedKVS AE.encode (fromJust . AE.decode) AE.encode (fromJust . AE.decode)