# Copyright 2014 MINES ParisTech
#
# This file is part of LinPy.
#
# LinPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# LinPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LinPy.  If not, see <http://www.gnu.org/licenses/>.

import math
import unittest

from ..geometry import *
from ..linexprs import Symbol
from ..polyhedra import Eq


class TestPoint(unittest.TestCase):

    def setUp(self):
        self.x = Symbol('x')
        self.y = Symbol('y')
        self.z = Symbol('z')
        self.pt1 = Point({self.x: 10, self.y: 5, self.z: 1})
        self.pt2 = Point({self.x: 15, self.y: 40, self.z: 60})
        self.vec1 = Vector({self.x: 20, self.y: 30, self.z: 40})

    def test_add(self):
        self.assertEqual(self.pt1 + self.vec1, Point({self.x: 30, self.y: 35, self.z: 41}))
        with self.assertRaises(TypeError):
            self.pt1 + self.pt2

    def test_eq(self):
        self.assertEqual(self.pt1, self.pt1)
        self.assertNotEqual(self.pt1, self.pt2)
        self.assertNotEqual(self.pt1, self.vec1)

    def test_sub(self):
        self.assertEqual(self.pt1 - self.pt2, Vector({self.x: -5, self.y: -35, self.z: -59}))
        self.assertEqual(self.pt1 - self.vec1, Point({self.x: -10, self.y: -25, self.z: -39}))

    def test_aspolyhedron(self):
        self.assertEqual(self.pt1.aspolyhedron(), Eq(self.x, 10) & Eq(self.y, 5) & Eq(self.z, 1))


class TestVector(unittest.TestCase):

    def setUp(self):
        self.x = Symbol('x')
        self.y = Symbol('y')
        self.z = Symbol('z')
        self.pt1 = Point({self.x: 10, self.y: 5, self.z: 1})
        self.pt2 = Point({self.x: 15, self.y: 40, self.z: 60})
        self.vec1 = Vector({self.x: 20, self.y: 30, self.z: 40})
        self.vec2 = Vector({self.x: 45, self.y: 70, self.z: 80})

    def test_add(self):
        self.assertEqual(self.vec1 + self.pt1, Point({self.x: 30, self.y: 35, self.z: 41}))
        self.assertEqual(self.vec1 + self.vec2, Vector({self.x: 65, self.y: 100, self.z: 120}))

    def test_angle(self):
        self.assertEqual(math.degrees(self.vec1.angle(self.vec1)), 0)
        self.assertAlmostEqual(math.degrees(self.vec1.angle(self.vec2)), 4.15129, places=5)
        self.assertAlmostEqual(math.degrees(self.vec2.angle(self.vec1)), 4.15129, places=5)

    def test_cross(self):
        self.assertEqual(self.vec1.cross(self.vec2), Vector({self.x: -400, self.y: 200, self.z: 50}))

    def test_div(self):
        self.assertEqual(self.vec1 / 10, Vector({self.x: 2, self.y: 3, self.z: 4}))

    def test_dot(self):
        self.assertEqual(self.vec1.dot(self.vec2), 6200)

    def test_eq(self):
        self.assertEqual(self.vec1, self.vec1)
        self.assertNotEqual(self.vec1, self.vec2)

    def test_mul(self):
        self.assertEqual(75 * self.vec1, Vector({self.x: 1500, self.y: 2250, self.z: 3000}))
        self.assertEqual(self.vec1 * 75, Vector({self.x: 1500, self.y: 2250, self.z: 3000}))

    def test_neg(self):
        self.assertEqual(-self.vec1, Vector({self.x: -20, self.y: -30, self.z: -40}))

    def test_norm(self):
        self.assertAlmostEqual(self.vec1.norm(), 53.85165, places=5)

    def test_norm2(self):
        self.assertEqual(self.vec1.norm2(), 2900)

    def test_asunit(self):
        unit = self.vec1.asunit()
        self.assertAlmostEqual(unit[self.x], 0.37139, 5)
        self.assertAlmostEqual(unit[self.y], 0.55709, 5)
        self.assertAlmostEqual(unit[self.z], 0.74278, 5)

    def test_sub(self):
        self.assertEqual(self.vec1 - self.pt1, Point({self.x: 10, self.y: 25, self.z: 39}))
        self.assertEqual(self.vec1 - self.vec2, Vector({self.x: -25, self.y: -40, self.z: -40}))
