from zone_api.core.zone_manager import ZoneManager
from zone_api import platform_encapsulator as pe

from zone_api.core.zone import Zone
from zone_api.core.zone_event import ZoneEvent
from zone_api.core.devices.dimmer import Dimmer
from zone_api.core.devices.switch import Fan, Light, Switch
from zone_api.core.devices.illuminance_sensor import IlluminanceSensor
from zone_api.core.devices.motion_sensor import MotionSensor
from zone_api.core.actions.turn_on_switch import TurnOnSwitch

from zone_api_test.core.device_test import DeviceTest

ILLUMINANCE_THRESHOLD_IN_LUX = 8
INVALID_ITEM_NAME = 'invalid item name'


class ZoneManagerTest(DeviceTest):
    """ Unit tests for zone_manager.py. """

    def setUp(self):
        items = [pe.create_switch_item('TestLightName'),
                 pe.create_switch_item('TestMotionSensorName'),
                 pe.create_number_item('IlluminanceSensorName'),
                 pe.create_string_item('AstroSensorName'),
                 pe.create_dimmer_item('TestDimmerName'),
                 pe.create_switch_item('TestFanName'),
                 ]

        self.set_items(items)
        super(ZoneManagerTest, self).setUp()

        [self.lightItem, self.motionSensorItem,
         self.illuminanceSensorItem, self.astroSensorItem, self.dimmerItem,
         self.fanItem] = items

        self.illuminanceSensor = IlluminanceSensor(self.illuminanceSensorItem)
        self.light = Light(self.lightItem, 2,
                           ILLUMINANCE_THRESHOLD_IN_LUX)
        self.motionSensor = MotionSensor(self.motionSensorItem)
        self.dimmer = Dimmer(self.dimmerItem, 2, 100, "0-23:59")
        self.fan = Fan(self.fanItem, 2)

        self.zm = ZoneManager()

    def tearDown(self):
        self.zm.stop_auto_report_watch_dog()
        self.fan._cancel_timer()
        self.dimmer._cancel_timer()
        self.light._cancel_timer()

        super(ZoneManagerTest, self).tearDown()

    def testAddZone_validZone_zoneAdded(self):
        zone1 = Zone('ff')
        self.zm.add_zone(zone1)
        self.assertEqual(1, len(self.zm.get_zones()))

        zone2 = Zone('2f')
        self.zm.add_zone(zone2)
        self.assertEqual(2, len(self.zm.get_zones()))

    def testGetZoneById_validZoneId_returnValidZone(self):
        zone1 = Zone('ff')
        self.zm.add_zone(zone1)

        zone2 = Zone('2f')
        self.zm.add_zone(zone2)

        self.assertEqual(zone1.get_name(),
                         self.zm.get_zone_by_id(zone1.get_id()).get_name())
        self.assertEqual(zone2.get_name(),
                         self.zm.get_zone_by_id(zone2.get_id()).get_name())

    def testGetZoneById_invalidZoneId_returnNone(self):
        self.assertTrue(self.zm.get_zone_by_id('invalid zone id') is None)

    def testRemoveZone_validZone_zoneRemoved(self):
        zone1 = Zone('ff')
        self.zm.add_zone(zone1)

        zone2 = Zone('2f')
        self.zm.add_zone(zone2)

        self.assertEqual(2, len(self.zm.get_zones()))

        self.zm.remove_zone(zone1)
        self.assertEqual(1, len(self.zm.get_zones()))

        self.zm.remove_zone(zone2)
        self.assertEqual(0, len(self.zm.get_zones()))

    def testContainingZone_validDevice_returnsCorrectZone(self):
        zone1 = Zone('ff').add_device(self.light)
        zone2 = Zone('sf').add_device(self.fan)

        self.zm.add_zone(zone1)
        self.zm.add_zone(zone2)
        self.assertEqual(zone1,
                         self.zm.get_immutable_instance().get_containing_zone(self.light))
        self.assertEqual(zone2,
                         self.zm.get_immutable_instance().get_containing_zone(self.fan))

    def testContainingZone_invalidDevice_returnsNone(self):
        zone1 = Zone('ff').add_device(self.light)

        self.zm.add_zone(zone1)
        self.assertEqual(None,
                         self.zm.get_immutable_instance().get_containing_zone(self.fan))

    def testGetDevicesByType_variousScenarios_returnsCorrectList(self):
        zone1 = Zone('ff').add_device(self.light)
        zone2 = Zone('sf').add_device(self.fan)

        self.zm.add_zone(zone1)
        self.zm.add_zone(zone2)
        self.assertEqual(2, len(self.zm.get_zones()))

        self.assertEqual(1, len(self.zm.get_devices_by_type(Light)))
        self.assertEqual(2, len(self.zm.get_devices_by_type(Switch)))

        self.assertEqual(0, len(self.zm.get_devices_by_type(Dimmer)))

    def testOnMotionSensorTurnedOn_noZone_returnsFalse(self):
        self.assertFalse(self.zm.get_immutable_instance().dispatch_event(
            ZoneEvent.MOTION, pe.get_event_dispatcher(), self.motionSensor, pe.create_string_item(INVALID_ITEM_NAME)))

    def testOnMotionSensorTurnedOn_withNonApplicableZone_returnsFalse(self):
        zone = Zone('ff', [self.light, self.motionSensor])
        self.zm.add_zone(zone)

        self.assertFalse(self.zm.get_immutable_instance().dispatch_event(
            ZoneEvent.MOTION, pe.get_event_dispatcher(), self.motionSensor, pe.create_string_item(INVALID_ITEM_NAME)))

    def testOnMotionSensorTurnedOn_withApplicableZone_returnsTrue(self):
        self.assertFalse(self.light.is_on())
        pe.set_number_value(self.illuminanceSensorItem, ILLUMINANCE_THRESHOLD_IN_LUX - 1)

        zone = Zone('ff', [self.light, self.motionSensor, self.illuminanceSensor])
        zone = zone.add_action(TurnOnSwitch())
        self.zm.add_zone(zone)

        self.assertTrue(self.zm.get_immutable_instance().dispatch_event(
            ZoneEvent.MOTION, pe.get_event_dispatcher(), self.motionSensor, self.motionSensor.get_item()))

    def testOnSwitchTurnedOn_noZone_returnsFalse(self):
        self.assertFalse(self.zm.get_immutable_instance().on_switch_turned_on(
            pe.get_event_dispatcher(), self.light, pe.create_string_item(INVALID_ITEM_NAME)))

    def testOnSwitchTurnedOn_withNonApplicableZone_returnsFalse(self):
        zone = Zone('ff', [self.light, self.motionSensor])
        self.zm.add_zone(zone)

        self.assertFalse(self.zm.get_immutable_instance().on_switch_turned_on(
            pe.get_event_dispatcher(), self.light, pe.create_string_item(INVALID_ITEM_NAME)))

    def testOnSwitchTurnedOn_withApplicableZone_returnsTrue(self):
        zone = Zone('ff', [self.light, self.motionSensor])
        self.zm.add_zone(zone)

        self.assertTrue(self.zm.get_immutable_instance().on_switch_turned_on(
            pe.get_event_dispatcher(), self.light, self.light.get_item()))

    def testOnSwitchTurnedOff_noZone_returnsFalse(self):
        self.assertFalse(self.zm.get_immutable_instance().on_switch_turned_off(
            pe.get_event_dispatcher(), self.light, pe.create_string_item(INVALID_ITEM_NAME)))

    def testOnSwitchTurnedOff_withNonApplicableZone_returnsFalse(self):
        zone = Zone('ff', [self.light, self.motionSensor])
        self.zm.add_zone(zone)

        self.assertFalse(self.zm.get_immutable_instance().on_switch_turned_off(
            pe.get_event_dispatcher(), self.light, pe.create_string_item(INVALID_ITEM_NAME)))

    def testOnSwitchTurnedOff_withApplicableZone_returnsTrue(self):
        zone = Zone('ff', [self.light, self.motionSensor])
        self.zm.add_zone(zone)

        self.assertTrue(self.zm.get_immutable_instance().on_switch_turned_off(
            pe.get_event_dispatcher(), self.light, self.light.get_item()))

[((1361, 1406), 'zone_api.core.devices.illuminance_sensor.IlluminanceSensor', 'IlluminanceSensor', (['self.illuminanceSensorItem'], {}), '(self.illuminanceSensorItem)\n', (1378, 1406), False, 'from zone_api.core.devices.illuminance_sensor import IlluminanceSensor\n'), ((1428, 1482), 'zone_api.core.devices.switch.Light', 'Light', (['self.lightItem', '(2)', 'ILLUMINANCE_THRESHOLD_IN_LUX'], {}), '(self.lightItem, 2, ILLUMINANCE_THRESHOLD_IN_LUX)\n', (1433, 1482), False, 'from zone_api.core.devices.switch import Fan, Light, Switch\n'), ((1538, 1573), 'zone_api.core.devices.motion_sensor.MotionSensor', 'MotionSensor', (['self.motionSensorItem'], {}), '(self.motionSensorItem)\n', (1550, 1573), False, 'from zone_api.core.devices.motion_sensor import MotionSensor\n'), ((1596, 1638), 'zone_api.core.devices.dimmer.Dimmer', 'Dimmer', (['self.dimmerItem', '(2)', '(100)', '"""0-23:59"""'], {}), "(self.dimmerItem, 2, 100, '0-23:59')\n", (1602, 1638), False, 'from zone_api.core.devices.dimmer import Dimmer\n'), ((1658, 1678), 'zone_api.core.devices.switch.Fan', 'Fan', (['self.fanItem', '(2)'], {}), '(self.fanItem, 2)\n', (1661, 1678), False, 'from zone_api.core.devices.switch import Fan, Light, Switch\n'), ((1698, 1711), 'zone_api.core.zone_manager.ZoneManager', 'ZoneManager', ([], {}), '()\n', (1709, 1711), False, 'from zone_api.core.zone_manager import ZoneManager\n'), ((1999, 2009), 'zone_api.core.zone.Zone', 'Zone', (['"""ff"""'], {}), "('ff')\n", (2003, 2009), False, 'from zone_api.core.zone import Zone\n'), ((2113, 2123), 'zone_api.core.zone.Zone', 'Zone', (['"""2f"""'], {}), "('2f')\n", (2117, 2123), False, 'from zone_api.core.zone import Zone\n'), ((2286, 2296), 'zone_api.core.zone.Zone', 'Zone', (['"""ff"""'], {}), "('ff')\n", (2290, 2296), False, 'from zone_api.core.zone import Zone\n'), ((2346, 2356), 'zone_api.core.zone.Zone', 'Zone', (['"""2f"""'], {}), "('2f')\n", (2350, 2356), False, 'from zone_api.core.zone import Zone\n'), ((2829, 2839), 'zone_api.core.zone.Zone', 'Zone', (['"""ff"""'], {}), "('ff')\n", (2833, 2839), False, 'from zone_api.core.zone import Zone\n'), ((2889, 2899), 'zone_api.core.zone.Zone', 'Zone', (['"""2f"""'], {}), "('2f')\n", (2893, 2899), False, 'from zone_api.core.zone import Zone\n'), ((4754, 4797), 'zone_api.core.zone.Zone', 'Zone', (['"""ff"""', '[self.light, self.motionSensor]'], {}), "('ff', [self.light, self.motionSensor])\n", (4758, 4797), False, 'from zone_api.core.zone import Zone\n'), ((5150, 5236), 'zone_api.platform_encapsulator.set_number_value', 'pe.set_number_value', (['self.illuminanceSensorItem', '(ILLUMINANCE_THRESHOLD_IN_LUX - 1)'], {}), '(self.illuminanceSensorItem, \n    ILLUMINANCE_THRESHOLD_IN_LUX - 1)\n', (5169, 5236), True, 'from zone_api import platform_encapsulator as pe\n'), ((5248, 5315), 'zone_api.core.zone.Zone', 'Zone', (['"""ff"""', '[self.light, self.motionSensor, self.illuminanceSensor]'], {}), "('ff', [self.light, self.motionSensor, self.illuminanceSensor])\n", (5252, 5315), False, 'from zone_api.core.zone import Zone\n'), ((5892, 5935), 'zone_api.core.zone.Zone', 'Zone', (['"""ff"""', '[self.light, self.motionSensor]'], {}), "('ff', [self.light, self.motionSensor])\n", (5896, 5935), False, 'from zone_api.core.zone import Zone\n'), ((6224, 6267), 'zone_api.core.zone.Zone', 'Zone', (['"""ff"""', '[self.light, self.motionSensor]'], {}), "('ff', [self.light, self.motionSensor])\n", (6228, 6267), False, 'from zone_api.core.zone import Zone\n'), ((6773, 6816), 'zone_api.core.zone.Zone', 'Zone', (['"""ff"""', '[self.light, self.motionSensor]'], {}), "('ff', [self.light, self.motionSensor])\n", (6777, 6816), False, 'from zone_api.core.zone import Zone\n'), ((7107, 7150), 'zone_api.core.zone.Zone', 'Zone', (['"""ff"""', '[self.light, self.motionSensor]'], {}), "('ff', [self.light, self.motionSensor])\n", (7111, 7150), False, 'from zone_api.core.zone import Zone\n'), ((735, 773), 'zone_api.platform_encapsulator.create_switch_item', 'pe.create_switch_item', (['"""TestLightName"""'], {}), "('TestLightName')\n", (756, 773), True, 'from zone_api import platform_encapsulator as pe\n'), ((792, 837), 'zone_api.platform_encapsulator.create_switch_item', 'pe.create_switch_item', (['"""TestMotionSensorName"""'], {}), "('TestMotionSensorName')\n", (813, 837), True, 'from zone_api import platform_encapsulator as pe\n'), ((856, 902), 'zone_api.platform_encapsulator.create_number_item', 'pe.create_number_item', (['"""IlluminanceSensorName"""'], {}), "('IlluminanceSensorName')\n", (877, 902), True, 'from zone_api import platform_encapsulator as pe\n'), ((921, 961), 'zone_api.platform_encapsulator.create_string_item', 'pe.create_string_item', (['"""AstroSensorName"""'], {}), "('AstroSensorName')\n", (942, 961), True, 'from zone_api import platform_encapsulator as pe\n'), ((980, 1019), 'zone_api.platform_encapsulator.create_dimmer_item', 'pe.create_dimmer_item', (['"""TestDimmerName"""'], {}), "('TestDimmerName')\n", (1001, 1019), True, 'from zone_api import platform_encapsulator as pe\n'), ((1038, 1074), 'zone_api.platform_encapsulator.create_switch_item', 'pe.create_switch_item', (['"""TestFanName"""'], {}), "('TestFanName')\n", (1059, 1074), True, 'from zone_api import platform_encapsulator as pe\n'), ((5347, 5361), 'zone_api.core.actions.turn_on_switch.TurnOnSwitch', 'TurnOnSwitch', ([], {}), '()\n', (5359, 5361), False, 'from zone_api.core.actions.turn_on_switch import TurnOnSwitch\n'), ((3249, 3259), 'zone_api.core.zone.Zone', 'Zone', (['"""ff"""'], {}), "('ff')\n", (3253, 3259), False, 'from zone_api.core.zone import Zone\n'), ((3299, 3309), 'zone_api.core.zone.Zone', 'Zone', (['"""sf"""'], {}), "('sf')\n", (3303, 3309), False, 'from zone_api.core.zone import Zone\n'), ((3717, 3727), 'zone_api.core.zone.Zone', 'Zone', (['"""ff"""'], {}), "('ff')\n", (3721, 3727), False, 'from zone_api.core.zone import Zone\n'), ((3993, 4003), 'zone_api.core.zone.Zone', 'Zone', (['"""ff"""'], {}), "('ff')\n", (3997, 4003), False, 'from zone_api.core.zone import Zone\n'), ((4043, 4053), 'zone_api.core.zone.Zone', 'Zone', (['"""sf"""'], {}), "('sf')\n", (4047, 4053), False, 'from zone_api.core.zone import Zone\n'), ((4572, 4597), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ([], {}), '()\n', (4595, 4597), True, 'from zone_api import platform_encapsulator as pe\n'), ((4618, 4658), 'zone_api.platform_encapsulator.create_string_item', 'pe.create_string_item', (['INVALID_ITEM_NAME'], {}), '(INVALID_ITEM_NAME)\n', (4639, 4658), True, 'from zone_api import platform_encapsulator as pe\n'), ((4934, 4959), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ([], {}), '()\n', (4957, 4959), True, 'from zone_api import platform_encapsulator as pe\n'), ((4980, 5020), 'zone_api.platform_encapsulator.create_string_item', 'pe.create_string_item', (['INVALID_ITEM_NAME'], {}), '(INVALID_ITEM_NAME)\n', (5001, 5020), True, 'from zone_api import platform_encapsulator as pe\n'), ((5498, 5523), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ([], {}), '()\n', (5521, 5523), True, 'from zone_api import platform_encapsulator as pe\n'), ((5723, 5748), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ([], {}), '()\n', (5746, 5748), True, 'from zone_api import platform_encapsulator as pe\n'), ((5762, 5802), 'zone_api.platform_encapsulator.create_string_item', 'pe.create_string_item', (['INVALID_ITEM_NAME'], {}), '(INVALID_ITEM_NAME)\n', (5783, 5802), True, 'from zone_api import platform_encapsulator as pe\n'), ((6059, 6084), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ([], {}), '()\n', (6082, 6084), True, 'from zone_api import platform_encapsulator as pe\n'), ((6098, 6138), 'zone_api.platform_encapsulator.create_string_item', 'pe.create_string_item', (['INVALID_ITEM_NAME'], {}), '(INVALID_ITEM_NAME)\n', (6119, 6138), True, 'from zone_api import platform_encapsulator as pe\n'), ((6390, 6415), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ([], {}), '()\n', (6413, 6415), True, 'from zone_api import platform_encapsulator as pe\n'), ((6603, 6628), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ([], {}), '()\n', (6626, 6628), True, 'from zone_api import platform_encapsulator as pe\n'), ((6642, 6682), 'zone_api.platform_encapsulator.create_string_item', 'pe.create_string_item', (['INVALID_ITEM_NAME'], {}), '(INVALID_ITEM_NAME)\n', (6663, 6682), True, 'from zone_api import platform_encapsulator as pe\n'), ((6941, 6966), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ([], {}), '()\n', (6964, 6966), True, 'from zone_api import platform_encapsulator as pe\n'), ((6980, 7020), 'zone_api.platform_encapsulator.create_string_item', 'pe.create_string_item', (['INVALID_ITEM_NAME'], {}), '(INVALID_ITEM_NAME)\n', (7001, 7020), True, 'from zone_api import platform_encapsulator as pe\n'), ((7274, 7299), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ([], {}), '()\n', (7297, 7299), True, 'from zone_api import platform_encapsulator as pe\n')]