import sys


from greenlet import greenlet
from . import TestCase

def switch(*args):
    return greenlet.getcurrent().parent.switch(*args)


class ThrowTests(TestCase):
    def test_class(self):
        def f():
            try:
                switch("ok")
            except RuntimeError:
                switch("ok")
                return
            switch("fail")
        g = greenlet(f)
        res = g.switch()
        self.assertEqual(res, "ok")
        res = g.throw(RuntimeError)
        self.assertEqual(res, "ok")

    def test_val(self):
        def f():
            try:
                switch("ok")
            except RuntimeError:
                val = sys.exc_info()[1]
                if str(val) == "ciao":
                    switch("ok")
                    return
            switch("fail")

        g = greenlet(f)
        res = g.switch()
        self.assertEqual(res, "ok")
        res = g.throw(RuntimeError("ciao"))
        self.assertEqual(res, "ok")

        g = greenlet(f)
        res = g.switch()
        self.assertEqual(res, "ok")
        res = g.throw(RuntimeError, "ciao")
        self.assertEqual(res, "ok")

    def test_kill(self):
        def f():
            switch("ok")
            switch("fail")
        g = greenlet(f)
        res = g.switch()
        self.assertEqual(res, "ok")
        res = g.throw()
        self.assertTrue(isinstance(res, greenlet.GreenletExit))
        self.assertTrue(g.dead)
        res = g.throw()    # immediately eaten by the already-dead greenlet
        self.assertTrue(isinstance(res, greenlet.GreenletExit))

    def test_throw_goes_to_original_parent(self):
        main = greenlet.getcurrent()

        def f1():
            try:
                main.switch("f1 ready to catch")
            except IndexError:
                return "caught"
            else:
                return "normal exit"

        def f2():
            main.switch("from f2")

        g1 = greenlet(f1)
        g2 = greenlet(f2, parent=g1)
        with self.assertRaises(IndexError):
            g2.throw(IndexError)
        self.assertTrue(g2.dead)
        self.assertTrue(g1.dead)

        g1 = greenlet(f1)
        g2 = greenlet(f2, parent=g1)
        res = g1.switch()
        self.assertEqual(res, "f1 ready to catch")
        res = g2.throw(IndexError)
        self.assertEqual(res, "caught")
        self.assertTrue(g2.dead)
        self.assertTrue(g1.dead)

        g1 = greenlet(f1)
        g2 = greenlet(f2, parent=g1)
        res = g1.switch()
        self.assertEqual(res, "f1 ready to catch")
        res = g2.switch()
        self.assertEqual(res, "from f2")
        res = g2.throw(IndexError)
        self.assertEqual(res, "caught")
        self.assertTrue(g2.dead)
        self.assertTrue(g1.dead)

    def test_non_traceback_param(self):
        with self.assertRaises(TypeError) as exc:
            greenlet.getcurrent().throw(
                Exception,
                Exception(),
                self
            )
        self.assertEqual(str(exc.exception),
                         "throw() third argument must be a traceback object")

    def test_instance_of_wrong_type(self):
        with self.assertRaises(TypeError) as exc:
            greenlet.getcurrent().throw(
                Exception(),
                BaseException()
            )

        self.assertEqual(str(exc.exception),
                         "instance exception may not have a separate value")

    def test_not_throwable(self):
        with self.assertRaises(TypeError) as exc:
            greenlet.getcurrent().throw(
                "abc"
            )
        self.assertEqual(str(exc.exception),
                         "exceptions must be classes, or instances, not str")
