-- This file is part of Bindings-bfd.
--
-- Copyright (C) 2010 Michael Nelson
--
-- Bindings-bfd is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- Bindings-bfd 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 Lesser General Public License for more details.

-- You should have received a copy of the GNU Lesser General Public License
-- along with Bindings-bfd.  If not, see <http://www.gnu.org/licenses/>.

{-# LANGUAGE DeriveDataTypeable #-}

module Bindings.Bfd.Exception where

import Control.Exception

import Data.Typeable

import Foreign.C
import Foreign.Ptr

import Bindings.Bfd.Target


type FunctionName = String
type FormatName   = String
type ErrorMsg     = String

data BfdException = NoError                   FunctionName FilePath   TargetName
                  | SystemCall                FunctionName FilePath   TargetName ErrorMsg
                  | InvalidTarget             FunctionName FilePath   TargetName
                  | WrongFormat               
                  | WrongObjectFormat         
                  | InvalidOperation          
                  | NoMemory                  
                  | NoSymbols                 
                  | NoArmap                   
                  | NoMoreArchivedFiles       
                  | MalformedArchive          
                  | FileNotRecognized         FunctionName FormatName
                  | FileAmbiguouslyRecognized 
                  | NoContents                
                  | NonrepresentableSection   
                  | NoDebugSection            
                  | BadValue                  
                  | FileTruncated             
                  | FileTooBig                
                  | OnInput                   
                  | InvalidErrorCode          
     deriving (Typeable)

instance Show BfdException where
   show (NoError                   fn fp tn    ) = fp ++ ": " ++ fn ++ ": '" ++ tn ++ "': no error (report this as a bug)"
   show (SystemCall                fn fp tn msg) = fp ++ ": " ++ fn ++ ": '" ++ tn ++ "': system call (" ++ msg ++ ")"
   show (InvalidTarget             fn fp tn    ) = fp ++ ": " ++ fn ++ ": '" ++ tn ++ "': invalid target"
   show (WrongFormat                           ) = "3"
   show (WrongObjectFormat                     ) = "4"
   show (InvalidOperation                      ) = "5"
   show (NoMemory                              ) = "6"
   show (NoSymbols                             ) = "7"
   show (NoArmap                               ) = "8"
   show (NoMoreArchivedFiles                   ) = "9"
   show (MalformedArchive                      ) = "10"
   show (FileNotRecognized         fn ff       ) = ff ++ ": " ++ fn ++ ": " ++ "file format not recognized"
   show (FileAmbiguouslyRecognized             ) = "12"
   show (NoContents                            ) = "13"
   show (NonrepresentableSection               ) = "14"
   show (NoDebugSection                        ) = "15"
   show (BadValue                              ) = "16"
   show (FileTruncated                         ) = "17"
   show (FileTooBig                            ) = "18"
   show (OnInput                               ) = "19"
   show (InvalidErrorCode                      ) = "20"

instance Exception BfdException where


throwExceptionIfNull
   :: FunctionName
   -> FilePath
   -> TargetName
   -> IO (Ptr a)
   -> IO (Ptr a)
throwExceptionIfNull fn fp tn f =
   do
      res <- f
      if res == nullPtr 
         then throwException fn fp tn
         else return res

throwExceptionIfFalse
   :: FunctionName
   -> FormatName
   -> IO Bool
   -> IO Bool
throwExceptionIfFalse fn ff f =
   do
      res <- f
      if res == False
         then throwException fn ff ""
         else return res

throwException
   :: String
   -> String
   -> String
   -> IO a
throwException s1 s2 s3 =
   do
      errNum <- c_bfd_get_error
      errMsg <- peekCString (c_bfd_errmsg errNum)
      let
         e =
            case errNum of
                0  -> NoError                   s1 s2 s3
                1  -> SystemCall                s1 s2 s3 errMsg
                2  -> InvalidTarget             s1 s2 s3
                3  -> WrongFormat               
                4  -> WrongObjectFormat         
                5  -> InvalidOperation          
                6  -> NoMemory                  
                7  -> NoSymbols                
                8  -> NoArmap                  
                9  -> NoMoreArchivedFiles       
                10 -> MalformedArchive          
                11 -> FileNotRecognized         s1 s2
                12 -> FileAmbiguouslyRecognized
                13 -> NoContents               
                14 -> NonrepresentableSection   
                15 -> NoDebugSection           
                16 -> BadValue                
                17 -> FileTruncated            
                18 -> FileTooBig              
                19 -> OnInput                 
                _  -> InvalidErrorCode        
      throwIO e


foreign import ccall unsafe "bfd.h bfd_get_error" c_bfd_get_error
   :: IO CInt

foreign import ccall unsafe "bfd.h bfd_errmsg" c_bfd_errmsg
   :: CInt
   -> CString