"""
Main Zencoder module
"""

import os
import httplib2
from urllib import urlencode

# Note: I've seen this pattern for dealing with json in different versions of
# python in a lot of modules -- if there's a better way, I'd love to use it.
try:
    # python 2.6 and greater
    import json
except ImportError:
    try:
        # python 2.5
        import simplejson
        json = simplejson
    except ImportError:
        # if we're in django or Google AppEngine land
        # use this as a last resort
        from django.utils import simplejson
        json = simplejson

class ZencoderError(Exception):
    pass

class ZencoderResponseError(Exception):
    def __init__(self, http_response, content):
        self.http_response = http_response
        self.content = content

class HTTPBackend(object):
    """
    Abstracts out an HTTP backend, but defaults to httplib2

    @FIXME: Build in support for supplying arbitrary backends
    """
    def __init__(self, base_url, api_key, as_xml=False, resource_name=None, timeout=None, test=False):
        """
        Creates an HTTPBackend object, which abstracts out some of the
        library specific HTTP stuff.
        """
        self.base_url = base_url
        if resource_name:
            self.base_url = self.base_url + resource_name

        self.http = httplib2.Http(timeout=timeout)
        self.as_xml = as_xml
        self.api_key = api_key
        self.test = test

        if self.as_xml:
            self.headers = {'Content-Type': 'application/xml',
                            'Accepts': 'application/xml'}
        else:
            self.headers = {'Content-Type': 'application/json',
                            'Accepts': 'application/json'}

    def encode(self, data):
        """
        Encodes data as either JSON or XML, so that it can be passed onto
        the Zencoder API
        """
        if not self.as_xml:
            return json.dumps(data)
        else:
            raise NotImplementedError('Encoding as XML is not supported.')

    def decode(self, raw_body):
        """
        Returns the raw_body as json (the default) or XML
        """
        if not self.as_xml:
            # only parse json when it exists, else just return None
            if not raw_body or raw_body == ' ':
                return None
            else:
                return json.loads(raw_body)
        else:
            raise NotImplementedError('Decoding as XML is not supported.')

    def delete(self, url, params=None):
        """
        Executes an HTTP DELETE request for the given URL

        params should be a urllib.urlencoded string
        """
        if params:
            url = '?'.join([url, params])

        response, content = self.http.request(url, method="DELETE",
                                              headers=self.headers)
        return self.process(response, content)

    def get(self, url, data=None):
        """
        Executes an HTTP GET request for the given URL

        data should be a dictionary of url parameters
        """
        if data:
            params = urlencode(data)
            url = '?'.join([url, params])

        response, content = self.http.request(url, method="GET",
                                              headers=self.headers)
        return self.process(response, content)

    def post(self, url, body=None):
        """
        Executes an HTTP POST request for the given URL
        """
        response, content = self.http.request(url, method="POST",
                                              body=body,
                                              headers=self.headers)

        return self.process(response, content)

    def put(self, url, body=None):
        """
        Executes an HTTP PUT request for the given URL
        """
        _headers = self.headers.copy()
        if body:
            content_length = str(len(body))
        else:
            content_length = 0
        _headers['Content-Length'] = str(content_length)
        response, content = self.http.request(url, method="PUT",
                                              body=body,
                                              headers=_headers)

        return self.process(response, content)

    def process(self, http_response, content):
        """
        Returns HTTP backend agnostic Response data
        """

        try:
            code = http_response.status
            body = self.decode(content)
            response = Response(code, body, content, http_response)

            return response

        except ValueError:
            raise ZencoderResponseError(http_response, content)

class Zencoder(object):
    """ This is the entry point to the Zencoder API """
    def __init__(self, api_key=None, api_version=None, as_xml=False, timeout=None, test=False):
        """
        Initializes Zencoder. You must have a valid API_KEY.

        You can pass in the api_key as an argument, or set
        'ZENCODER_API_KEY' as an environment variable, and it will use
        that, if api_key is unspecified.

        Set api_version='edge' to get the Zencoder development API. (defaults to 'v2')
        Set as_xml=True to get back xml data instead of the default json.
        """
        if not api_version:
            api_version = 'v2'

        self.base_url = 'https://app.zencoder.com/api/'
        if not api_version == 'edge':
            self.base_url = self.base_url + '%s/' % api_version

        if not api_key:
            try:
                self.api_key = os.environ['ZENCODER_API_KEY']
            except KeyError:
                raise ZencoderError('ZENCODER_API_KEY not set')
        else:
            self.api_key = api_key

        self.test = test
        self.as_xml = as_xml
        self.job = Job(self.base_url, self.api_key, self.as_xml, timeout=timeout, test=self.test)
        self.account = Account(self.base_url, self.api_key, self.as_xml, timeout=timeout)
        self.output = Output(self.base_url, self.api_key, self.as_xml, timeout=timeout)

class Response(object):
    """
    The Response object stores the details of an API request in an XML/JSON
    agnostic way.
    """
    def __init__(self, code, body, raw_body, raw_response):
        self.code = code
        self.body = body
        self.raw_body = raw_body
        self.raw_response = raw_response

class Account(HTTPBackend):
    """ Account object """
    def __init__(self, base_url, api_key=None, as_xml=False, timeout=None):
        """
        Initializes an Account object
        """
        super(Account, self).__init__(base_url, api_key, as_xml, 'account', timeout=timeout)

    def create(self, email, tos=1, options=None):
        """
        Creates an account with Zencoder, no API Key necessary.