{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE StandaloneDeriving #-}
-- | This module implements data structures and function related to the
-- metadata of the concurrent page allocator.
module Database.Haskey.Alloc.Concurrent.Internal.Meta where

import Data.Binary (Binary)
import Data.Proxy (Proxy)
import Data.Typeable (Typeable)

import GHC.Generics (Generic)

import Data.BTree.Impure.Internal.Structures
import Data.BTree.Primitives

import Database.Haskey.Alloc.Concurrent.Internal.Environment
import Database.Haskey.Alloc.Concurrent.Internal.FreePages.Tree
import Database.Haskey.Alloc.Concurrent.Internal.Overflow
import Database.Haskey.Store

-- | User-defined data root stored inside 'ConcurrentMeta'.
--
-- This can be a user-defined collection of 'Tree' roots.
class Value root => Root root where

instance (Key k, Value v) => Root (Tree k v) where

-- | Data type used to point to the most recent version of the meta data.
data CurrentMetaPage = Meta1 | Meta2

-- | Meta data of the page allocator.
--
-- The @root@ type parameter should be a user-defined collection of 'Tree'
-- roots, instantiating the 'Root' type class.
--
-- To store store a single tree, use @ConcurrentMeta (Tree k v)@.
data ConcurrentMeta root = ConcurrentMeta {
    concurrentMetaRevision :: TxId
  , concurrentMetaDataNumPages :: S 'TypeData PageId
  , concurrentMetaIndexNumPages :: S 'TypeIndex PageId
  , concurrentMetaRoot :: root
  , concurrentMetaDataFreeTree :: S 'TypeData FreeTree
  , concurrentMetaIndexFreeTree :: S 'TypeIndex FreeTree
  , concurrentMetaOverflowTree :: OverflowTree
  , concurrentMetaDataCachedFreePages :: S 'TypeData [FreePage]
  , concurrentMetaIndexCachedFreePages :: S 'TypeIndex [FreePage]
  } deriving (Generic, Typeable)

deriving instance (Show root) => Show (ConcurrentMeta root)

instance (Binary root) => Binary (ConcurrentMeta root) where

-- | A class representing the storage requirements of the page allocator.
--
-- A store supporting the page allocator should be an instance of this class.
class StoreM FilePath m => ConcurrentMetaStoreM m where
    -- | Write the meta-data structure to a certain page.
    putConcurrentMeta :: Root root
                      => FilePath
                      -> ConcurrentMeta root
                      -> m ()

    -- | Try to read the meta-data structure from a handle, or return 'Nothing'
    -- if the handle doesn't contain a meta page.
    readConcurrentMeta :: Root root
                       => FilePath
                       -> Proxy root
                       -> m (Maybe (ConcurrentMeta root))