from AnyQt.QtTest import QSignalSpy
from Orange.data import Table, ContinuousVariable, Domain
from Orange.widgets.tests.base import WidgetTest
from Orange.widgets.tests.utils import simulate

from orangecontrib.imageanalytics.widgets.owimagegrid import OWImageGrid
import numpy as np

from orangecontrib.imageanalytics.widgets.tests.utils import load_images


class TestOWImageGrid(WidgetTest):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()

        cls.signal_name = "Embeddings"
        cls.signal_data = Table("iris")

        cls.test_images = load_images()

        domain = Domain([
            ContinuousVariable("emb1"), ContinuousVariable("emb2"),
            ContinuousVariable("emb3")],
            cls.test_images.domain.class_vars,
            cls.test_images.domain.metas
        )
        data = np.random.random((len(cls.test_images), 3))
        cls.fake_embeddings = Table(domain, data, cls.test_images.Y,
                                    metas=cls.test_images.metas)

    def setUp(self):
        self.widget = self.create_widget(OWImageGrid)

    def tearDown(self):
        self.widget.onDeleteWidget()
        self.widget.deleteLater()
        self.widget = None

    def _startandwait(self, widget):
        spy = QSignalSpy(widget.blockingStateChanged)
        widget.start()
        assert len(spy)
        assert spy[-1] == [True]
        assert spy.wait(5000)
        assert spy[-1] == [False]
        self.assertFalse(widget.isBlocking())

    def test_no_image_data(self):
        """
        It should not fail when there is a data without images.
        """
        table = Table("iris")
        self.send_signal("Embeddings", table)

    def test_no_data(self):
        """
        It should not fail when there is no data.
        """
        table = Table("iris")[:0]
        self.send_signal("Embeddings", table)
        self.send_signal("Embeddings", None)

    def test_subset_data(self):
        table = Table("iris")
        self.send_signal("Embeddings", table)
        self.send_signal("Data Subset", table[:5])

    def test_no_subset_data(self):
        table = Table("iris")
        self.send_signal("Embeddings", table)
        self.send_signal("Data Subset", table[:0])
        self.send_signal("Data Subset", None)

    def test_different_subset_data(self):
        self.send_signal("Embeddings", Table("iris"))
        self.send_signal("Data Subset", self.test_images)

    def test_selection(self):
        w = self.widget

        self.send_signal("Embeddings", self.fake_embeddings)

        # all image spaces are full
        w.colSpinner.setValue(2)
        w.rowSpinner.setValue(2)

        w.on_selection_changed(
            [w.items[0].widget, w.items[3].widget, w.items[2].widget], True)

        im_out = self.get_output("Images")
        self.assertEqual(sum(im_out.Y), 3)  # 3 selected elements

        sel_out = self.get_output("Selected Images")
        self.assertEqual(len(sel_out), 3)

    def test_labels(self):
        w = self.widget

        self.send_signal("Embeddings", self.fake_embeddings)
        self.wait_until_stop_blocking()

        self.assertTrue(
            all(x.label is None for x in w.thumbnailView.grid.thumbnails))

        simulate.combobox_activate_index(w.controls.label_attr, 2)
        self.wait_until_stop_blocking()
        self.assertTrue(
            all(x.label is not None for x in w.thumbnailView.grid.thumbnails))

        # reset to no labels
        simulate.combobox_activate_index(w.controls.label_attr, 0)
        self.wait_until_stop_blocking()
        self.assertTrue(
            all(x.label is None for x in w.thumbnailView.grid.thumbnails))

    def tests_subset_mixed_arrays(self):
        """
        This function test subsets with mixed arrays.
        """
        w = self.widget
        self.send_signal(w.Inputs.data, self.fake_embeddings)
        self.assertEqual(0, len(w.subset_indices))

        # send regular subset
        self.send_signal(w.Inputs.data_subset, self.fake_embeddings[:2])
        self.assertListEqual([True, True, False, False], w.subset_indices)

        self.send_signal(w.Inputs.data_subset, self.fake_embeddings[::2])
        self.assertListEqual([True, False, True, False], w.subset_indices)

        # send mixed subset
        self.send_signal(w.Inputs.data_subset, self.fake_embeddings[[3, 2]])
        self.assertListEqual([False, False, True, True], w.subset_indices)

        self.send_signal(w.Inputs.data_subset, self.fake_embeddings[[3, 0, 2]])
        self.assertListEqual([True, False, True, True], w.subset_indices)

        # try with mixed data (data has mixed indices - at [0] there is no
        # row with index 0
        mixed_fake_emb = self.fake_embeddings[[1, 3, 2, 0]]
        self.send_signal(w.Inputs.data_subset, None)
        self.send_signal(w.Inputs.data, mixed_fake_emb)
        self.assertEqual(0, len(w.subset_indices))

        # send regular subset
        self.send_signal(w.Inputs.data_subset, self.fake_embeddings[:2])
        self.assertListEqual([True, False, False, True], w.subset_indices)

        self.send_signal(w.Inputs.data_subset, self.fake_embeddings[::2])
        self.assertListEqual([False, False, True, True], w.subset_indices)

        # send mixed subset
        self.send_signal(w.Inputs.data_subset, self.fake_embeddings[[3, 2]])
        self.assertListEqual([False, True, True, False], w.subset_indices)

        self.send_signal(w.Inputs.data_subset, self.fake_embeddings[[3, 0, 2]])
        self.assertListEqual([False, True, True, True], w.subset_indices)

    def tests_subset_conditions(self):
        """
        This function test the condition for the validness of the subset.
        Here we will just consider examples not included in
        `tests_subset_mixed_arrays`
        """
        w = self.widget
        self.send_signal(w.Inputs.data, self.fake_embeddings)
        self.assertEqual(0, len(w.subset_indices))

        # test with different indices in the subset
        fake_embedding_copy = self.fake_embeddings.copy()
        fake_embedding_copy.ids[0] = 10000
        self.send_signal(w.Inputs.data_subset, fake_embedding_copy)
        self.assertListEqual([], w.subset_indices)
        self.assertTrue(self.widget.Warning.incompatible_subset.is_shown())

        # reset
        self.send_signal(w.Inputs.data_subset, None)
        self.assertListEqual([], w.subset_indices)
        self.assertFalse(self.widget.Warning.incompatible_subset.is_shown())

        # test with different image under the same index
        fake_embedding_copy = self.fake_embeddings.copy()
        fake_embedding_copy[0, "Images"] = "tralala"
        self.send_signal(w.Inputs.data_subset, fake_embedding_copy)
        self.assertListEqual([], w.subset_indices)
        self.assertTrue(self.widget.Warning.incompatible_subset.is_shown())
