#!/usr/bin/env python3

from __future__ import division

import math
import unittest

from ellipse import Ellipse


class EllipseTests(unittest.TestCase):
    """
    Tests for ellipse.
    """

    def test_big_base(self):
        ellipse = Ellipse(5, 1)
        self.assertEqual(ellipse.big_base, 5)

    def test_small_base(self):
        ellipse = Ellipse(5, 3)
        self.assertEqual(ellipse.small_base, 3)

    def test_default_values(self):
        ellipse = Ellipse()
        self.assertEqual(ellipse.big_base, 1)
        self.assertEqual(ellipse.small_base, 1)

    def test_half_big_base(self):
        ellipse = Ellipse(2, 1)
        self.assertEqual(ellipse.half_big_base, 1)

    def test_half_small_base(self):
        ellipse = Ellipse(2, 1)
        self.assertEqual(ellipse.half_small_base, 0.5)

    def test_area(self):
        ellipse = Ellipse()  # Circle of radius 0.5
        self.assertEqual(ellipse.area, math.pi / 4)
        ellipse = Ellipse(2, 2)  # circle of radius 1
        self.assertEqual(ellipse.area, math.pi)
        ellipse = Ellipse(20, 4)  # actual_ellipse
        self.assertEqual(ellipse.area, math.pi * 10 * 2)

    def test_perimeter(self):
        ellipse = Ellipse()  # Circle of radius 0.5
        self.assertEqual(ellipse.perimeter, 2 * math.pi * 0.5)
        ellipse = Ellipse(2, 2)  # circle of radius 1
        self.assertEqual(ellipse.perimeter, 2 * math.pi)
        ellipse = Ellipse(20, 4)  # actual ellipse
        self.assertEqual(ellipse.perimeter, 45.308693596555905)

    def test_string_representation(self):
        ellipse = Ellipse()
        self.assertEqual(str(ellipse), 'Ellipse(1, 1)')
        self.assertEqual(repr(ellipse), 'Ellipse(1, 1)')
        ellipse.big_base = 10
        self.assertEqual(repr(ellipse), 'Ellipse(10, 1)')
        ellipse.small_base = 5.5
        self.assertEqual(repr(ellipse), 'Ellipse(10, 5.5)')

    # Bonus 1 of this exercise, comment out to actually test
    @unittest.expectedFailure
    def test_attributes_modification_reaction(self):
        ellipse = Ellipse()
        self.assertEqual(ellipse.half_big_base, 0.5)
        self.assertEqual(ellipse.big_base, 1)

        ellipse.big_base = 4
        self.assertEqual(ellipse.half_big_base, 2)
        self.assertEqual(ellipse.area, math.pi * 2 / 2)
        self.assertEqual(ellipse.perimeter, 9.15923781814074)

    # Bonus 2 of this exercise, comment out to actually test
    @unittest.expectedFailure
    def test_half_bases_changeable_but_area_or_perimeter_not(self):
        ellipse = Ellipse()
        self.assertEqual(ellipse.half_big_base, 0.5)
        self.assertEqual(ellipse.area, math.pi / 2 / 2)

        ellipse.half_big_base = 4
        self.assertEqual(ellipse.big_base, 8)
        self.assertEqual(ellipse.area, math.pi * 4 / 2)
        self.assertEqual(ellipse.perimeter, 17.909833696475356)

        ellipse.half_small_base = 4
        self.assertEqual(ellipse.small_base, 8)
        self.assertEqual(ellipse.area, math.pi * 4 * 4)
        self.assertEqual(ellipse.perimeter, 25.132741228718345)

        with self.assertRaises(AttributeError):
            ellipse.area = 3

        with self.assertRaises(AttributeError):
            ellipse.perimeter = 3

    # Bonus 3 of this exercise, comment out to actually test
    @unittest.expectedFailure
    def test_no_negative_radius(self):
        ellipse = Ellipse(2, 2)
        with self.assertRaises(ValueError) as context:
            ellipse.big_base = -10
        self.assertEqual(str(context.exception), "Base cannot be negative")
        with self.assertRaises(ValueError) as context:
            ellipse.small_base = -10
        self.assertEqual(str(context.exception), "Base cannot be negative")

        with self.assertRaises(ValueError) as context:
            ellipse.half_big_base = -20
        self.assertEqual(str(context.exception), "Base cannot be negative")
        with self.assertRaises(ValueError) as context:
            ellipse.half_small_base = -20
        self.assertEqual(str(context.exception), "Base cannot be negative")

        self.assertEqual(ellipse.big_base, 2)
        self.assertEqual(ellipse.small_base, 2)


if __name__ == "__main__":
    unittest.main(verbosity=2)
