# Datetime configuration spoke class
#
# Copyright (C) 2012-2013 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
# the GNU General Public License v.2, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY expressed or implied, including the implied warranties of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
# Public License for more details.  You should have received a copy of the
# GNU General Public License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
# source code or documentation are not subject to the GNU General Public
# License and may only be used or replicated with the express permission of
# Red Hat, Inc.
#
import datetime
import re
import time
import locale as locale_mod
import functools
import copy

from pyanaconda import isys
from pyanaconda import ntp
from pyanaconda import flags
from pyanaconda.anaconda_loggers import get_module_logger
from pyanaconda.core import util, constants
from pyanaconda.core.async_utils import async_action_wait, async_action_nowait
from pyanaconda.core.configuration.anaconda import conf
from pyanaconda.core.i18n import _, CN_
from pyanaconda.core.timer import Timer
from pyanaconda.localization import get_xlated_timezone, resolve_date_format
from pyanaconda.modules.common.structures.timezone import TimeSourceData
from pyanaconda.modules.common.constants.services import TIMEZONE, NETWORK
from pyanaconda.modules.common.util import is_module_available
from pyanaconda.ntp import NTPServerStatusCache
from pyanaconda.ui.communication import hubQ
from pyanaconda.ui.common import FirstbootSpokeMixIn
from pyanaconda.ui.gui.spokes import NormalSpoke
from pyanaconda.ui.categories.localization import LocalizationCategory
from pyanaconda.ui.gui.utils import blockedHandler
from pyanaconda.ui.gui.spokes.lib.ntp_dialog import NTPConfigDialog
from pyanaconda.timezone import NTP_SERVICE, get_all_regions_and_timezones, get_timezone, is_valid_timezone
from pyanaconda.threading import threadMgr, AnacondaThread

import gi
gi.require_version("Gdk", "3.0")
gi.require_version("Gtk", "3.0")
gi.require_version("TimezoneMap", "1.0")

from gi.repository import Gdk, Gtk, TimezoneMap

log = get_module_logger(__name__)

__all__ = ["DatetimeSpoke"]

DEFAULT_TZ = "America/New_York"

SPLIT_NUMBER_SUFFIX_RE = re.compile(r'([^0-9]*)([-+])([0-9]+)')


def _compare_regions(reg_xlated1, reg_xlated2):
    """Compare two pairs of regions and their translations."""

    reg1, xlated1 = reg_xlated1
    reg2, xlated2 = reg_xlated2

    # sort the Etc timezones to the end
    if reg1 == "Etc" and reg2 == "Etc":
        return 0
    elif reg1 == "Etc":
        return 1
    elif reg2 == "Etc":
        return -1
    else:
        # otherwise compare the translated names
        return locale_mod.strcoll(xlated1, xlated2)


def _compare_cities(city_xlated1, city_xlated2):
    """Compare two paris of cities and their translations."""

    # if there are "cities" ending with numbers (like GMT+-X), we need to sort
    # them based on their numbers
    val1 = city_xlated1[1]
    val2 = city_xlated2[1]

    match1 = SPLIT_NUMBER_SUFFIX_RE.match(val1)
    match2 = SPLIT_NUMBER_SUFFIX_RE.match(val2)

    if match1 is None and match2 is None:
        # no +-X suffix, just compare the strings
        return locale_mod.strcoll(val1, val2)

    if match1 is None or match2 is None:
        # one with the +-X suffix, compare the prefixes
        if match1:
            prefix, _sign, _suffix = match1.groups()
            return locale_mod.strcoll(prefix, val2)
        else:
            prefix, _sign, _suffix = match2.groups()
            return locale_mod.strcoll(val1, prefix)

    # both have the +-X suffix
    prefix1, sign1, suffix1 = match1.groups()
    prefix2, sign2, suffix2 = match2.groups()

    if prefix1 == prefix2:
        # same prefixes, let signs determine

        def _cmp(a, b):
            if a < b:
                return -1
            elif a > b:
                return 1
            else:
                return 0

        return _cmp(int(sign1 + suffix1), int(sign2 + suffix2))
 
   else:
        # compare prefixes
        return locale_mod.strcoll(prefix1, prefix2)


def _new_date_field_box(store):
    """
    Creates new date field box (a combobox and a label in a horizontal box) for
    a given store.

    """

    box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
    suffix_label = Gtk.Label()
    renderer = Gtk.CellRendererText()
    combo = Gtk.ComboBox(model=
store)
    combo.pack_start(renderer, False)

    # idx is column 0, string we want to show is 1
    combo.add_attribute(renderer, "text", 1)
    combo.set_wrap_width(1)

    box.pack_start(combo, False, False, 0)
    box.pack_start(suffix_label, False, False, 0)

    return (box, combo, suffix_label)


class DatetimeSpoke(FirstbootSpokeMixIn, NormalSpoke):
    """
       .. inheritance-diagram:: DatetimeSpoke
          :parts: 3
    """
    builderObjects = ["datetimeWindow",
                      "days", "months", "years", "regions", "cities",
                      "upImage", "upImage1", "upImage2", "downImage",
                      "downImage1", "downImage2", "downImage3", "configImage",
                      "citiesFilter", "daysFilter",
                      "cityCompletion", "regionCompletion",
                      ]

    mainWidgetName = "datetimeWindow"
    uiFile = "spokes/datetime_spoke.glade"
    category = LocalizationCategory
    icon = "preferences-system-time-symbolic"
    title = CN_("GUI|Spoke", "_Time & Date")

    # Hack to get libtimezonemap loaded for GtkBuilder
    # see https://bugzilla.gnome.org/show_bug.cgi?id=712184
    _hack = TimezoneMap.TimezoneMap()
    del(_hack)

    @staticmethod
    def get_screen_id():
        """Return a unique id of this UI screen."""
        return "date-time-configuration"

    @classmethod
    def should_run(cls, environment, data):
        """Should the spoke run?"""
        if not is_module_available(TIMEZONE):
            return False

        return FirstbootSpokeMixIn.should_run(environment, data)

    def __init__(self, *args):
        NormalSpoke.__init__(self, *args)

        # taking values from the kickstart file?
        self._kickstarted = flags.flags.automatedInstall

        self._update_datetime_timer = None
        self._start_updating_timer = None
        self._shown = False
        self._tz = None

        self._timezone_module = TIMEZONE.get_proxy()
        self._network_module = NETWORK.get_proxy()

        self._ntp_servers = []
        self._ntp_servers_states = NTPServerStatusCache()

    def initialize(self):
        NormalSpoke.initialize(self)
        self.initialize_start()
        self._daysStore = self.builder.get_object("days")
        self._monthsStore = self.builder.get_object("months")
        self._yearsStore = self.builder.get_object("years")
        self._regionsStore = self.builder.get_object("regions")
        self._citiesStore = self.builder.get_object("cities")
        self._tzmap = self.builder.get_object("tzmap")
        self._dateBox = self.builder.get_object("dateBox")

        # we need to know it the new value is the same as previous or not
        self._old_region = None
        self._old_city = None

        self._regionCombo = self.builder.get_object("regionCombobox")
        self._cityCombo = self.builder.get_object("cityCombobox")

        self._daysFilter = self.builder.get_object("daysFilter")
        self._daysFilter.set_visible_func(self.existing_date, None)

        self._citiesFilter = self.builder.get_object("citiesFilter")
        self._citiesFilter.set_visible_func(self.city_in_region, None)

        self._hoursLabel = self.builder.get_object("hoursLabel")
        self._minutesLabel = self.builder.get_object("minutesLabel")
        self._amPmUp = self.builder.get_object("amPmUpButton")