#!/usr/bin/env python
#
# Copyright 2007 Google Inc.
#
# 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.
#
"""A base class for all Admin UI request handlers and related utilities."""
 
 
import os.path
import random
import string
import urllib
 
import google
import jinja2
import webapp2
 
from google.appengine.tools import sdk_update_checker
 
 
def _urlencode_filter(value):
  if isinstance(value, basestring):
    return urllib.quote(value)
  else:
    return urllib.urlencode(value)
 
 
def _byte_size_format(value):
  byte_count = int(value)
  if byte_count == 1:
    return '1 Byte'
  elif byte_count < 1024:
    return '%d Bytes' % byte_count
  elif byte_count < 1024 ** 2:
    return '%.1f KiB (%d Bytes)' % (byte_count/1024.0, byte_count)
  elif byte_count < 1024 ** 3:
    return '%.1f MiB (%d Bytes)' % (byte_count/1024.0 ** 2, byte_count)
  else:
    return '%.1f GiB (%d Bytes)' % (byte_count/1024.0 ** 3, byte_count)
 
 
TEMPLATE_PATH = os.path.abspath(
    os.path.join(os.path.dirname(__file__), 'templates'))
admin_template_environment = jinja2.Environment(
    loader=jinja2.FileSystemLoader(TEMPLATE_PATH),
    autoescape=True)
admin_template_environment.filters['urlencode'] = _urlencode_filter
admin_template_environment.filters['bytesizeformat'] = _byte_size_format
 
_DEFAULT_SDK_VERSION = '(Internal)'
 
 
def _get_sdk_version():
  version_object = sdk_update_checker.GetVersionObject()
  if version_object:
    return version_object['release']
  else:
    return _DEFAULT_SDK_VERSION
 
 
class AdminRequestHandler(webapp2.RequestHandler):
  """Base class for all admin UI request handlers."""
 
  _SDK_VERSION = _get_sdk_version()
 
  @classmethod
  def init_xsrf(cls, xsrf_path):
    """Load the XSRF token from the given path."""
    if os.path.exists(xsrf_path):
      with open(xsrf_path, 'r') as token_file:
        cls.xsrf_token = token_file.read().strip()
    else:
      cls.xsrf_token = ''.join(random.choice(string.ascii_letters)
                               for _ in range(10))
      with open(xsrf_path, 'w') as token_file:
        token_file.write(cls.xsrf_token)
 
  def dispatch(self):
    if self.request.method in ['PATCH', 'POST', 'PUT', 'DELETE'] and (
        self.request.get('xsrf_token') != self.xsrf_token):
      self.response.set_status(403, 'Invalid XSRF token')
      self.response.out.write('<h1>Invalid XSRF token</h1>')
    else:
      super(AdminRequestHandler, self).dispatch()
 
  def render(self, template, context):
    """Returns a rendered version of the given jinja2 template.
 
    Args:
      template: The file name of the template file to use e.g.
          "memcache_viewer.html".
      context: A dict of values to use when rendering the template.
 
    Returns:
      A Unicode object containing the rendered template.
    """
    template = admin_template_environment.get_template(template)
 
    values = {
        'app_id': self.configuration.app_id,
        'request': self.request,
        'sdk_version': self._SDK_VERSION,
        'xsrf_token': self.xsrf_token,
      }
    values.update(context)
    return template.render(values)
 
  def _construct_url(self, remove=None, add=None):
    """Returns a URL referencing the current resource with the same params.
 
    For example, if the request URL is
    "http://foo/bar?animal=cat&color=redirect" then
    _construct_url(['animal'], {'vehicle': 'car'}) will return
    "http://foo/bar?vehicle=car&color=redirect"
 
    Args:
      remove: A sequence of query parameters to remove from the query string.
      add: A mapping of query parameters to add to the query string.
 
    Returns:
      A new query string suitable for use in "GET" requests.
    """
    remove = remove or []
    add = add or {}
    params = dict(self.request.params)
    for arg in remove:
      if arg in params:
        del params[arg]
 
    params.update(add)
    return str('%s?%s' % (self.request.path,
                          urllib.urlencode(sorted(params.iteritems()))))
 
  @property
  def dispatcher(self):
    return self.request.app.dispatcher
 
  @property
  def configuration(self):
    return self.request.app.configuration