-- |
-- A backend-aware component, which prepares statements.
module Hasql.Postgres.StatementPreparer where

import Hasql.Postgres.Prelude
import qualified Data.HashTable.IO as Hashtables
import qualified Database.PostgreSQL.LibPQ as PQ
import qualified Hasql.Postgres.ResultParser as Result
import qualified Hasql.Postgres.ResultHandler as ResultHandler
import qualified Data.ByteString as B
import qualified Data.ByteString.Builder as BB
import qualified Data.ByteString.Lazy as BL



type StatementPreparer =
  (PQ.Connection, IORef Word16, Hashtables.BasicHashTable LocalKey RemoteKey)


-- |
-- Local statement key.
data LocalKey =
  LocalKey !ByteString ![PQ.Oid]
  deriving (Show, Eq)

-- |
-- Optimized by ignoring the OIDs.
instance Hashable LocalKey where
  hashWithSalt s (LocalKey b _) = hashWithSalt s b


-- |
-- Remote statement key.
type RemoteKey =
  ByteString


new :: PQ.Connection -> IO StatementPreparer
new connection =
  (,,) <$> pure connection <*> newIORef 0 <*> Hashtables.new

prepare :: ByteString -> [PQ.Oid] -> StatementPreparer -> IO RemoteKey
prepare s tl (c, counter, table) =
  do
    let k = LocalKey s tl
    r <- Hashtables.lookup table k
    case r of
      Just r -> 
        return r
      Nothing ->
        do
          w <- readIORef counter
          n <- return (BL.toStrict $ BB.toLazyByteString $ BB.word16Dec w)
          ResultHandler.unit =<< Result.parse c =<< PQ.prepare c n s (partial (not . null) tl)
          Hashtables.insert table k n
          writeIORef counter (succ w)
          return n