{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances #-}

-- | A wrapper for the new GHC (and Hugs) Dynamic module.
-- The main improvement over the original Dynamic module is
-- that we provide flavours of TypeableXXXX for kinds with
-- arguments other than *, a feature used by "DisplayView".
module Util.Dynamics (
        Typeable(..), -- inherited from Dynamic
        TypeRep, -- same as Dynamic.TypeRep


        Dyn, -- equal to Dynamic.Dynamic
        toDyn, -- inherited from Dynamic.toDyn
        fromDynamic, -- inherited from Dynamic.fromDynamic
        fromDynamicWE, -- :: Dyn -> WithError a

        coerce, -- read Dyn or (match) error
        coerceIO, -- read Dyn or fail with typeMismatch
        typeMismatch,
        dynCast, -- Cast to another value of the same type, or
           -- error (useful for extracting from existential types).
        dynCastOpt,

        mkTypeRep,
           -- :: String -> String -> TypeRep

        -- Flavours of Typeable we need not already in Data.Typeable.
        -- The only customer for these at the moment seems to be
        -- types/DisplayView.hs
        Typeable1_1(..),
        Typeable2_11(..),
        Typeable3_111(..),
        Typeable4_0111(..),
        Typeable5_00111(..),
        Typeable6_000111(..),
        )
where

import qualified Data.Dynamic
import Data.Typeable

import Util.Computation
import Util.Debug(debug)

fromDynamic :: Typeable a => Dyn -> Maybe a
fromDynamic = Data.Dynamic.fromDynamic

-- | Like 'fromDynamic' but provides an error message indicating what
-- types are getting confused.
fromDynamicWE :: Typeable a => Dyn -> WithError a
fromDynamicWE dyn =
   case fromDynamic dyn of
      Just a -> return a
      (aOpt @ Nothing) ->
         fail ("Dynamic type error.  Looking for "
            ++ show (typeOf (typeHack aOpt))
            ++ " but found a " ++ show dyn)
   where
      typeHack :: Maybe a -> a
      typeHack _ = undefined
type Dyn = Data.Dynamic.Dynamic

toDyn :: Typeable a => a -> Dyn
toDyn = Data.Dynamic.toDyn

coerce  :: Typeable a => Dyn -> a
coerce d =
   case fromDynamic d of
      Just x -> x

coerceIO :: Typeable a => Dyn -> IO a
coerceIO d =
   case fromDynamic d of
      Nothing ->
         do
            debug "Dynamics.coerceIO failure"
            ioError typeMismatch
      (Just x) -> return x

typeMismatch :: IOError
typeMismatch =
        userError "internal type of dynamics does not match expected type"

dynCast :: (Typeable a,Typeable b) => String -> a -> b
dynCast mess value = case dynCastOpt value of
   Nothing -> error ("Dynamics.dynCast failure in "++mess)
   Just value2 -> value2

dynCastOpt :: (Typeable a,Typeable b) => a -> Maybe b
dynCastOpt = Data.Dynamic.cast

-- | Construct a TypeRep for a type or type constructor with no arguments.
-- The first string should be the module name, the second that of the type.
mkTypeRep :: String -> String -> TypeRep
mkTypeRep s1 s2 = mkTyConApp (mkTyCon (s1 ++ "." ++ s2)) []

-- ------------------------------------------------------------
-- Flavours of Typeable we need not already in Data.Typeable.
-- The only customer for these at the moment seems to be
-- types/DisplayView.hs
-- ------------------------------------------------------------

class Typeable1_1 ty where
   typeOf1_1 :: Typeable1 typeArg => ty typeArg -> TypeRep

instance (Typeable1_1 ty,Typeable1 typeArg) => Typeable (ty typeArg) where
   typeOf (x :: ty typeArg) = (typeOf1_1 x) `mkAppTy` typeOf v
      where
         v :: typeArg ()
         v = error "Dynamics.31"

class Typeable2_11 ty where
   typeOf2_11 :: (Typeable1 typeArg1,Typeable1 typeArg2)
      => ty typeArg1 typeArg2 -> TypeRep

instance (Typeable2_11 ty,Typeable1 typeArg1)
      => Typeable1_1 (ty typeArg1) where
   typeOf1_1 (x :: ty typeArg1 typeArg2) =
         (typeOf2_11 x) `mkAppTy` (typeOf1 v)
      where
         v :: typeArg1 ()
         v = error "Dynamics.23"

class Typeable3_111 ty where
   typeOf3_111 :: (Typeable1 typeArg1,Typeable1 typeArg2,Typeable1 typeArg3)
      => ty typeArg1 typeArg2 typeArg3 -> TypeRep

instance (Typeable3_111 ty,Typeable1 typeArg1)
      => Typeable2_11 (ty typeArg1) where
   typeOf2_11 (x :: ty typeArg1 typeArg2 typeArg3) =
         (typeOf3_111 x) `mkAppTy` (typeOf1 v)
      where
         v :: typeArg1 ()
         v = error "Dynamics.23"

class Typeable4_0111 ty where
   typeOf4_0111
      :: (Typeable ty1,
         Typeable1 typeArg1,Typeable1 typeArg2,Typeable1 typeArg3)
      => ty ty1 typeArg1 typeArg2 typeArg3  -> TypeRep

instance (Typeable4_0111 ty,Typeable ty1)
      => Typeable3_111 (ty ty1) where
   typeOf3_111 (x :: ty ty1 typeArg2 typeArg3 typeArg4) =
         (typeOf4_0111 x) `mkAppTy` (typeOf v)
      where
         v :: ty1
         v = error "Dynamics.23"

class Typeable5_00111 ty where
   typeOf5_00111
      :: (Typeable ty1,Typeable ty2,
         Typeable1 typeArg1,Typeable1 typeArg2,Typeable1 typeArg3)
      => ty ty1 ty2 typeArg1 typeArg2 typeArg3  -> TypeRep

instance (Typeable5_00111 ty,Typeable ty1)
      => Typeable4_0111 (ty ty1) where
   typeOf4_0111 (x :: ty ty1 ty2 typeArg1 typeArg2 typeArg3) =
         (typeOf5_00111 x) `mkAppTy` (typeOf v)
      where
         v :: ty1
         v = error "Dynamics.23"

class Typeable6_000111 ty where
   typeOf6_000111
      :: (Typeable ty1,Typeable ty2,Typeable ty3,
         Typeable1 typeArg1,Typeable1 typeArg2,Typeable1 typeArg3)
      => ty ty1 ty2 ty3 typeArg1 typeArg2 typeArg3  -> TypeRep

instance (Typeable6_000111 ty,Typeable ty1)
      => Typeable5_00111 (ty ty1) where
   typeOf5_00111 (x :: ty ty1 ty2 ty3 typeArg1 typeArg2 typeArg3) =
         (typeOf6_000111 x) `mkAppTy` (typeOf v)
      where
         v :: ty1
         v = error "Dynamics.23"