import copy
from time import clock
import unittest
from nose.tools import eq_
from helix.calculators.subarray_graph import SubarrayGraph
from test.fixtures.sample_arrays import *
from helix.constants.system_type import SystemType


class SubarrayGraphTest(unittest.TestCase):
    def test_find_disconnected_subgraphs(self):
        copy_dumbell_no_wind_anchors = copy.deepcopy(dumbell_no_wind_anchors)
        graph = SubarrayGraph(copy_dumbell_no_wind_anchors, SystemType.singleTilt)

        graph.pop_rung()
        subgraphs = graph.find_disconnected_subgraphs()

        eq_(len(subgraphs), 2)
        eq_(len(subgraphs[0]), 4)
        eq_(len(subgraphs[1]), 4)

    def test_find_disconnected_subgraphs_more_complicated(self):
        copy_odd_shape_no_wind_anchors = copy.deepcopy(odd_shape_no_wind_anchors)
        graph = SubarrayGraph(copy_odd_shape_no_wind_anchors, SystemType.singleTilt)

        graph.pop_rung()
        subgraphs = graph.find_disconnected_subgraphs()

        eq_(len(subgraphs), 3)
        eq_(len(subgraphs[0]), 4)
        eq_(len(subgraphs[1]), 8)
        eq_(len(subgraphs[2]), 1)

    def test_find_node(self):
        copy_odd_shape_no_wind_anchors = copy.deepcopy(odd_shape_no_wind_anchors)
        graph = SubarrayGraph(copy_odd_shape_no_wind_anchors, SystemType.singleTilt)

        eq_(graph.find_node(Coordinate(-1, -1)), None)

        lower_left_node = self.naive_find_node(graph, Coordinate(0, 0))
        eq_(graph.find_node(Coordinate(0, 0)), lower_left_node)

        middle_ish_node = self.naive_find_node(graph, Coordinate(2, 7))
        eq_(graph.find_node(Coordinate(2, 7)), middle_ish_node)

    # def test_find_node_performance(self):
    #     copy_odd_shape_no_wind_anchors = copy.deepcopy(odd_shape_no_wind_anchors)
    #     graph = SubarrayGraph(copy_odd_shape_no_wind_anchors, SystemType.singleTilt)
    #
    #     iteration_count = 100
    #
    #     invalid_base = self.check_performance(lambda: self.naive_find_node(graph, Coordinate(-1, -1)), iteration_count)
    #     lower_left_base = self.check_performance(lambda: self.naive_find_node(graph, Coordinate(0, 0)), iteration_count)
    #     middle_ish_base = self.check_performance(lambda: self.naive_find_node(graph, Coordinate(2, 7)), iteration_count)
    #
    #     invalid_test = self.check_performance(lambda: graph.find_node(Coordinate(-1, -1)), iteration_count)
    #     lower_left_test = self.check_performance(lambda: graph.find_node(Coordinate(0, 0)), iteration_count)
    #     middle_ish_test = self.check_performance(lambda: graph.find_node(Coordinate(2, 7)), iteration_count)
    #
    #     print("Invalid coordinate: %f base, %f actual" % (invalid_base, invalid_test))
    #     print("0, 0 coordinate: %f base, %f actual" % (lower_left_base, lower_left_test))
    #     print("2, 7 coordinate: %f base, %f actual" % (middle_ish_base, middle_ish_test))
    #
    #     assert invalid_test < (invalid_base / 10), "Expected invalid coordinate to almost no-op, didn't"
    #     assert lower_left_test < lower_left_base, "Expected 0, 0 coordinate to be better than naive approach"
    #     assert middle_ish_base < middle_ish_test, "Expected 2, 7 coordinate to be better than naive approach"

    def naive_find_node(self, graph, coordinate):
        for node in graph.nodes:
            if node.location == coordinate:
                return node

    def check_performance(self, function, iterations):
        start = clock()
        for _ in range(iterations):
            function()
        end = clock()
        return (end - start) / iterations
