# Copyright 2015 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This module implements update checking and notification to the user.

It provides a context manager around the cache file that stores information
about the last update check.  The general process is as follows:

1) This stores the last time an update check occurred, so the check will only
   be done if the update check frequency has expired.
2) When an update check is done, all notifications in the latest snapshot are
   queried to see if their condition matches the current state of the SDK.  Any
   notifications that match are "activated" and cached.
3) Every time a command is run, Notify() is called to notify the user of
   available updates.  It loops over the activated notifications and determines
   if any of the triggers match the current command invocation.  If there is a
   match, the notification is printed and the last nag time is recorded for that
   particular notification.  At most one notification is printed per command.
   The priority is determined by the order the notifications are registered
   in the component snapshot.
"""

import json
import os
import time

from googlecloudsdk.core import config
from googlecloudsdk.core import log
from googlecloudsdk.core.updater import schemas


class UpdateCheckData(object):
  """A class to hold update checking data and to perform notifications."""

  UPDATE_CHECK_FREQUENCY_IN_SECONDS = 86400  # Once a day.

  def __init__(self):
    self._last_update_check_file = config.Paths().update_check_cache_path
    self._dirty = False
    self._data = self._LoadData()

  def _LoadData(self):
    """Deserializes data from the json file."""
    if not os.path.isfile(self._last_update_check_file):
      return schemas.LastUpdateCheck.FromDictionary({})

    with open(self._last_update_check_file) as fp:
      try:
        data = json.loads(fp.read())
        return schemas.LastUpdateCheck.FromDictionary(data)
      except ValueError:
        log.debug('Failed to parse update check cache file.  Using empty '
                  'cache instead.')
        return schemas.LastUpdateCheck.FromDictionary({})

  def _SaveData(self):
    """Serializes data to the json file."""
    if not self._dirty:
      return
    with open(self._last_update_check_file, 'w') as fp:
      fp.write(json.dumps(self._data.ToDictionary()))
    self._dirty = False

  def __enter__(self):
    return self

  def __exit__(self, *args):
    self._SaveData()

  def LastUpdateCheckRevision(self):
    """Gets the revision of the snapshot from the last update check.

    Returns:
      long, The revision of the last checked snapshot.  This is a long int but
        formatted as an actual date in seconds (i.e 20151009132504). It is *NOT*
        seconds since the epoch.
    """
    return self._data.last_update_check_revision

  def LastUpdateCheckTime(self):
    """Gets the time of the last update check as seconds since the epoch.

    Returns:
      int, The time of the last update check in seconds since the epoch.
    """
    return self._data.last_update_check_time

  def SecondsSinceLastUpdateCheck(self):
    """Gets the number of seconds since we last did an update check.

    Returns:
      int, The amount of time in seconds.
    """
    return time.time() - self._data.last_update_check_time

  def ShouldDoUpdateCheck(self):
    """Checks if it is time to do an update check.

    Returns:
      True, if enough time has elapsed and we should perform another update
      check.  False otherwise.