import sys
import time

import objc
import Foundation
from PyObjCTest.testhelper import PyObjC_TestClass4
from PyObjCTools.TestSupport import TestCase


class ThreadingTest(TestCase):
    def setUp(self):
        # Set a very small check interval, this will make it more likely
        # that the interpreter crashes when threading is done incorrectly.
        if sys.version_info[:2] >= (3, 2):
            self._int = sys.getswitchinterval()
            sys.setswitchinterval(0.000_000_1)
        else:
            self._int = sys.getcheckinterval()
            sys.setcheckinterval(1)

    def tearDown(self):
        if sys.version_info[:2] >= (3, 2):
            sys.setswitchinterval(self._int)
        else:
            sys.setcheckinterval(self._int)

    def testNSObjectString(self):
        class PyObjCTestThreadRunnerString(Foundation.NSObject):
            def init(self):
                self = objc.super(PyObjCTestThreadRunnerString, self).init()
                if self is None:
                    return None

                self.storage = []
                return self

            def run_(self, argument):
                Foundation.NSAutoreleasePool.alloc().init()
                self.storage.append(argument)

        myObj = PyObjCTestThreadRunnerString.alloc().init()

        Foundation.NSThread.detachNewThreadSelector_toTarget_withObject_(
            "run:", myObj, "hello world"
        )

        time.sleep(2)
        self.assertEqual(myObj.storage[0], "hello world")

    def testNSObject(self):
        class PyObjCTestThreadRunner(Foundation.NSObject):
            def run_(self, argument):
                Foundation.NSAutoreleasePool.alloc().init()
                for i in range(100):
                    argument.append(i)

        myObj = PyObjCTestThreadRunner.alloc().init()
        lst = []

        Foundation.NSThread.detachNewThreadSelector_toTarget_withObject_(
            "run:", myObj, lst
        )

        lst2 = []
        for i in range(100):
            lst2.append(i * 2)

        time.sleep(2)
        self.assertEqual(lst, list(range(100)))

    def testPyObject(self):
        import os

        class TestThreadRunner:
            def run_(self, argument):
                for i in range(100):
                    argument.append(i)

        myObj = TestThreadRunner()
        lst = []

        # Redirect stderr to avoid spurious messages when running the
        # tests.
        dupped = os.dup(2)
        fp = os.open("/dev/null", os.O_RDWR)
        os.dup2(fp, 2)
        os.close(fp)

        try:
            Foundation.NSThread.detachNewThreadSelector_toTarget_withObject_(
                "run:", myObj, lst
            )

            lst2 = []
            for i in range(100):
                lst2.append(i * 2)

            time.sleep(2)
            self.assertEqual(lst, list(range(100)))

        finally:
            os.dup2(dupped, 2)

    def testCalling(self):
        class Dummy:
            pass

        class PyObjCTestCalling(Foundation.NSObject):
            def call(self):
                return Dummy()

        my = PyObjC_TestClass4.alloc().init()
        cb = PyObjCTestCalling.alloc().init()

        Foundation.NSThread.detachNewThreadSelector_toTarget_withObject_(
            "runThread:", my, cb
        )

        time.sleep(2)

        retval = my.returnObject()
        self.assertIsInstance(retval, Dummy)
