# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from marionette import expected
from marionette.by import By
from marionette.errors import NoSuchElementException
from marionette.errors import StaleElementException
from marionette.marionette import Actions
from marionette.wait import Wait
from gaiatest.apps.base import Base
from gaiatest.apps.base import PageRegion
from gaiatest.apps.calendar.regions.event_details import EventDetails


class Calendar(Base):

    name = 'Calendar'

    _current_month_year_locator = (By.ID, 'current-month-year')
    _current_month_day_agenda_locator = (By.ID, 'month-day-agenda')
    _current_monthly_calendar_locator = (By.ID, 'month-view')
    _add_event_button_locator = (By.XPATH, "//a[@href='/event/add/']")

    _month_display_button_locator = (By.XPATH, "//a[@href='/month/']")
    _week_display_button_locator = (By.XPATH, "//a[@href='/week/']")
    _day_display_button_locator = (By.XPATH, "//a[@href='/day/']")
    _day_view_locator = (By.ID, 'day-view')
    _week_view_locator = (By.ID, 'week-view')

    _time_views_locator = (By.ID, 'time-views')
    _modify_event_view_locator = (By.ID, 'modify-event-view')
    _event_view_locator = (By.ID, 'event-view')
    _time_header_locator = (By.ID, 'time-header')

    _create_account_button_locator = (By.CLASS_NAME, 'create-account')
    _create_account_header_locator = (By.ID, 'create-account-header')
    _create_account_view_locator = (By.ID, 'create-account-view')
    _create_account_preset_locator = (By.CSS_SELECTOR,
                                      '#create-account-presets a')

    _modify_account_header_locator = (By.ID, 'modify-account-header')
    _modify_account_view_locator = (By.ID, 'modify-account-view')

    _settings_locator = (By.ID, 'settings')

    _advanced_settings_view_locator = (By.ID, 'advanced-settings-view')
    _advanced_settings_done_button_locator = (By.XPATH, "//a[@href='/settings/']")

    _event_list_date_locator = (By.ID, 'event-list-date')
    _event_list_empty_locator = (By.ID, 'empty-message')
    _event_locator = (By.CLASS_NAME, 'event')
    _today_locator = (By.CSS_SELECTOR, '.month.active .present')
    _tomorrow_locator = (By.CSS_SELECTOR, '.month.active .present + li > .day')

    _day_view_all_day_button = (By.CSS_SELECTOR,
                                '.day-view .md__allday:nth-child(2) .md__allday-events')
    _day_view_event_link = (By.CSS_SELECTOR, '.day-view .md__day:nth-child(2) .md__event')

    def launch(self):
        Base.launch(self)
        # empty message is only displayed after first MonthsDay#render,
        # so we are sure app is "ready" after that
        Wait(self.marionette).until(expected.element_displayed(
            Wait(self.marionette).until(expected.element_present(
                *self._event_list_empty_locator))))

    @property
    def current_month_year(self):
        return self.marionette.find_element(*self._current_month_year_locator).text

    @property
    def current_month_day(self):
        return self.marionette.find_element(*self._current_month_day_agenda_locator).get_attribute('data-date')

    @property
    def event_list_date(self):
        return self.marionette.find_element(*self._event_list_date_locator).text

    @property
    def events(self):
        return [self.Event(marionette=self.marionette, element=event)
                for event in self.marionette.find_elements(*self._event_locator)]

    def wait_for_events(self, number_to_wait_for=1):
        Wait(self.marionette).until(lambda m: len(m.find_elements(*self._event_locator)) == number_to_wait_for)

    def event(self, title):
        for event in self.events:
            if event.title == title:
                return event

    @property
    def accounts(self):
        return [account for account in self.marionette.find_elements(
            *self._create_account_preset_locator)]

    def account(self, preset):
        for account in self.accounts:
            if account.get_attribute('data-provider') == preset:
                return account

    @property
    def settings(self):
        return self.Settings(marionette=self.marionette,
                             element=self.marionette.find_elements(*self._settings_locator))

    def wait_for_new_event(self):
        from gaiatest.apps.calendar.regions.event import NewEvent
        new_event = NewEvent(self.marionette)
        new_event.wait_for_panel_to_load()
        return new_event

    def a11y_click_add_event_button(self):
        self.accessibility.click(self.marionette.find_element(*self._add_event_button_locator))
        return self.wait_for_new_event()

    def tap_add_event_button(self):
        self.marionette.find_element(*self._add_event_button_locator).tap()
        return self.wait_for_new_event()

    def a11y_click_week_display_button(self):
        self.accessibility.click(self.marionette.find_element(*self._week_display_button_locator))
        Wait(self.marionette).until(expected.element_displayed(
            Wait(self.marionette).until(expected.element_present(
                *self._week_view_locator))))

    def tap_week_display_button(self):
        self.marionette.find_element(*self._week_display_button_locator).tap()
        Wait(self.marionette).until(expected.element_displayed(
            Wait(self.marionette).until(expected.element_present(
                *self._week_view_locator))))

    def a11y_click_month_display_button(self):
        self.accessibility.click(self.marionette.find_element(*self._month_display_button_locator))
        Wait(self.marionette).until(expected.element_displayed(
            Wait(self.marionette).until(expected.element_present(
                *self._current_monthly_calendar_locator))))
        Wait(self.marionette).until(expected.element_displayed(
            Wait(self.marionette).until(expected.element_present(
                *self._current_month_day_agenda_locator))))

    def a11y_click_day_display_button(self):
        self.accessibility.click(self.marionette.find_element(*self._day_display_button_locator))
        Wait(self.marionette).until(expected.element_displayed(
            Wait(self.marionette).until(expected.element_present(
                *self._day_view_locator))))

    def tap_day_display_button(self):
        self.marionette.find_element(*self._day_display_button_locator).tap()
        Wait(self.marionette).until(expected.element_displayed(
            Wait(self.marionette).until(expected.element_present(
                *self._day_view_locator))))

    def displayed_events_in_month_view(self, date_time):
        return self.marionette.find_element(*self._get_events_locator_in_month_view()).text

    def displayed_events_in_week_view(self, date_time):
        return self.marionette.find_element(*self._get_events_locator_in_week_view(date_time)).text

    def displayed_events_in_day_view(self, date_time):
        return self.marionette.find_element(*self._get_events_locator_in_day_view(date_time)).text

    def _get_events_locator_in_day_view(self, date_time):
        data_date = self._get_data_date(date_time)
        return (By.CSS_SELECTOR,
                "#day-view .md__day[data-date*='%s'] .md__event" % data_date)

    def _get_events_locator_in_month_view(self):
        return (By.CSS_SELECTOR,
                '#event-list .event')

    def _get_events_locator_in_week_view(self, date_time):
        data_date = self._get_data_date(date_time)
        return (By.CSS_SELECTOR,
                "#week-view .md__day[data-date*='%s'] .md__event" % data_date)

    @staticmethod
    def _get_data_date(date_time):
        return date_time.strftime("%b %d")

    @staticmethod
    def _get_data_hour(date_time):
        return date_time.hour

    def a11y_click_other_day(self, next, previous):
        try:
            # Try clicking tomorrow
            self.accessibility.click(self.marionette.find_element(*self._tomorrow_locator))
            return next
        except NoSuchElementException:
            # Tomorrow is next week or month, try clicking yesterday.
            yesterday = self.marionette.execute_script(
                "return arguments[0].previousSibling.querySelector('.day');",
                [self.marionette.find_element(*self._today_locator)])
            self.accessibility.click(yesterday)
            return previous

    def a11y_click_header(self, header, selector):
        self.marionette.execute_async_script(
            "Accessibility.click(arguments[0].shadowRoot.querySelector('%s'));" % selector,
            [header], special_powers=True)

    def wait_fot_settings_drawer_animation(self):
        el = self.marionette.find_element(*self.settings._settings_drawer_locator)
        Wait(self.marionette).until(lambda m: el.get_attribute('data-animstate') == 'done')

    def a11y_click_create_account_back(self):
        self.a11y_click_header(self.marionette.find_element(*self._create_account_header_locator),
                               'button.icon-back')
        Wait(self.marionette).until(expected.element_displayed(
            Wait(self.marionette).until(expected.element_present(
                *self._advanced_settings_view_locator))))

    def a11y_click_modify_account_back(self):
        self.a11y_click_header(self.marionette.find_element(*self._modify_account_header_locator),
                               'button.icon-back')
        Wait(self.marionette).until(expected.element_displayed(
            Wait(self.marionette).until(expected.element_present(
                *self._create_account_view_locator))))

    def a11y_click_settings(self):
        self.a11y_click_header(self.marionette.find_element(*self._time_header_locator),
                               'button.icon-menu')
        self.wait_fot_settings_drawer_animation()

    def a11y_click_close_settings(self):
        self.a11y_click_header(self.marionette.find_element(
            *self.settings._settings_header_locator), 'button.icon-menu')
        self.wait_fot_settings_drawer_animation()

    def a11y_create_event(self, title):
        new_event = self.a11y_click_add_event_button()
        # create a new event
        new_event.a11y_fill_event_title(title)
        new_event.a11y_click_save_event()
        self.wait_for_events(1)

    def a11y_click_day_view_event(self):
        self.accessibility.click(self.marionette.find_element(*self._day_view_event_link))
        el = self.marionette.find_element(*self._event_view_locator)
        Wait(self.marionette).until(expected.element_displayed(el))
        return EventDetails(self.marionette)

    def flick_to_next_month(self):
        self._flick_to_month('next')

    def flick_to_previous_month(self):
        self._flick_to_month('previous')

    def a11y_wheel_to_next_month(self):
        self._a11y_wheel_to_month('left')

    def a11y_wheel_to_previous_month(self):
        self._a11y_wheel_to_month('right')

    def _a11y_wheel_to_month(self, direction):
        self.accessibility.wheel(self.marionette.find_element(
            *self._current_monthly_calendar_locator), direction)

    def _flick_to_month(self, direction):
        """Flick current monthly calendar to next or previous month.

        @param direction: flick to next month if direction='next', else flick to previous month
        """
        action = Actions(self.marionette)

        month = self.marionette.find_element(
            *self._current_monthly_calendar_locator)
        month_year = self.current_month_year

        x_start = (month.size['width'] / 100) * (direction == 'next' and 90 or 10)
        x_end = (month.size['width'] / 100) * (direction == 'next' and 10 or 90)
        y_start = month.size['height'] / 4
        y_end = month.size['height'] / 4

        action.flick(month, x_start, y_start, x_end, y_end, 200).perform()

        Wait(self.marionette).until(lambda m: self.current_month_year != month_year)

    class Settings(PageRegion):

        _calendar_local_locator = (By.CSS_SELECTOR, '#calendar-local-first .pack-checkbox')
        _calendar_local_checkbox_locator = (By.CSS_SELECTOR,
                                            '#calendar-local-first input[type="checkbox"]')
        _advanced_settings_button_locator = (By.CSS_SELECTOR, '.settings')
        _settings_header_locator = (By.ID, 'settings-header')
        _settings_drawer_locator = (By.CLASS_NAME, 'settings-drawer')

        def wait_for_calendar_unchecked(self, timeout=None):
            checkbox = self.marionette.find_element(*self._calendar_local_checkbox_locator)
            Wait(self.marionette, timeout).until(expected.element_not_selected(checkbox))

        def wait_for_a11y_calendar_unchecked(self, timeout=None):
            Wait(self.marionette, timeout, ignored_exceptions=StaleElementException).until(
                lambda m: not self.marionette.find_element(
                    *self._calendar_local_locator).get_attribute('aria-selected'))

    class Event(PageRegion):

        _title_locator = (By.TAG_NAME, 'h5')
        _location_locator = (By.CLASS_NAME, 'location')
        _event_view_locator = (By.ID, 'event-view')

        @property
        def title(self):
            return self.root_element.find_element(*self._title_locator).text

        @property
        def location(self):
            return self.root_element.find_element(*self._location_locator).text

        def a11y_click(self):
            self.accessibility.click(self.root_element)
            el = self.marionette.find_element(*self._event_view_locator)
            Wait(self.marionette).until(expected.element_displayed(el))
            return EventDetails(self.marionette)
