-- I AM NOT DONE

import Test.Tasty
import Test.Tasty.HUnit



 - Typeclasses can have dependencies on one another . When defining a typeclass , 
   you can give a dependency in much the same way a function can be constrained . 
   The simplest example of this is that '  ' depends on '  ' . We can only order 
   elements if they might be equal to one another : 

 class ( Eq a ) = > Ord a where 
   ... 

 - We just give the dependency , then the sign ' = > ' , and then our class definition 
   as usual . This allows you to use functions from the " parent " class in functions 
   constrained by your new , dependent class . 

 printEquals : : (  a ) = > a - > a - > String 
 printEquals x y = if x = = y -- < Can use (= =) because of implicit  constraint 
   then ... 



- Typeclasses can have dependencies on one another. When defining a typeclass,
  you can give a dependency in much the same way a function can be constrained.
  The simplest example of this is that 'Ord' depends on 'Eq'. We can only order
  elements if they might be equal to one another:

class (Eq a) => Ord a where
  ...

- We just give the dependency, then the sign '=>', and then our class definition
  as usual. This allows you to use functions from the "parent" class in functions
  constrained by your new, dependent class.

printEquals :: (Ord a) => a -> a -> String
printEquals x y = if x == y -- < Can use (==) because of implicit Eq constraint
  then ...

-}

-- TODO:

-- Define a class called Debuggable
-- This class should depend on 'Eq', 'Show', and 'Read'
  It should have two functions :

-- logObject should take the class variable and a string for the file
-- it was generated from and produce a message

-- compareFromEntry should take a string and the class variable and assess
-- whether or not they are the same, returning a String message and a boolean.

-- Create instances of this class for the 'Person' and 'Point3' types.
-- Use the sample output to guide you
data Person = Person String String Int
  deriving (Show, Read, Eq)

  logObject ( Person "  " "  " 32 ) " Test.hs " - > " Produced ' Person \"John\ " \"Smith\ " 32 ' from file Test.hs "
  compareFromEntry " Person \"John\ " \"Smith\ " 32 " ( Person "  " "  " 32 )
    - > ( True , " Found entered Person object equivalent . " )
  compareFromEntry " Adult \"Jane\ " \"Smith\ " 32 " ( Adult "  " "  " 32 )
--   -> (False, "Found entered Person object does not match.")

data Point3 = Point3 Int Int Int
  deriving (Show, Read, Eq)

-- logObject (Point3 3 4 5) "Test.hs" -> "Calculated 'Point 3 4 5' from input file Test.hs"
-- compareFromEntry (Point3 3 4 5) (Point3 3 4 5)
    - > ( True , " New Point calculation matches . " )
-- compareFromEntry (Point3 3 4 5) (Point3 3 4 5)
    - > ( False , " New Point calculation does not match previous . " )

-- Fill in this function as a generalization of compareFromEntry
-- Should return a Bool for whether or not the 'read' input matches the given
-- value. The output String should simply 'show' both values on either side of "vs."
--
-- compareAndPrint "Point3 3 4 5" (Point3 6 8 10)
--   -> (False, "'Point3 3 4 5' vs. 'Point3 6 8 10'")
compareAndPrint :: (Debuggable a) => String -> a -> (Bool, String)
compareAndPrint = undefined

-- Test Code

main :: IO ()
main = defaultMain $ testGroup "Typeclasses5" $
  [ testCase "Log Object 1" $ logObject (Person "John" "Smith" 32) "Test.hs" @?= "Produced 'Person \"John\" \"Smith\" 32' from file Test.hs"
  , testCase "Log Object 2" $ logObject (Person "Jane" "Doe" 31) "Input.txt" @?= "Produced 'Person \"Jane\" \"Doe\" 31' from file Input.txt"
  , testCase "Log Object 3" $ logObject (Point3 3 4 5) "Test.hs" @?= "Calculated 'Point3 3 4 5' from input file Test.hs"
  , testCase "Log Object 4" $ logObject (Point3 6 8 10) "Input.csv" @?= "Calculated 'Point3 6 8 10' from input file Input.csv"
  , testCase "Compare From Entry 1" $ compareFromEntry "Person \"John\" \"Smith\" 32" (Person "John" "Smith" 32) @?=
      (True, "Found entered Person object equivalent.")
  , testCase "Compare From Entry 2" $ compareFromEntry "Person \"John\" \"Smith\" 32" (Person "Jane" "Smith" 35) @?=
      (False, "Found entered Person object does not match.")
  , testCase "Compare From Entry 3" $ compareFromEntry "Point3 3 4 5" (Point3 3 4 5) @?=
      (True, "New Point calculation matches.")
  , testCase "Compare From Entry 4" $ compareFromEntry "Point3 3 4 5" (Point3 6 8 10) @?=
      (False, "New Point calculation does not match previous.")
  , testCase "Compare and Print 1" $ compareAndPrint "Person \"John\" \"Smith\" 32" (Person "John" "Smith" 32) @?=
      (True, "'Person \"John\" \"Smith\" 32' vs. 'Person \"John\" \"Smith\" 32'")
  , testCase "Compare and Print 2" $ compareAndPrint "Person \"John\" \"Smith\" 32" (Person "Jane" "Smith" 31) @?=
      (False, "'Person \"John\" \"Smith\" 32' vs. 'Person \"Jane\" \"Smith\" 31'")
  , testCase "Compare and Print 3" $ compareAndPrint "Point3 3 4 5" (Point3 3 4 5) @?=
      (True, "'Point3 3 4 5' vs. 'Point3 3 4 5'")
  , testCase "Compare and Print 4" $ compareAndPrint "Point3 3 4 5" (Point3 6 8 10) @?=
      (False, "'Point3 3 4 5' vs. 'Point3 6 8 10'")
  ]

 I AM NOT DONE
 < Can use (= =) because of implicit  constraint 
 < Can use (==) because of implicit Eq constraint
 TODO:
 Define a class called Debuggable
 This class should depend on 'Eq', 'Show', and 'Read'
 logObject should take the class variable and a string for the file
 it was generated from and produce a message
 compareFromEntry should take a string and the class variable and assess
 whether or not they are the same, returning a String message and a boolean.
 Create instances of this class for the 'Person' and 'Point3' types.
 Use the sample output to guide you
   -> (False, "Found entered Person object does not match.")
 logObject (Point3 3 4 5) "Test.hs" -> "Calculated 'Point 3 4 5' from input file Test.hs"
 compareFromEntry (Point3 3 4 5) (Point3 3 4 5)
 compareFromEntry (Point3 3 4 5) (Point3 3 4 5)
 Fill in this function as a generalization of compareFromEntry
 Should return a Bool for whether or not the 'read' input matches the given
 value. The output String should simply 'show' both values on either side of "vs."

 compareAndPrint "Point3 3 4 5" (Point3 6 8 10)
   -> (False, "'Point3 3 4 5' vs. 'Point3 6 8 10'")
 Test Code

import Test.Tasty
import Test.Tasty.HUnit



 - Typeclasses can have dependencies on one another . When defining a typeclass , 
   you can give a dependency in much the same way a function can be constrained . 
   The simplest example of this is that '  ' depends on '  ' . We can only order 
   elements if they might be equal to one another : 

 class ( Eq a ) = > Ord a where 
   ... 

 - We just give the dependency , then the sign ' = > ' , and then our class definition 
   as usual . This allows you to use functions from the " parent " class in functions 
   constrained by your new , dependent class . 

 printEquals : : (  a ) = > a - > a - > String 
   then ... 



- Typeclasses can have dependencies on one another. When defining a typeclass,
  you can give a dependency in much the same way a function can be constrained.
  The simplest example of this is that 'Ord' depends on 'Eq'. We can only order
  elements if they might be equal to one another:

class (Eq a) => Ord a where
  ...

- We just give the dependency, then the sign '=>', and then our class definition
  as usual. This allows you to use functions from the "parent" class in functions
  constrained by your new, dependent class.

printEquals :: (Ord a) => a -> a -> String
  then ...

-}


  It should have two functions :



data Person = Person String String Int
  deriving (Show, Read, Eq)

  logObject ( Person "  " "  " 32 ) " Test.hs " - > " Produced ' Person \"John\ " \"Smith\ " 32 ' from file Test.hs "
  compareFromEntry " Person \"John\ " \"Smith\ " 32 " ( Person "  " "  " 32 )
    - > ( True , " Found entered Person object equivalent . " )
  compareFromEntry " Adult \"Jane\ " \"Smith\ " 32 " ( Adult "  " "  " 32 )

data Point3 = Point3 Int Int Int
  deriving (Show, Read, Eq)

    - > ( True , " New Point calculation matches . " )
    - > ( False , " New Point calculation does not match previous . " )

compareAndPrint :: (Debuggable a) => String -> a -> (Bool, String)
compareAndPrint = undefined


main :: IO ()
main = defaultMain $ testGroup "Typeclasses5" $
  [ testCase "Log Object 1" $ logObject (Person "John" "Smith" 32) "Test.hs" @?= "Produced 'Person \"John\" \"Smith\" 32' from file Test.hs"
  , testCase "Log Object 2" $ logObject (Person "Jane" "Doe" 31) "Input.txt" @?= "Produced 'Person \"Jane\" \"Doe\" 31' from file Input.txt"
  , testCase "Log Object 3" $ logObject (Point3 3 4 5) "Test.hs" @?= "Calculated 'Point3 3 4 5' from input file Test.hs"
  , testCase "Log Object 4" $ logObject (Point3 6 8 10) "Input.csv" @?= "Calculated 'Point3 6 8 10' from input file Input.csv"
  , testCase "Compare From Entry 1" $ compareFromEntry "Person \"John\" \"Smith\" 32" (Person "John" "Smith" 32) @?=
      (True, "Found entered Person object equivalent.")
  , testCase "Compare From Entry 2" $ compareFromEntry "Person \"John\" \"Smith\" 32" (Person "Jane" "Smith" 35) @?=
      (False, "Found entered Person object does not match.")
  , testCase "Compare From Entry 3" $ compareFromEntry "Point3 3 4 5" (Point3 3 4 5) @?=
      (True, "New Point calculation matches.")
  , testCase "Compare From Entry 4" $ compareFromEntry "Point3 3 4 5" (Point3 6 8 10) @?=
      (False, "New Point calculation does not match previous.")
  , testCase "Compare and Print 1" $ compareAndPrint "Person \"John\" \"Smith\" 32" (Person "John" "Smith" 32) @?=
      (True, "'Person \"John\" \"Smith\" 32' vs. 'Person \"John\" \"Smith\" 32'")
  , testCase "Compare and Print 2" $ compareAndPrint "Person \"John\" \"Smith\" 32" (Person "Jane" "Smith" 31) @?=
      (False, "'Person \"John\" \"Smith\" 32' vs. 'Person \"Jane\" \"Smith\" 31'")
  , testCase "Compare and Print 3" $ compareAndPrint "Point3 3 4 5" (Point3 3 4 5) @?=
      (True, "'Point3 3 4 5' vs. 'Point3 3 4 5'")
  , testCase "Compare and Print 4" $ compareAndPrint "Point3 3 4 5" (Point3 6 8 10) @?=
      (False, "'Point3 3 4 5' vs. 'Point3 6 8 10'")
  ]
