-- {-# LANGUAGE Arrows #-}
-- {-# LANGUAGE ImplicitParams #-}
module IntervalSpec where

import Test.Hspec

main :: IO ()
main = hspec spec

spec :: Spec
spec =
  describe "Stub" $
    it "Simple addition" $
      (1 + 1) `shouldBe` 2
--
-- import           Prelude hiding (Bool(..))
-- import qualified Prelude as P
--
-- import Syntax
-- import Shared
-- import Interval
--
-- import qualified Data.Map as Map
-- import           Data.List
-- import           Data.Boolean
-- import           Data.Abstract.Bounded
-- import           Data.Abstract.HandleError
-- import qualified Data.Abstract.Interval as I
--
-- import Control.Arrow
--
-- import Test.Hspec
--
-- import Classes.Object
-- import Classes.Throwable
-- import Classes.IllegalArgumentException
-- import Classes.ArrayIndexOutOfBoundsException
-- import Classes.ArithmeticException
--
-- import Classes.ArrayFieldExample
-- import Classes.FactorialExample
-- import Classes.PersonExample
-- import Classes.SingleMethodExample
-- import Classes.TryCatchExample
--
-- main :: IO ()
-- main = hspec spec
--
-- spec :: Spec
-- spec = do
--   describe "Literals" $ do
--     it "LocalName lookup" $ do
--       let ?bound = I.Interval (-100) 100
--       let expr = Local "x"
--       let nv = [("x", 1)]
--       let st = [(1, int 2 2)]
--       eval_ nv st expr `shouldSatisfy` ifSuccess (int 2 2)
--     it "Integer literals" $ do
--       let ?bound = I.Interval (-100) 100
--       let expr = IntConstant 7
--       eval_ env store expr `shouldBe` Success (int 7 7)
--     it "Float literals" $ do
--       let ?bound = I.Interval (-100) 100
--       let expr = FloatConstant 2.5
--       eval_ env store expr `shouldBe` Success (FloatVal 2.5)
--     it "String literals" $ do
--       let ?bound = I.Interval (-100) 100
--       let expr = StringConstant "Hello World"
--       eval_ env store expr `shouldBe` Success (StringVal "Hello World")
--     it "Class literals" $ do
--       let ?bound = I.Interval (-100) 100
--       let expr = ClassConstant "java.lang.Object"
--       eval_ env store expr `shouldBe` Success (ClassVal "java.lang.Object")
--     it "Null literals" $ do
--       let ?bound = I.Interval (-100) 100
--       let expr = NullConstant
--       eval_ env store expr `shouldBe` Success NullVal
--
--   describe "Boolean expressions" $ do
--     it "3 < 4" $ do
--       let ?bound = I.Interval (-100) 100
--       let expr = BinopExpr (IntConstant 3) Cmplt (IntConstant 4)
--       evalBool_ env store expr `shouldBe` Success true
--     it "3 != 'three'" $ do
--       let ?bound = I.Interval (-100) 100
--       let expr = BinopExpr (IntConstant 3) Cmpne (StringConstant "three")
--       evalBool_ env store expr `shouldBe` Success true
--
--   -- describe "Simple expressions" $ do
--   --   it "-3" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let expr = UnopExpr Neg (IntConstant 3)
--   --     eval_ env store expr `shouldBe` Success (int (-3) (-3))
--   --   it "lengthof [1, 2, 3]" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let expr = UnopExpr Lengthof (Local "x")
--   --     let nv = [("x", 1)]
--   --     let st = [(1, RefVal 2),
--   --               (2, ArrayVal (int 1 3) (int 3 3))]
--   --     eval_ nv st expr `shouldSatisfy` ifSuccess (int 3 3)
--   --   it "8 + 2" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let expr = BinopExpr (IntConstant 8) Plus (IntConstant 2)
--   --     eval_ env store expr `shouldBe` Success (int 10 10)
--   --   it "8 / 0" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let expr = BinopExpr (IntConstant 8) Div (IntConstant 0)
--   --     eval_ env store expr `shouldBe` Fail (DynamicException (RefVal 0))
--   --   it "3.0 % 2.5" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let expr = BinopExpr (FloatConstant 3.0) Rem (FloatConstant 2.5)
--   --     eval_ env store expr `shouldBe` Success (FloatVal 0.5)
--   --   it "new boolean" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let expr = NewExpr BooleanType
--   --     eval_ env store expr `shouldBe` Success (BoolVal false)
--   --   it "[1, 2, 3][2]" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let expr = ArrayRef "xs" (IntConstant 2)
--   --     let nv = [("xs", 1)]
--   --     let st = [(1, RefVal 2),
--   --               (2, ArrayVal (int 1 3) (int 3 3))]
--   --     eval_ nv st expr `shouldSatisfy` ifSuccess (int 1 3)
--   --   it "newmultiarray (float) [3][2]" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let expr = NewMultiArrayExpr FloatType [IntConstant 3, IntConstant 2]
--   --     eval_ env store expr `shouldSatisfy` ifSuccess (ArrayVal (ArrayVal (FloatVal 0.0) (int 2 2)) (int 3 3))
--   --
--   -- describe "Simple Statements" $ do
--   --   it "i0 = 2 + 3; return i0;" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let nv = [("i0", 1)]
--   --     let st = [(1, int 0 0)]
--   --     let stmts = [Assign (LocalVar "i0") (BinopExpr (IntConstant 2) Plus (IntConstant 3)),
--   --                  Return (Just (Local "i0"))]
--   --     runStatements_ nv st stmts `shouldSatisfy` ifSuccess (Just (int 5 5))
--   --   it "assign non-declared variable" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let stmts = [Assign (LocalVar "s") (IntConstant 2)]
--   --     runStatements_ env store stmts `shouldBe` staticException "Variable \"s\" not bound"
--   --   it "s = 2; xs = newarray (int)[s]; y = lengthof xs; return xs;" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let nv = [("s", 0),
--   --               ("xs", 1),
--   --               ("y", 2)]
--   --     let st = [(0, int 0 0),
--   --               (1, NullVal),
--   --               (2, int 0 0)]
--   --     let stmts = [Assign (LocalVar "s") (IntConstant 2),
--   --                  Assign (LocalVar "xs") (NewArrayExpr IntType (Local "s")),
--   --                  Assign (LocalVar "y") (UnopExpr Lengthof (Local "xs")),
--   --                  Return (Just (Local "y"))]
--   --     runStatements_ nv st stmts `shouldSatisfy` ifSuccess (Just (int 2 2))
--   --   it "if 2 <= 3 goto l2; l1: return 1; l2: return 0;" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let stmts = [If (BinopExpr (IntConstant 2) Cmple (IntConstant 3)) "l2",
--   --                  Label "l1",
--   --                  Return (Just (IntConstant 1)),
--   --                  Label "l2",
--   --                  Return (Just (IntConstant 0))]
--   --     runStatements_ env store stmts `shouldBe` Success (Just (int 0 0))
--   --   it "lookupswitch(4) { case 0: goto l1; case 4: goto l2; default: goto l3;}; l1: return 1; l2: return 2; l3: return 3;" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let stmts = [Lookupswitch (IntConstant 4) [(ConstantCase 0, "l1"),
--   --                                                (ConstantCase 4, "l2"),
--   --                                                (DefaultCase, "l3")],
--   --                  Label "l1",
--   --                  Return (Just (IntConstant 1)),
--   --                  Label "l2",
--   --                  Return (Just (IntConstant 2)),
--   --                  Label "l3",
--   --                  Return (Just (IntConstant 3))]
--   --     runStatements_ env store stmts `shouldSatisfy` ifSuccess (Just (int 2 2))
--   --   it "lookupswitch(2) { case 0: goto l1; case 4: goto l2; default: goto l3;}; l1: return 1; l2: return 2; l3: return 3;" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let stmts = [Lookupswitch (IntConstant 2) [(ConstantCase 0, "l1"),
--   --                                         (ConstantCase 4, "l2"),
--   --                                         (DefaultCase, "l3")],
--   --                  Label "l1",
--   --                  Return (Just (IntConstant 1)),
--   --                  Label "l2",
--   --                  Return (Just (IntConstant 2)),
--   --                  Label "l3",
--   --                  Return (Just (IntConstant 3))]
--   --     runStatements_ env store stmts `shouldSatisfy` ifSuccess (Just (int 3 3))
--   --   it "f0 := @parameter0: float; f1 = f0 * 2.5; return f1; (@parameter0 = 2.0)" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let nv = [("@parameter0", 0),
--   --               ("f0",          1),
--   --               ("f1",          2)]
--   --     let st = [(0, FloatVal 2.0),
--   --               (1, FloatVal 0.0),
--   --               (2, FloatVal 0.0)]
--   --     let stmts = [Identity "f0" (ParameterRef 0) FloatType,
--   --                  Assign (LocalVar "f1") (BinopExpr (Local "f0") Mult (FloatConstant 2.5)),
--   --                  Return (Just (Local "f1"))]
--   --     runStatements_ nv st stmts `shouldSatisfy` ifSuccess (Just (FloatVal 5.0))
--   --
--   -- describe "Complete program" $ do
--   --   it "10! = 3628800" $ do
--   --     let ?bound = I.Interval (-100) 4000000
--   --     let files = baseCompilationUnits ++ [factorialExampleFile]
--   --     runProgram_ files factorialExampleFile [IntConstant 10] `shouldSatisfy` ifSuccess (Just (int 3628800 3628800))
--   --   it "s = new SingleMethodExample; s.x = 2; return s.x" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let files = baseCompilationUnits ++ [singleMethodExampleFile]
--   --     runProgram_ files singleMethodExampleFile [] `shouldSatisfy` ifSuccess (Just (int 2 2))
--   --   it "(-10)! throws IllegalArgumentException" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let files = baseCompilationUnits ++ [factorialExampleFile]
--   --     runProgram_ files factorialExampleFile [IntConstant (-10)] `shouldBe` dynamicException "java.lang.IllegalArgumentException" "Negative value for argument n"
--   --   it "5 -> [5,10,5,10]" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let files = baseCompilationUnits ++ [arrayFieldExampleFile]
--   --     runProgram_ files arrayFieldExampleFile [IntConstant 5] `shouldSatisfy` ifSuccess (Just (ArrayVal (int 0 10) (int 4 4)))
--   --   it "(new Person(10)).yearsToLive() = 90" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let files = baseCompilationUnits ++ [personExampleFile]
--   --     runProgram_ files personExampleFile [] `shouldSatisfy` ifSuccess (Just (int 90 90))
--   --   it "try { throw e } catch (e) { throw e' }" $ do
--   --     let ?bound = I.Interval (-100) 100
--   --     let files = baseCompilationUnits ++ [tryCatchExampleFile]
--   --     runProgram_ files tryCatchExampleFile [] `shouldBe` dynamicException "java.lang.ArrayIndexOutOfBoundsException" "b"
--
--   where
--     baseCompilationUnits = [objectFile,
--                             throwableFile,
--                             illegalArgumentExceptionFile,
--                             arrayIndexOutOfBoundsExceptionFile,
--                             arithmeticExceptionFile]
--     env = []
--     store = []
--
--     int i j = IntVal $ bounded $ I.Interval i j
--
--     staticException msg = Fail (StaticException msg)
--     dynamicException clzz msg = Fail (DynamicException (ObjectVal clzz (Map.fromList [(throwableMessageSignature, StringVal msg)])))
--
--     ifSuccess x e = case e of
--       Success y -> y == x
--       SuccessOrFail _ y -> y == x
--       _ -> P.False
--
--     testMethodBody stmts = FullBody { declarations = []
--                                  , statements = stmts
--                                  , catchClauses = []
--                                  }
--
--     testMethod stmts = Method { methodModifiers = [Public, Static]
--                               , returnType = VoidType
--                               , methodName = "test"
--                               , parameters = []
--                               , throws = []
--                               , methodBody = testMethodBody stmts
--                               }
--
--     testCompilationUnits stmts = [CompilationUnit { fileModifiers = [Public]
--                                                   , fileType = ClassFile
--                                                   , fileName = "Test"
--                                                   , extends = Just "java.lang.Object"
--                                                   , implements = []
--                                                   , fileBody = [MethodMember (testMethod stmts)]
--                                                   }] ++ baseCompilationUnits
--     getMethod :: Member -> [Method]
--     getMethod (MethodMember m) = [m]
--     getMethod _ = []
--     getMethods members = concatMap getMethod members
--     mainMethod unit = case find (\m -> methodName m == "main") (getMethods (fileBody unit)) of
--       Just m -> m
--       Nothing -> error "No entry method found"
--
--     deepDerefMaybe = proc val -> case val of
--       Just x -> deepDeref >>^ Just -< x
--       Nothing -> returnA -< Nothing
--
--     evalBool_ env' store' = runInterp evalBool (testCompilationUnits []) env' store' (testMethod [])
--     eval_ env' store' = runInterp (eval >>> deepDeref) (testCompilationUnits []) env' store' (testMethod [])
--
--     runStatements_ env' store' stmts =
--       runInterp (runStatements >>> deepDerefMaybe) (testCompilationUnits stmts) env' store' (testMethod stmts) stmts
--
--     runProgram_ compilationUnits mainUnit args =
--       runInterp (runProgram >>> deepDerefMaybe) compilationUnits [] [] (mainMethod mainUnit) args
