{-# LANGUAGE TypeFamilies, FlexibleInstances #-}
module Data.NetCDF.Utils where

import Data.NetCDF.Raw
import Data.NetCDF.Types

import Control.Monad.Trans.Class
import Control.Monad.IO.Class
import Control.Monad.Trans.Reader
import Control.Monad.Trans.Except

-- | Simple synonym to tidy up signatures.
type NcIO a = IO (Either NcError a)

-- | Monad stack to help with handling errors from FFI functions.
type Access a = ReaderT (String, FilePath) (ExceptT NcError IO) a

-- | Utility function to run access monad stack.
runAccess :: String -> String -> Access a -> NcIO a
runAccess f p = runExceptT . flip runReaderT (f, p)

-- | Utility class to make dealing with status return from foreign
-- NetCDF functions a little easier.
class Checkable a where
  type OutType a :: *
  status :: a -> Int
  proj :: a -> OutType a

instance Checkable Int where
  type OutType Int = ()
  status s = s
  proj _ = ()

instance Checkable (Int, a) where
  type OutType (Int, a) = a
  status (s, _) = s
  proj (_, a) = a

instance Checkable (Int, a, b) where
  type OutType (Int, a, b) = (a, b)
  status (s, _, _) = s
  proj (_, a, b) = (a, b)

instance Checkable (Int, a, b, c) where
  type OutType (Int, a, b, c) = (a, b, c)
  status (s, _, _, _) = s
  proj (_, a, b, c) = (a, b, c)

instance Checkable (Int, a, b, c, d) where
  type OutType (Int, a, b, c, d) = (a, b, c, d)
  status (s, _, _, _, _) = s
  proj (_, a, b, c, d) = (a, b, c, d)

instance Checkable (Int, a, b, c, d, e) where
  type OutType (Int, a, b, c, d, e) = (a, b, c, d, e)
  status (s, _, _, _, _, _) = s
  proj (_, a, b, c, d, e) = (a, b, c, d, e)

-- | Perform an IO action that returns a tuple with an integer status
-- and some return values, processing errors.
chk :: Checkable a => IO a -> Access (OutType a)
chk act = do
  res <- lift $ liftIO $ act
  let st = status res
      val = proj res
  (f, p) <- ask
  lift $ if st == 0
    then ExceptT (return $ Right val)
    else ExceptT (return $ Left $ NcError f st (nc_strerror st) p)