#!/usr/bin/python
#
# Copyright 2010 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.

"""Interface for accessing all other services."""

__author__ = 'api.sgrinberg@gmail.com (Stan Grinberg)'

import datetime
import os
import pickle
import warnings
import sys

from adspygoogle.common import ETREE, PYXML
from adspygoogle.common import SanityCheck
from adspygoogle.common import Utils
from adspygoogle.common.Errors import ValidationError


DEFAULT_XML_PARSER = ETREE
if sys.version_info < (2, 7):
    DEFAULT_XML_PARSER = PYXML

# The values in _DEFAULT_CONFIG will be used to populate a user's configuration
# if any of these keys was not provided.
_DEFAULT_CONFIG = {
    'proxy': None,
    'xml_parser': DEFAULT_XML_PARSER,
    'debug': 'n',
    'raw_debug': 'n',
    'xml_log': 'y',
    'request_log': 'y',
    'raw_response': 'n',
    'strict': 'y',
    'auth_token_epoch': 0,
    'auth_type': '',
    'pretty_xml': 'y',
    'compress': 'y',
    'access': '',
    'wrap_in_tuple': 'y'
}

# The _OAUTH_2_AUTH_KEYS are the keys in the authentication dictionary that are
# used to construct an OAuth 2.0 credential.
_OAUTH_2_AUTH_KEYS = set(['clientId', 'clientSecret', 'refreshToken'])
# The web address for generating OAuth 2.0 credentials at Google.
_GOOGLE_OAUTH2_ENDPOINT = 'https://accounts.google.com/o/oauth2/token'


class Client(object):

  """Provides entry point to all web services.

  Allows instantiation of all web services.
  """

  home = os.getcwd()
  auth_pkl = ''
  config_pkl = ''

  def __init__(self, headers=None, config=None, path=None):
    """Inits Client.

    Args:
      [optional]
      headers: dict Object with populated authentication credentials.
      config: dict Object with client configuration values.
      path: str Relative or absolute path to home directory (i.e. location of
            pickles and logs/).
    """
    self._headers = headers or {}
    self._config = config or self._SetMissingDefaultConfigValues()

  def _LoadAuthCredentials(self):
    """Load existing authentication credentials from auth.pkl.

    Returns:
      dict Dictionary object with populated authentication credentials.

    Raises:
      ValidationError: if authentication data is missing.
    """
    auth = {}
    if os.path.exists(self.__class__.auth_pkl):
      fh = open(self.__class__.auth_pkl, 'r')
      try:
        auth = pickle.load(fh)
      finally:
        fh.close()

    if not auth:
      msg = 'Authentication data is missing.'
      raise ValidationError(msg)

    if _OAUTH_2_AUTH_KEYS.issubset(set(auth.keys())):
      from oauth2client.client import OAuth2Credentials
      auth['oauth2credentials'] = OAuth2Credentials(
          None, auth['clientId'], auth['clientSecret'], auth['refreshToken'],
          datetime.datetime(1980, 1, 1, 12), _GOOGLE_OAUTH2_ENDPOINT,
          'Google Ads* Python Client Library')
      for auth_key in _OAUTH_2_AUTH_KEYS:
        del auth[auth_key]

    return auth

  def _WriteUpdatedAuthValue(self, key, new_value):
    """Write updated authentication value for a key in auth.pkl.

    Args:
      key: str Key to update.
      new_value: str New value to update the key with.
    """
    auth = self._LoadAuthCredentials()
    auth[key] = new_value

    # Only write to an existing pickle.
    if os.path.exists(self.__class__.auth_pkl):
      fh = open(self.__class__.auth_pkl, 'w')
      try:
        pickle.dump(auth, fh)
      finally:
        fh.close()

  def _LoadConfigValues(self):
    """Load existing configuration values from config.pkl.

    Returns:
      dict Dictionary object with populated configuration values.
    """
    config = {}
    if os.path.exists(self.__class__.config_pkl):
      fh = open(self.__class__.config_pkl, 'r')
      try:
        config = pickle.load(fh)
      finally:
        fh.close()

    if not config:
      # Proceed to set default config values.
      pass
    return config

  def _SetMissingDefaultConfigValues(self, config=None):
    """Set default configuration values for missing elements in the config dict.

    Args:
      config: dict Object with client configuration values.

    Returns:
      dict Given config dictionary with default values added in.
    """
    if config is None: config = {}
    for key in _DEFAULT_CONFIG:
      if key not in config:
        config[key] = _DEFAULT_CONFIG[key]
    return config

  def GetAuthCredentials(self):
    """Return authentication credentials.

    Returns:
      dict Authentiaction credentials.
    """
    return self._headers

  def GetConfigValues(self):
    """Return configuration values.

    Returns:
      dict Configuration values.
    """
    return self._config

  def SetDebug(self, new_state):
    """Temporarily change debug mode for a given Client instance.

    Args:
      new_state: bool New state of the debug mode.
    """
    self._config['debug'] = Utils.BoolTypeConvert(new_state, str)

  def __GetDebug(self):
    """Return current state of the debug mode.

    Returns: