module Data.Expression.Sort ( Sort(..)
                            , Sing(..)
                            , DynamicSort(..)
                            , DynamicallySorted(..)
                            , parseSort
                            , toStaticSort
                            , toStaticallySorted ) where
import Data.Attoparsec.Text
import Data.Kind
import Data.Singletons
import Data.Singletons.Decide
import Data.Singletons.TH
import Data.Expression.Utils.Indexed.Eq
import Data.Expression.Utils.Indexed.Functor
import Data.Expression.Utils.Indexed.Show
data Sort = BooleanSort                                  
          | IntegralSort                                 
          | ArraySort { index :: Sort, element :: Sort } 
    deriving Eq
genSingletons [''Sort]
singDecideInstance ''Sort
show' :: Sort -> String
show' BooleanSort     = "bool"
show' IntegralSort    = "int"
show' (ArraySort i e) = "(array " ++ show' i ++ " " ++ show' e ++ ")"
instance Show Sort where
    show s@BooleanSort   = show' s
    show s@IntegralSort  = show' s
    show (ArraySort i e) = "array " ++ show' i ++ " " ++ show' e
ssortToSort :: forall s. SSort s -> Sort
ssortToSort SBooleanSort     = BooleanSort
ssortToSort SIntegralSort    = IntegralSort
ssortToSort (SArraySort i e) = ArraySort (ssortToSort i) (ssortToSort e)
instance Show (SSort s) where
    show = show . ssortToSort
data DynamicSort where
    DynamicSort :: forall (s :: Sort). Sing s -> DynamicSort
instance Eq DynamicSort where
    DynamicSort a == DynamicSort b = case a %~ b of
        Proved Refl -> True
        Disproved _ -> False
data DynamicallySorted (f :: (Sort -> *) -> (Sort -> *)) where
  DynamicallySorted :: forall (s :: Sort) f. Sing s -> IFix f s -> DynamicallySorted f
instance IEq1 f => Eq (DynamicallySorted f) where
    DynamicallySorted sa a == DynamicallySorted sb b = case sa %~ sb of
        Proved Refl -> a == b
        Disproved _ -> False
instance (IFunctor f, IShow f) => Show (DynamicallySorted f) where
    show (DynamicallySorted _ a) = show a
toStaticSort :: forall (s :: Sort). SingI s => DynamicSort -> Maybe (Sing s)
toStaticSort dx = case dx of
    DynamicSort x -> case x %~ (sing :: Sing s) of
        Proved Refl -> Just x
        Disproved _ -> Nothing
toStaticallySorted :: forall f (s :: Sort). SingI s => DynamicallySorted f -> Maybe (IFix f s)
toStaticallySorted dx = case dx of
    DynamicallySorted s x -> case s %~ (sing :: Sing s) of
        Proved Refl -> Just x
        Disproved _ -> Nothing
parseSort :: Parser DynamicSort
parseSort = choice [ bool, int, array ] <?> "Sort" where
        bool  = string "bool" *> pure (DynamicSort SBooleanSort)
        int   = string "int"  *> pure (DynamicSort SIntegralSort)
        array = array' <$> (string "array" *> space *> sort') <*> (space *> sort')
        sort' = choice [ bool, int, char '(' *> array <* char ')' ]
        array' :: DynamicSort -> DynamicSort -> DynamicSort
        array' (DynamicSort i) (DynamicSort e) = DynamicSort (SArraySort i e)