{-# Language DeriveDataTypeable, StandaloneDeriving #-} -- for GHC <= 7.8
-- |
-- Module      : Test.Speculate.Expr.Instance
-- Copyright   : (c) 2016-2019 Rudy Matela
-- License     : 3-Clause BSD  (see the file LICENSE)
-- Maintainer  : Rudy Matela <rudy@matela.com.br>
--
-- This module is part of Speculate.
--
-- Typeclass instance information.
module Test.Speculate.Expr.Instance
  ( Instances

  -- * reifying instances
  , reifyInstances
  , reifyInstances1
  , reifyListable, mkListable

  -- * checking instances
  , isListable, isListableT

  -- * finding functions
  , lookupTiers
  , lookupTiersT
  , holeOfTy, maybeHoleOfTy

  -- * the preludeInstances definition
  , preludeInstances

  -- * module re-export
  , module Data.Express.Instances
  )
where

import Data.Express.Instances
import Test.Speculate.Expr.Core
import Test.Speculate.Utils
import Test.LeanCheck
import Test.LeanCheck.Utils
import Data.Maybe

type Instances = [Expr] -- TODO: remove?

reifyInstances1 :: (Typeable a, Listable a, Show a, Eq a, Ord a, Name a) => a -> Instances
reifyInstances1 a  =  concat [reifyListable a, reifyEqOrd a, reifyName a]

reifyInstances :: (Typeable a, Listable a, Show a, Eq a, Ord a, Name a) => a -> Instances
reifyInstances a  =  concat
  [ r1 a
  , r1 [a]
--, r1 [[a]]
  , r1 (a,a)
--, r1 (a,a,a)
--, r1 [(a,a)]
  , r1 (mayb a)
--, r1 (eith a a)
  ]
  where
  r1 :: (Typeable a, Listable a, Show a, Eq a, Ord a, Name a)
     => a -> Instances
  r1 = reifyInstances1

reifyListable :: (Typeable a, Show a, Listable a) => a -> Instances
reifyListable a  =  mkListable (tiers -: [[a]])

mkListable :: (Typeable a, Show a) => [[a]] -> [Expr]
mkListable xss
  | null (concat xss)  =  err
  | otherwise          =  [value "tiers" $ mapT val xss]
  where
  err  =  error
       $  "Speculate does not allow an empty tiers enumeration"
       ++ ", offending type: " ++ show (typeOf . head $ head xss)
-- TODO: reify an "undefined" value of a type to be able to holeOfTy and lift
--       the above restriction of no empty tiers?

isListable :: Instances -> Expr -> Bool
isListable is  =  isListableT is . typ

isListableT :: Instances -> TypeRep -> Bool
isListableT is  =  not . null . lookupTiersT is

lookupTiers :: Instances -> Expr -> [[Expr]]
lookupTiers is  =  lookupTiersT is . typ

lookupTiersT :: Instances -> TypeRep -> [[Expr]]
lookupTiersT is t  =  fromMaybe [] $ maybeTiersE is t
  where
  maybeTiersE :: Instances -> TypeRep -> Maybe [[Expr]]
  maybeTiersE is t  =  case i of
    [] -> Nothing
    (tiers:_) -> Just tiers
    where
    i = [tiers | e@(Value "tiers" _) <- is
               , let tiers = eval (undefined :: [[Expr]]) e
               , typ (head . concat $ tiers) == t]
  -- TODO: make the above work on empty tiers

holeOfTy :: Instances -> TypeRep -> Expr
holeOfTy is t = fromMaybe err $ maybeHoleOfTy is t
  where
  err  =  error $ "holeOfTy: could not find tiers with type `[[" ++ show t ++ "]]'."

maybeHoleOfTy :: Instances -> TypeRep -> Maybe Expr
maybeHoleOfTy is t = case concat $ lookupTiersT is t of
                     (e:_) -> Just $ "" `varAsTypeOf` e
                     _     -> Nothing

-- despite the name, this _does not_ include most types from the prelude.
preludeInstances :: Instances
preludeInstances  =  concat
  [ r1 (u :: ())
  , r1 (u :: [()])

  , r (u :: Bool)

  , r (u :: Int)
--, r (u :: Word)
  , r (u :: Integer)

  , r (u :: Ordering)
  , r (u :: Char)

  , r (u :: Rational)
  , r (u :: Float)
  , r (u :: Double)

-- TODO: uncomment the following and investigate why compilation takes so long
--, r (u :: Int1)
--, r (u :: Int2)
--, r (u :: Int3)
--, r (u :: Int4)
--, r (u :: Word1)
--, r (u :: Word2)
--, r (u :: Word3)
--, r (u :: Word4)
--, r (u :: Nat1)
--, r (u :: Nat2)
--, r (u :: Nat3)
--, r (u :: Nat4)
--, r (u :: Nat5)
--, r (u :: Nat6)
--, r (u :: Nat7)
  ]
  where
  u :: a
  u  =  undefined
  r, r1 :: (Typeable a, Listable a, Show a, Eq a, Ord a, Name a)
        => a -> Instances
  r = reifyInstances
  r1 = reifyInstances1
-- WHOA!  Have I discovered a "bug" in GHC?  adding to many type compositions
-- on ins and types on preludeInstances makes compilation of this module
-- *really* slow: it takes a whopping 2 minutes!
-- (the above report is using -O2, I have not tested without optimizations).