 {-# LANGUAGE ForeignFunctionInterface #-}
 {-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE OverloadedStrings #-}
 
 module GDAL.Internal.OSR (
     SpatialReference


coordinateTransformation
  :: SpatialReference -> SpatialReference -> Maybe CoordinateTransformation
coordinateTransformation source target =
  unsafePerformIO $
  withSpatialReference source $ \pSource ->
  withSpatialReference target $ \pTarget ->
  maybeNewCoordinateTransformation pSource pTarget
{-# NOINLINE coordinateTransformation #-}

maybeNewCoordinateTransformation
  :: Ptr SpatialReference
  -> Ptr SpatialReference
  -> IO (Maybe CoordinateTransformation)
maybeNewCoordinateTransformation pSource pTarget =
  bracketOnError alloc freeIfNotNull go
  where
    alloc =
      {#call unsafe OCTNewCoordinateTransformation as ^#} pSource pTarget
    go p
      | p==nullPtr = return Nothing
      | otherwise  = liftM (Just . CoordinateTransformation)

{-# NOINLINE srsFromEPSG #-}

fromImporter
  :: (SpatialReference -> a -> IO CInt) -> a
  -> Either OGRException SpatialReference
fromImporter f s = unsafePerformIO $ do
  r <- emptySpatialRef
  try (checkOGRError (f r s) >> return r)
{-# NOINLINE fromImporter #-}


{#fun ImportFromProj4 as ^
   { withSpatialReference* `SpatialReference'
   , unsafeUseAsCString* `ByteString'} -> `CInt' #}

{#fun ImportFromEPSG as ^
   {withSpatialReference* `SpatialReference', `Int'} -> `CInt' #}

{#fun ImportFromXML as ^
   { withSpatialReference* `SpatialReference'
   , unsafeUseAsCString* `ByteString'} -> `CInt' #}

{#fun pure unsafe IsGeographic as ^
   {withSpatialReference* `SpatialReference'} -> `Bool'#}

{#fun pure unsafe IsLocal as ^
   {withSpatialReference* `SpatialReference'} -> `Bool'#}

{#fun pure unsafe IsProjected as ^
   {withSpatialReference* `SpatialReference'} -> `Bool'#}

{#fun pure unsafe IsSameGeogCS as ^
   { withSpatialReference* `SpatialReference'
   , withSpatialReference* `SpatialReference'} -> `Bool'#}

{#fun pure unsafe IsSame as ^
   { withSpatialReference* `SpatialReference'
   , withSpatialReference* `SpatialReference'} -> `Bool'#}

instance Eq SpatialReference where
  (==) = isSame

getLinearUnits :: SpatialReference -> (Double, String)
getLinearUnits =
  unsafePerformIO . getUnitsWith {#call unsafe OSRGetLinearUnits as ^#}

getAngularUnits :: SpatialReference -> (Double, String)
getAngularUnits =
  unsafePerformIO . getUnitsWith {#call unsafe OSRGetAngularUnits as ^#}

getUnitsWith
  :: (Ptr SpatialReference -> Ptr CString -> IO CDouble)
  -> SpatialReference
  -> IO (Double, String)
getUnitsWith fun s = alloca $ \p -> do
  value <- withSpatialReference s (\s' -> fun s' p)
  ptr <- peek p
  units <- peekCString ptr
  return (realToFrac value, units)

withMaybeSRAsCString :: Maybe SpatialReference -> (CString -> IO a) -> IO a
withMaybeSRAsCString Nothing    = ($ nullPtr)
