# 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.

# Note: this file is part of the sdk-ext-runtime package.  It gets copied into
# individual GAE runtime modules so that they can be easily deployed.

import json
import os
import sys


class JSONObject(object):
    """Wrapper for a JSON object.

    Presents a JSON object as a python object (where fields are attributes)
    instead of a dictionary.  Undefined attributes produce a value of None
    instead of an AttributeError.

    Note that attribute names beginning with an underscore are excluded.
    """

    def __getattr__(self, attr):
        return None

    def to_dict(self):
        result = {}
        for attr, val in self.__dict__.iteritems():
            if not attr.startswith('_'):
                result[attr] = _make_serializable(val)
        return result

    # Alias old style naming so this interoperates with gcloud's appinfo.
    ToDict = to_dict


def _make_serializable(obj):
    """Converts objects to serializable form."""
    if isinstance(obj, JSONObject):
        return obj.to_dict()
    else:
        return obj


def _write_msg(**message):
    """Write a message to standard output.

    Args:
        **message: ({str: object, ...}) A JSON message encoded in keyword
            arguments.
    """
    json.dump(message, sys.stdout, default=_make_serializable)
    sys.stdout.write('\n')
    sys.stdout.flush()


def error(message, *args):
    _write_msg(type='error', message=message % args)


def warn(message, *args):
    _write_msg(type='warn', message=message % args)


def info(message, *args):
    _write_msg(type='info', message=message % args)


def debug(message, *args):
    _write_msg(type='debug', message=message % args)


def print_status(message, *args):
    _write_msg(type='print_status', message=message % args)


def send_runtime_params(params, appinfo=None):
    """Send runtime parameters back to the controller.

    Args:
        params: ({str: object, ...}) Set of runtime parameters.  Must be
            json-encodable.
        appinfo: ({str: object, ...} or None) Contents of the app.yaml file to
            be produced by the runtime definition.  Required fields may be
            added to this by the framework, the only thing an application
            needs to provide is the "runtime" field and any additional data
            fields.
    """
    if appinfo is not None:
        _write_msg(type='runtime_parameters', runtime_data=params,
                   appinfo=appinfo)
    else:
        _write_msg(type='runtime_parameters', runtime_data=params)


def get_config():
    """Request runtime parameters from the controller.

    Returns:
      (object) The runtime parameters represented as an object.
    """
    _write_msg(type='get_config')
    return dict_to_object(json.loads(sys.stdin.readline()))


def dict_to_object(json_dict):
    """Converts a dictionary to a python object.

    Converts key-values to attribute-values.

    Args:
      json_dict: ({str: object, ...})

    Returns:
      (JSONObject)
    """
    obj = JSONObject()
    for name, val in json_dict.iteritems():
        if isinstance(val, dict):
          val = dict_to_object(val)
        setattr(obj, name, val)
    return obj


class RuntimeDefinitionRoot(object):
    """Abstraction that allows us to access files in the runtime definiton."""

    def __init__(self, path):
        self.root = path

    def read_file(self, *name):
        with open(os.path.join(self.root, *name)) as src:
            return src.read()


def gen_file(name, contents):
    """Generate the file.

    This writes the file to be generated back to the controller.

    Args:
        name: (str) The UNIX-style relative path of the file.
        contents: (str) The complete file contents.
    """
    _write_msg(type='gen_file', filename=name, contents=contents)


def query_user(prompt, default=None):
    """Query the user for data.

    Args:
        prompt: (str) Prompt to display to the user.
        default: (str or None) Default value to use if the user doesn't input
            anything.

    Returns:
        (str) Value returned by the user.
    """
    kwargs = {}
    kwargs['prompt'] = prompt
    if default is not None:
        kwargs['default'] = default
    _write_msg(type='query_user', **kwargs)
    return json.loads(sys.stdin.readline())['result']
