{-# LANGUAGE ExistentialQuantification, Rank2Types #-}
-----------------------------------------------------------
-- |
-- Module      :  DriverAPI
-- Copyright   :  Anders Hockersten (c), chucky@dtek.chalmers.se
-- License     :  BSD-style
--
-- Maintainer  :  chucky@dtek.chalmers.se
-- Stability   :  experimental
-- Portability :  portable
--
-- This exports an API that all drivers must conform to. It
-- is used by the end user to load drivers either dynamically
-- or statically.
-----------------------------------------------------------

module Database.HaskellDB.DriverAPI (
				     DriverInterface(..),
                                     MonadIO, 
				     defaultdriver,
                                     getOptions,
                                     getAnnotatedOptions,
                                     getGenerator
				    ) where

import Database.HaskellDB.Database (Database)

import Database.HaskellDB.Sql.Generate (SqlGenerator)
import Database.HaskellDB.Sql.Default (defaultSqlGenerator)
import Database.HaskellDB.Sql.MySQL as MySQL
import Database.HaskellDB.Sql.PostgreSQL as PostgreSQL
import Database.HaskellDB.Sql.SQLite as SQLite


import Control.Monad (liftM)
import Control.Monad.Trans (MonadIO)

-- | Interface which drivers should implement.
--   The 'connect' function takes some driver specific name, value pairs
--   use to setup the database connection, and a database action to run.
--   'requiredOptions' lists all required options with a short description,
--   that is printed as help in the DBDirect program.
data DriverInterface = DriverInterface
    { connect :: forall m a. MonadIO m => [(String,String)] -> (Database -> m a) -> m a,
      requiredOptions :: [(String, String)]
    }

-- | Default dummy driver, real drivers should overload this
defaultdriver :: DriverInterface 
defaultdriver =
    DriverInterface {
        connect = error "DriverAPI.connect: not implemented",
        requiredOptions = error "DriverAPI.requiredOptions: not implemented"}

-- | Can be used by drivers to get option values from the given
--   list of name, value pairs.
getOptions ::Monad m => [String] -- ^ names of options to get
           -> [(String,String)] -- ^ options given
           -> m [String] -- ^ a list of the same length as the first argument
                         --   with the values of each option. Fails in the given
                         --   monad if any options is not found.
getOptions [] _ = return []
getOptions (x:xs) ys = 
    case lookup x ys of
                     Nothing -> fail $ "Missing field " ++ x
                     Just v -> liftM (v:) $ getOptions xs ys

-- | Can be used by drivers to get option values from the given
--   list of name, value pairs.
--   It is intended for use with the 'requiredOptions' value of the driver.
getAnnotatedOptions :: Monad m =>
              [(String,String)] -- ^ names and descriptions of options to get
           -> [(String,String)] -- ^ options given
           -> m [String] -- ^ a list of the same length as the first argument
                         --   with the values of each option. Fails in the given
                         --   monad if any options is not found.
getAnnotatedOptions opts = getOptions (map fst opts)

-- | Gets an 'SqlGenerator' from the "generator" option in the given list.
--   Currently available generators: "mysql", "postgresql", "sqlite", "default"
getGenerator :: Monad m => 
                [(String,String)] -- ^ options given
           -> m SqlGenerator -- ^ An SQL generator. If there was no
                             --   "generator" option, the default is used.
                             -- Fails if the generator is unknown
getGenerator opts = maybe (return defaultSqlGenerator) f $ lookup "generator" opts
    where f n = maybe (fail msg) return $ lookup n generators
              where msg = "Unknown SqlGenerator: " ++ n

generators :: [(String,SqlGenerator)]
generators = [("mysql",      MySQL.generator),
              ("postgresql", PostgreSQL.generator),
              ("sqlite",     SQLite.generator),
              ("default",    defaultSqlGenerator)]