module Control.Exception.Common where

-- import Control.Exception.Safe (Exception, MonadThrow, SomeException, throwM)
import Control.Exception
import Control.Monad.Catch (MonadThrow (..))
import Data.Typeable -- (TypeRep, Typeable, typeRep)

import Data.Sparse.Utils


-- | Errors associated with partial functions
data PartialFunctionError = EmptyList String deriving (Eq, Typeable)
instance Show PartialFunctionError where
  show (EmptyList s) = unwords [s, ": empty list"]
instance Exception PartialFunctionError


-- | Input errors
data InputError = NonNegError String Int deriving (Eq, Typeable)
instance Show InputError where
  show (NonNegError s i) = unwords [s, ": parameter must be nonnegative, instead I got", show i]
instance Exception InputError



-- | Out of bounds index errors
data OutOfBoundsIndexError i = OOBIxError String i
                             | OOBIxsError String [i]
                             | OOBNoCompatRows String (i,i) deriving (Eq, Typeable)
instance Show i => Show (OutOfBoundsIndexError i) where
  show (OOBIxError e i) = unwords [e, ": index", show i,"out of bounds"]
  show (OOBIxsError e ixs) = unwords [e, ":, indices", show ixs, "out of bounds"]
  show (OOBNoCompatRows e ij) = unwords [e, ": no compatible rows for indices", show ij]
instance (Show i, Typeable i) => Exception (OutOfBoundsIndexError i)

checkIxBound :: MonadThrow m => String -> Int -> UB -> m a -> m a
checkIxBound e i n ff
  | not (inBounds0 n i) = throwM (OOBIxError e i)
  | otherwise = ff

    

-- | Operand size mismatch errors
data OperandSizeMismatch = DotSizeMismatch Int Int
                         | NonTriangularException String
                         | MatVecSizeMismatchException String (Int, Int) Int deriving (Eq, Typeable)
instance Show OperandSizeMismatch where
  show (DotSizeMismatch na nb) = unwords ["<.> : Incompatible dimensions : ", show na, show nb]
  show (NonTriangularException s )= unwords [s, ": Matrix must be triangular"]
  show (MatVecSizeMismatchException s dm o) = unwords [s, ": Matrix-vector dimensions are incompatible: Matrix is",show dm,", whereas vector is",show o]
instance Exception OperandSizeMismatch




-- | Matrix exceptions
data MatrixException i = HugeConditionNumber String i
                       | NeedsPivoting String String deriving (Eq, Typeable)
instance Show i => Show (MatrixException i) where
  show (HugeConditionNumber s x) = unwords [s, ": Rank-deficient system: condition number", show x]
  show (NeedsPivoting s1 s2) = unwords [s1, ":", s2, "is close to 0. Permute the rows to obtain a nonzero diagonal"]
instance (Show i, Typeable i) => Exception (MatrixException i)



-- | Numerical iteration errors
data IterationException a = NotConvergedE String Int a 
                          | DivergingE String Int a a
                          deriving (Eq, Typeable)
instance Show a => Show (IterationException a) where
  show (NotConvergedE s niters x) = unwords [s, ": Could not converge within",show niters, "iterations; final state:", show x]
  show (DivergingE s niters x0 x1) = unwords [s, ": Diverging at iteration #", show niters, "; latest state:",show x0, "; previous state:",show x1]
  -- show (NotConvergedToExpectedE s niters x x0) = unwords [s, ": Could not converge within",show niters, "iterations; final state:", show x, "expected state:", show x0]
instance (Show a, Typeable a) => Exception (IterationException a)