{- arch-tag: Python low-level utilities
Copyright (C) 2005 John Goerzen <jgoerzen@complete.org>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-}

{- |
   Module     : Python.Utils
   Copyright  : Copyright (C) 2005 John Goerzen
   License    : GNU GPL, version 2 or above

   Maintainer : John Goerzen,
   Maintainer : jgoerzen\@complete.org
   Stability  : provisional
   Portability: portable

Python low-level utilities

Written by John Goerzen, jgoerzen\@complete.org

Please use sparingly and with caution.  The documentation for their behavior
should be considered to be the source code.
-}

module Python.Utils (-- * Objects
                     fromCPyObject,
                     withPyObject,
                     maybeWithPyObject,
                     -- * Exceptions
                     raisePyException,
                     checkCInt,
                     -- * Environment
                     getDefaultGlobals,
                     pyImport_AddModule,
                     pyModule_GetDict,
                     py_incref
                    )
    where
import Python.Types (
                      CPyObject(..)
                    , PyObject(..)
                    , PyException(excType, excValue, excTraceBack, excFormatted, PyException)
                    )
import Python.ForeignImports (
                      cNone
                    , cpyImport_AddModule
                    , cpyModule_GetDict
                    , pyErr_Clear
                    , pyErr_Fetch
                    , pyErr_NormalizeException
                    , py_decref
                    , py_incref
                    , py_incref
                    )

import Foreign.C.Types (CInt)
import Foreign.C (withCString)
import Foreign (peek, Ptr, newForeignPtr, nullPtr, alloca, withForeignPtr)
import Control.OldException (throwDyn)

{- | Convert a Ptr 'CPyObject' to a 'PyObject'. -}
fromCPyObject :: Ptr CPyObject -> IO PyObject
fromCPyObject po =
    if po == nullPtr
       then raisePyException
       else do fp <- newForeignPtr py_decref po
               return $ PyObject fp

{- | Called to make sure the passed CInt isn't -1.  Raise an exception if
it is. -}
checkCInt :: CInt -> IO CInt
checkCInt x = 
    if x == (-1)
       then raisePyException
       else return x

{- | Called when a Python exception has been detected.  It will raise
the exception in Haskell. -}
raisePyException :: IO a
raisePyException =
    let noneorptr cval = if cval == nullPtr
                            then do p <- cNone
                                    fromCPyObject p
                            else fromCPyObject cval
        in alloca (\typeptr -> alloca (\valptr -> alloca (\tbptr ->
       do pyErr_Fetch typeptr valptr tbptr
          pyErr_NormalizeException typeptr valptr tbptr
          ctype <- peek typeptr
          cval <- peek valptr
          ctb <- peek tbptr
          otype <- noneorptr ctype
          oval <- noneorptr cval
          otb <- noneorptr ctb
          --seq otype $ return ()
          --seq oval $ return ()
          --seq otb $ return ()
          let exc = PyException {excType = otype, excValue = oval,
                                 excTraceBack = otb,
                                 excFormatted = ""}
          pyErr_Clear
          throwDyn exc
                   )))
{-
    do cpy <- getexc
       let (exc, val, tb) = cpy
       --pyErr_Print
       fail "Python Error!"
    where getexc = do cexc <- hspy_getexc
                      exc <- peekArray 3 cexc
                      exc2 <- mapM fromCPyObject exc
                      case exc2 of
                               [x, y, z] -> return (x, y, z)
                               _ -> fail "Got unexpected number of elements"
-}  
  
{- | Uses a 'PyObject' in a function that needs Ptr 'CPyObject'. -}
withPyObject :: PyObject -> (Ptr CPyObject -> IO b) -> IO b
withPyObject (PyObject x) = withForeignPtr x    

{- | Same as 'withPyObject', but uses nullPtr if the input is Nothing. -}
maybeWithPyObject :: Maybe PyObject -> (Ptr CPyObject -> IO b) -> IO b
maybeWithPyObject Nothing func = func nullPtr
maybeWithPyObject (Just x) y = withPyObject x y

{- | Returns the default globals environment. -}
getDefaultGlobals :: IO PyObject
getDefaultGlobals = 
    do m <- pyImport_AddModule "__main__"
       pyModule_GetDict m
       
{- | Wrapper around C PyImport_AddModule, which looks up an existing module -}
pyImport_AddModule :: String -> IO PyObject
pyImport_AddModule x =
    withCString x (\cstr -> 
        do r <- cpyImport_AddModule cstr
           py_incref r
           fromCPyObject r
                  )

{- | Gets the dict associated with a module. -}
pyModule_GetDict :: PyObject -> IO PyObject
pyModule_GetDict x =
    withPyObject x (\cpyo -> 
       do r <- cpyModule_GetDict cpyo
          py_incref r
          fromCPyObject r)