import org.junit.Before;
import org.junit.Test;

public class TestPersonsJUnit {

    // Fields that correspond to the variables in testStuff()
    private Person p1;
    private Person p2;
    private Person p3;
    private Person p4;

    private PhDStudent phd1;
    private Teacher t1;
    private Student s1;

    /**
     * This is the JUnit version of testStuff(): it runs before EACH test.
     */
    @Before
    public void setUp() {
        p1 = new Student();
        p2 = new PhDStudent();
        p3 = new Person();
        p4 = new Teacher();

        phd1 = new PhDStudent();
        t1 = new Teacher();
        s1 = new Student();
    }

    // ---- Each line from stuff() as its own test ----

    @Test
    public void test01_p1EqualsS1() {
        p1 = s1;
    }

    @Test
    public void test02_p1EqualsCastPersonS1() {
        p1 = (Person) s1;
    }

    @Test
    public void test03_p1EqualsCastPhDStudentP2() {
        p1 = (PhDStudent) p2;
    }

    // t1 = s1;  // DOES NOT COMPILE: Teacher <- Student

    // t1 = new Student();  // DOES NOT COMPILE: Teacher <- Student

    // t1 = new Person();   // DOES NOT COMPILE: Teacher <- Person

    @Test
    public void test07_objectFromP1() {
        Object o1 = p1;
    }

    @Test
    public void test08_objectFromCastStudentP1() {
        Object o1 = (Student) p1;
    }

    @Test(expected = ClassCastException.class)
    public void test09_s1FromCastStudentNewObject() {
        // This compiles, but will throw at runtime.
        s1 = (Student) new Object();
    }

    @Test
    public void test10_s1EqualsPhd1() {
        s1 = phd1;
    }

    @Test
    public void test11_s1EqualsNewPhDStudent() {
        s1 = new PhDStudent();
    }

    //12. compiler error s1 =  p1;

    @Test
    public void test13_s1EqualsCastStudentP1() {
        s1 = (Student) p1;
    }

    @Test
    public void test14_p2EqualsCastStudentNewPhDStudent() {
        p2 = (Student) (new PhDStudent());
    }

    // phd1 = s1;   // DOES NOT COMPILE: PhDStudent <- Student (downcast needed)

    // phd1 = p2;   // DOES NOT COMPILE: PhDStudent <- Person (cast needed)

    @Test(expected = ClassCastException.class)
    public void test17_phd1EqualsCastPhDStudentS1() {
        // crash
        phd1 = (PhDStudent) s1;
    }

    @Test
    public void test18_phd1EqualsCastPhDStudentP2() {
        phd1 = (PhDStudent) p2;
    }

    @Test(expected = ClassCastException.class)
    public void test19_phd1EqualsCastPhDStudentNewStudent() {
        // Compiles but blows up at runtime if new Student() is not actually a PhDStudent.
        phd1 = (PhDStudent) (new Student());
    }

    @Test
    public void test20_nameFromP2() {
        String name = p2.getName();
    }

    @Test
    public void test21_nameFromP3() {
        String name = p3.getName();
    }

    @Test
    public void test22_nameFromT1() {
        String name = t1.getName();
    }

    @Test
    public void test23_gpaFromS1() {
        double gpa = s1.getGPA();
    }

    // 24 Compiler error
    //    double gpa = p2.getGPA();
    // 25 compiler error
    //    double gpa = p3.getGPA();

    @Test
    public void test26_gpaFromCastStudentP1() {
        double gpa = ((Student) p1).getGPA();
    }

    @Test(expected = ClassCastException.class)
    public void test27_gpaFromCastStudentP3() {
        double gpa = ((Student) p3).getGPA();
    }

    // double gpa = (Student) p2.getGPA();  // DOES NOT COMPILE: cannot cast double to Student
    // 29, 30 compiler error
    //    boolean abd = p1.isABD();
    //    boolean abd = p2.isABD();
    

    @Test(expected = ClassCastException.class)
    public void test31_abdFromCastPhDStudentP1() {
        boolean abd = ((PhDStudent) p1).isABD();
    }

    @Test
    public void test32_abdFromPhd1() {
        boolean abd = phd1.isABD();
    }
    // 33 compiler error
    //    boolean abd = ((Student) phd1).isABD();
    // 34
    // boolean abd = (Student) phd1.isABD(); // DOES NOT COMPILE: cannot cast boolean to Student
    // 35 compiler
    //    boolean abd = s1.isABD();
    

    @Test
    public void test36_t1GiveGradeS1() {
        t1.giveGrade(s1, 1);
    }

    @Test
    public void test37_t1GiveGradePhd1() {
        t1.giveGrade(phd1, 1);
    }

    // t1.giveGrade(p1, 1); // LIKELY DOES NOT COMPILE: Person not acceptable if giveGrade expects Student

    // t1.giveGrade(p3, 1); // LIKELY DOES NOT COMPILE for same reason

    @Test(expected = ClassCastException.class)
    public void test40_t1GiveGradeCastStudentP3() {
        t1.giveGrade((Student) p3, 1);
    }

    @Test
    public void test41_t1GiveGradeCastStudentP2() {
        t1.giveGrade((Student) p2, 1);
    }

    // p3.giveGrade(s1,1); // DOES NOT COMPILE: giveGrade not defined on Person (most likely)

    @Test(expected = ClassCastException.class)
    public void test43_castTeacherP1GiveGradeS1() {
        ((Teacher) p1).giveGrade(s1, 1);
    }
    // 44 compiler error
    //    ((Teacher) s1).giveGrade(s1, 1);

    @Test
    public void test45_castTeacherP4GiveGradeNewPhDStudentAsStudent() {
        ((Teacher) p4).giveGrade((Student) (new PhDStudent()), 1);
    }


    /*
     * Original lines that are commented out because they do not compile:
     *
     *   t1 = s1;
     *   t1 = new Student();
     *   t1 = new Person();
     *   phd1 = s1;
     *   phd1 = p2;
     *   double gpa = (Student) p2.getGPA();
     *   boolean abd = (Student) phd1.isABD();
     *   t1.giveGrade(p1, 1);
     *   t1.giveGrade(p3, 1);
     *   p3.giveGrade(s1, 1);
     *
     * If you want students to see these as compile-time errors,
     * you can leave them in a non-JUnit class or keep them commented as examples.
     */
}
