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()))

[((37, 33, 37, 78), 'zone_api.core.devices.illuminance_sensor.IlluminanceSensor', 'IlluminanceSensor', ({(37, 51, 37, 77): 'self.illuminanceSensorItem'}, {}), '(self.illuminanceSensorItem)', False, 'from zone_api.core.devices.illuminance_sensor import IlluminanceSensor\n'), ((38, 21, 39, 56), 'zone_api.core.devices.switch.Light', 'Light', ({(38, 27, 38, 41): 'self.lightItem', (38, 43, 38, 44): '2', (39, 27, 39, 55): 'ILLUMINANCE_THRESHOLD_IN_LUX'}, {}), '(self.lightItem, 2, ILLUMINANCE_THRESHOLD_IN_LUX)', False, 'from zone_api.core.devices.switch import Fan, Light, Switch\n'), ((40, 28, 40, 63), 'zone_api.core.devices.motion_sensor.MotionSensor', 'MotionSensor', ({(40, 41, 40, 62): 'self.motionSensorItem'}, {}), '(self.motionSensorItem)', False, 'from zone_api.core.devices.motion_sensor import MotionSensor\n'), ((41, 22, 41, 64), 'zone_api.core.devices.dimmer.Dimmer', 'Dimmer', ({(41, 29, 41, 44): 'self.dimmerItem', (41, 46, 41, 47): '2', (41, 49, 41, 52): '100', (41, 54, 41, 63): '"""0-23:59"""'}, {}), "(self.dimmerItem, 2, 100, '0-23:59')", False, 'from zone_api.core.devices.dimmer import Dimmer\n'), ((42, 19, 42, 39), 'zone_api.core.devices.switch.Fan', 'Fan', ({(42, 23, 42, 35): 'self.fanItem', (42, 37, 42, 38): '2'}, {}), '(self.fanItem, 2)', False, 'from zone_api.core.devices.switch import Fan, Light, Switch\n'), ((44, 18, 44, 31), 'zone_api.core.zone_manager.ZoneManager', 'ZoneManager', ({}, {}), '()', False, 'from zone_api.core.zone_manager import ZoneManager\n'), ((55, 16, 55, 26), 'zone_api.core.zone.Zone', 'Zone', ({(55, 21, 55, 25): '"""ff"""'}, {}), "('ff')", False, 'from zone_api.core.zone import Zone\n'), ((59, 16, 59, 26), 'zone_api.core.zone.Zone', 'Zone', ({(59, 21, 59, 25): '"""2f"""'}, {}), "('2f')", False, 'from zone_api.core.zone import Zone\n'), ((64, 16, 64, 26), 'zone_api.core.zone.Zone', 'Zone', ({(64, 21, 64, 25): '"""ff"""'}, {}), "('ff')", False, 'from zone_api.core.zone import Zone\n'), ((67, 16, 67, 26), 'zone_api.core.zone.Zone', 'Zone', ({(67, 21, 67, 25): '"""2f"""'}, {}), "('2f')", False, 'from zone_api.core.zone import Zone\n'), ((79, 16, 79, 26), 'zone_api.core.zone.Zone', 'Zone', ({(79, 21, 79, 25): '"""ff"""'}, {}), "('ff')", False, 'from zone_api.core.zone import Zone\n'), ((82, 16, 82, 26), 'zone_api.core.zone.Zone', 'Zone', ({(82, 21, 82, 25): '"""2f"""'}, {}), "('2f')", False, 'from zone_api.core.zone import Zone\n'), ((129, 15, 129, 58), 'zone_api.core.zone.Zone', 'Zone', ({(129, 20, 129, 24): '"""ff"""', (129, 26, 129, 57): '[self.light, self.motionSensor]'}, {}), "('ff', [self.light, self.motionSensor])", False, 'from zone_api.core.zone import Zone\n'), ((137, 8, 137, 89), 'zone_api.platform_encapsulator.set_number_value', 'pe.set_number_value', ({(137, 28, 137, 54): 'self.illuminanceSensorItem', (137, 56, 137, 88): '(ILLUMINANCE_THRESHOLD_IN_LUX - 1)'}, {}), '(self.illuminanceSensorItem, \n    ILLUMINANCE_THRESHOLD_IN_LUX - 1)', True, 'from zone_api import platform_encapsulator as pe\n'), ((139, 15, 139, 82), 'zone_api.core.zone.Zone', 'Zone', ({(139, 20, 139, 24): '"""ff"""', (139, 26, 139, 81): '[self.light, self.motionSensor, self.illuminanceSensor]'}, {}), "('ff', [self.light, self.motionSensor, self.illuminanceSensor])", False, 'from zone_api.core.zone import Zone\n'), ((151, 15, 151, 58), 'zone_api.core.zone.Zone', 'Zone', ({(151, 20, 151, 24): '"""ff"""', (151, 26, 151, 57): '[self.light, self.motionSensor]'}, {}), "('ff', [self.light, self.motionSensor])", False, 'from zone_api.core.zone import Zone\n'), ((158, 15, 158, 58), 'zone_api.core.zone.Zone', 'Zone', ({(158, 20, 158, 24): '"""ff"""', (158, 26, 158, 57): '[self.light, self.motionSensor]'}, {}), "('ff', [self.light, self.motionSensor])", False, 'from zone_api.core.zone import Zone\n'), ((169, 15, 169, 58), 'zone_api.core.zone.Zone', 'Zone', ({(169, 20, 169, 24): '"""ff"""', (169, 26, 169, 57): '[self.light, self.motionSensor]'}, {}), "('ff', [self.light, self.motionSensor])", False, 'from zone_api.core.zone import Zone\n'), ((176, 15, 176, 58), 'zone_api.core.zone.Zone', 'Zone', ({(176, 20, 176, 24): '"""ff"""', (176, 26, 176, 57): '[self.light, self.motionSensor]'}, {}), "('ff', [self.light, self.motionSensor])", False, 'from zone_api.core.zone import Zone\n'), ((22, 17, 22, 55), 'zone_api.platform_encapsulator.create_switch_item', 'pe.create_switch_item', ({(22, 39, 22, 54): '"""TestLightName"""'}, {}), "('TestLightName')", True, 'from zone_api import platform_encapsulator as pe\n'), ((23, 17, 23, 62), 'zone_api.platform_encapsulator.create_switch_item', 'pe.create_switch_item', ({(23, 39, 23, 61): '"""TestMotionSensorName"""'}, {}), "('TestMotionSensorName')", True, 'from zone_api import platform_encapsulator as pe\n'), ((24, 17, 24, 63), 'zone_api.platform_encapsulator.create_number_item', 'pe.create_number_item', ({(24, 39, 24, 62): '"""IlluminanceSensorName"""'}, {}), "('IlluminanceSensorName')", True, 'from zone_api import platform_encapsulator as pe\n'), ((25, 17, 25, 57), 'zone_api.platform_encapsulator.create_string_item', 'pe.create_string_item', ({(25, 39, 25, 56): '"""AstroSensorName"""'}, {}), "('AstroSensorName')", True, 'from zone_api import platform_encapsulator as pe\n'), ((26, 17, 26, 56), 'zone_api.platform_encapsulator.create_dimmer_item', 'pe.create_dimmer_item', ({(26, 39, 26, 55): '"""TestDimmerName"""'}, {}), "('TestDimmerName')", True, 'from zone_api import platform_encapsulator as pe\n'), ((27, 17, 27, 53), 'zone_api.platform_encapsulator.create_switch_item', 'pe.create_switch_item', ({(27, 39, 27, 52): '"""TestFanName"""'}, {}), "('TestFanName')", True, 'from zone_api import platform_encapsulator as pe\n'), ((140, 31, 140, 45), 'zone_api.core.actions.turn_on_switch.TurnOnSwitch', 'TurnOnSwitch', ({}, {}), '()', False, 'from zone_api.core.actions.turn_on_switch import TurnOnSwitch\n'), ((94, 16, 94, 26), 'zone_api.core.zone.Zone', 'Zone', ({(94, 21, 94, 25): '"""ff"""'}, {}), "('ff')", False, 'from zone_api.core.zone import Zone\n'), ((95, 16, 95, 26), 'zone_api.core.zone.Zone', 'Zone', ({(95, 21, 95, 25): '"""sf"""'}, {}), "('sf')", False, 'from zone_api.core.zone import Zone\n'), ((105, 16, 105, 26), 'zone_api.core.zone.Zone', 'Zone', ({(105, 21, 105, 25): '"""ff"""'}, {}), "('ff')", False, 'from zone_api.core.zone import Zone\n'), ((112, 16, 112, 26), 'zone_api.core.zone.Zone', 'Zone', ({(112, 21, 112, 25): '"""ff"""'}, {}), "('ff')", False, 'from zone_api.core.zone import Zone\n'), ((113, 16, 113, 26), 'zone_api.core.zone.Zone', 'Zone', ({(113, 21, 113, 25): '"""sf"""'}, {}), "('sf')", False, 'from zone_api.core.zone import Zone\n'), ((126, 30, 126, 55), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ({}, {}), '()', True, 'from zone_api import platform_encapsulator as pe\n'), ((126, 76, 126, 116), 'zone_api.platform_encapsulator.create_string_item', 'pe.create_string_item', ({(126, 98, 126, 115): 'INVALID_ITEM_NAME'}, {}), '(INVALID_ITEM_NAME)', True, 'from zone_api import platform_encapsulator as pe\n'), ((133, 30, 133, 55), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ({}, {}), '()', True, 'from zone_api import platform_encapsulator as pe\n'), ((133, 76, 133, 116), 'zone_api.platform_encapsulator.create_string_item', 'pe.create_string_item', ({(133, 98, 133, 115): 'INVALID_ITEM_NAME'}, {}), '(INVALID_ITEM_NAME)', True, 'from zone_api import platform_encapsulator as pe\n'), ((144, 30, 144, 55), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ({}, {}), '()', True, 'from zone_api import platform_encapsulator as pe\n'), ((148, 12, 148, 37), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ({}, {}), '()', True, 'from zone_api import platform_encapsulator as pe\n'), ((148, 51, 148, 91), 'zone_api.platform_encapsulator.create_string_item', 'pe.create_string_item', ({(148, 73, 148, 90): 'INVALID_ITEM_NAME'}, {}), '(INVALID_ITEM_NAME)', True, 'from zone_api import platform_encapsulator as pe\n'), ((155, 12, 155, 37), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ({}, {}), '()', True, 'from zone_api import platform_encapsulator as pe\n'), ((155, 51, 155, 91), 'zone_api.platform_encapsulator.create_string_item', 'pe.create_string_item', ({(155, 73, 155, 90): 'INVALID_ITEM_NAME'}, {}), '(INVALID_ITEM_NAME)', True, 'from zone_api import platform_encapsulator as pe\n'), ((162, 12, 162, 37), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ({}, {}), '()', True, 'from zone_api import platform_encapsulator as pe\n'), ((166, 12, 166, 37), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ({}, {}), '()', True, 'from zone_api import platform_encapsulator as pe\n'), ((166, 51, 166, 91), 'zone_api.platform_encapsulator.create_string_item', 'pe.create_string_item', ({(166, 73, 166, 90): 'INVALID_ITEM_NAME'}, {}), '(INVALID_ITEM_NAME)', True, 'from zone_api import platform_encapsulator as pe\n'), ((173, 12, 173, 37), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ({}, {}), '()', True, 'from zone_api import platform_encapsulator as pe\n'), ((173, 51, 173, 91), 'zone_api.platform_encapsulator.create_string_item', 'pe.create_string_item', ({(173, 73, 173, 90): 'INVALID_ITEM_NAME'}, {}), '(INVALID_ITEM_NAME)', True, 'from zone_api import platform_encapsulator as pe\n'), ((180, 12, 180, 37), 'zone_api.platform_encapsulator.get_event_dispatcher', 'pe.get_event_dispatcher', ({}, {}), '()', True, 'from zone_api import platform_encapsulator as pe\n')]