module GraphRewrite.Internal.Test  where

import GraphRewrite.Internal.Rewrite
import GraphRewrite.Internal.RewriteTypes hiding (PointedGraph)

import System.IO.Unsafe
--import Test.ChasingBottoms.TimeOut
import Test.LazySmallCheck
import qualified Data.IntMap as IM
import Data.List
import Data.Function


{-
Serial instance needed:
    Graph
    RewriteSystem

-}


--------------------------- helper functions

infix 1 <=>
(<=>) :: Bool -> Bool -> Bool
(<=>) = (==)

sameSet :: Ord a => [a] -> [a] -> Bool
sameSet = (==) `on` sort
{-
timeOut2s :: a -> IO a
timeOut2s x = do
    r <- timeOut 2 (return x)
    case r of
        NonTermination      -> error "nontermination"
        Value x             -> return x
        Exception e         -> error (show e)
-}
--------------------------- Serial instances

instance Serial Expr where
    series
        =  cons1 SCons
        \/ cons2 SFun
        \/ cons1 SLit
        \/ cons1 SHole
        \/ cons1 (SRef . natToInt)
        \/ cons2 SApp

newtype DataExpr
    = DataExpr { unDataExpr :: Expr }
        deriving Show

instance Serial DataExpr where
    series
        =  cons1 (DataExpr . SCons)
        \/ cons2 (\x -> DataExpr . SFun x)
        \/ cons1 (DataExpr . SLit)
        \/ cons1 (DataExpr . SRef . natToInt)
        \/ cons2 (\(DataExpr x) ys -> DataExpr $ SApp x $ map unDataExpr ys)

newtype DataGraph
    = DataGraph Graph
        deriving Show

instance Serial DataGraph where
    series = cons1 (DataGraph . IM.fromList . zip [0..] . map unDataExpr)

data Nat
    = Zero
    | Succ Nat
        deriving (Eq, Ord, Show)

instance Num Nat where
    fromInteger 0 = Zero
    fromInteger n | n < 0 = error "fromInteger/Num"
    fromInteger n = Succ (fromInteger (n-1))

    (+) = undefined
    (*) = undefined
    abs = undefined
    signum = undefined

instance Serial Nat where
    series = cons0 Zero \/ cons1 Succ

natToInt :: Nat -> Int
natToInt Zero = 0
natToInt (Succ n) =  (1+) $! (natToInt n)

---------------------------------

data PointedGraph
    = PGraph Expr Graph
        deriving (Show)

instance Serial PointedGraph where
    series = cons1 mkDataGraph

mkDataGraph :: Nat -> PointedGraph
mkDataGraph n = PGraph (SLit "15") IM.empty

--------------------------- auxiliary predicates and functions

dataExpr :: Expr -> Bool
dataExpr (SHole _)   = False
dataExpr (SApp e es) = dataExpr e && all dataExpr es
dataExpr _           = True

dataGraph :: Graph -> Bool
dataGraph = all dataExpr . IM.elems

ref :: Expr -> Bool
ref (SRef _) = True
ref _        = False

collectRefs :: Expr -> [Int]
collectRefs (SRef i)    = [i]
collectRefs (SApp e es) = collectRefs e ++ concatMap collectRefs es
collectRefs _           = []

allReferenceDefined :: Expr -> Graph -> Bool
allReferenceDefined e g = all (`IM.member` g) (collectRefs e ++ concatMap collectRefs (IM.elems g))

---------------------------

allTests :: IO ()
allTests = do

    let
        p (SCons _)  = True
        p (SFun _ _) = True
        p (SLit _)   = True
        p _          = False

    smallCheck 30 $ \(PGraph e g) -> -- unsafePerformIO $ timeOut2s $
            not (ref e) && allReferenceDefined e g  ==>  p (fst (flattenSApp defaultRS e g))

{-
    sm $ interpreter "start = f True; f x = x" == "True"

    sm $ \s1 s2 s3 ->   s1 /= s2
            ==>  freeVars (s1 ++ " = 3;" ++ s2 ++ " " ++ s3 ++ " = " ++ s3)
                    `sameSet` [s1, s2]
-}


