#!/usr/bin/env python
# -*- coding: utf-8 -*-
# $Id: Request.py 9621 2016-04-06 13:36:10Z Eric $
#
# Copyright (c) 2013 Nuwa Information Co., Ltd, All Rights Reserved.
#
# Licensed under the Proprietary License,
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at our web site.
#
# See the License for the specific language governing permissions and
# limitations under the License.
#
# $Author: Eric $
# $Date: 2016-04-06 21:36:10 +0800 (Wed, 06 Apr 2016) $
# $Revision: 9621 $

import urllib

from Iuppiter.Encoding import utf8

from Iuno.browser import Config

class Request(object):
    """
    Request to browser. It carries out all functionality of Requests. 
    This is a abstract interface for all browser implementations.
    """

    def __init__(self, url=None, method=None, data=None, params=None, auth=None,
                 cookies=None, timeout=None, config=None, throw=True):
        """
        Constructor.
        
        @param url URL.
        @param method HTTP Method to use.
        @param data Dictionary or bytes of request body data to attach to the 
                    Request.
        @param params Dictionary or byte of querystring data to attach to the 
                      Request.
        @param auth Authentication tuple or object to attach to Request.
        @param cookies Dictionary or string to send with the Request.
        @param timeout Float describing the timeout of the request. NOT 
                       SUPPORTED YET.
        @param config Dictionary of configurations for this request.
        @param throw Throw/raise exception if HTTP response status code is not 
                     200.
        """
        self.url = utf8(url) # To utf8 first.
        self.method = method.lower()
        
        if data and isinstance(data, dict):
            data = urllib.urlencode(data)           
        self.data = data
        
        if params and isinstance(params, dict):
            params = urllib.urlencode(params)
        self.params = params
        
        if auth and isinstance(auth, basestring):
            auth = tuple(auth.split(':'))
        self.auth = auth
        
        if cookies and isinstance(cookies, dict):
            cookies = ';'.join(['%s=%s' % (k, v) for k, v in cookies.items()])
        self.cookies = cookies
        
        self.timeout = timeout
        
        self.configFile = None
        if config:
            if isinstance(config, basestring):
                self.config = Config.load(config)
                self.configFile = config
            else:
                self.config = Config.load()
                self.config.update(config)
        else:
            self.config = Config.load()
            
        self.throw = throw

        self.sent = False
        self.response = None
        
    def __repr__(self):
        return '%s - %s' % (super(Request, self).__repr__(), self.url)

    def _send(self):
        """
        Implementation of send.
        
        @return Response.
        """
        raise NotImplemented()

    def send(self, anyway=False):
        """
        Sends the request. If there was an HTTPError during transmission, 
        self.response.statusCode will contain the HTTPError code.

        Once a request is successfully sent, sent will equal True.
        
        @param anyway If True, request will be sent, even if it has already 
                      been sent.
        @return True if successful, False if not.
        """
        if self.sent and not anyway:
            return None

        self.response = self._send()
        self.sent = True

        return self.sent

class Response(object):
    """
    The core Response object. All Request objects contain a response attribute, 
    which is an instance of this class.
    """
    
    def __init__(self, url='', content='', statusCode=200, lastModified=None,
                 snapshot='', config=None):
        """
        Constructor.
        
        @param url Final URL location of Response.
        @param content Content of the response, in UTF8 string.
        @param statusCode Integer Code of responded HTTP Status.
        @param lastModified Last-Modified time string if exists.
        @param snapshot Web page screen snapshot binary string.
        @param config Dictionary of configurations for this request.
        """
        self.url = url
        self.content = content
        self.statusCode = statusCode
        self.lastModified = lastModified
        self.snapshot = snapshot
        self.config = config
        
        self.encoding = 'utf8'
        
    def __repr__(self):
        return '%s - %s (content: %ld bytes, snapshot: %ld bytes)' % (
            super(Response, self).__repr__(), self.url, 
            len(self.content) if self.content else 0, 
            len(self.snapshot) if self.snapshot else 0)

class NavigateError(Exception):
    """
    Base exception on navigation error.
    """

    def __init__(self, url, status, message=''):
        """
        Constructor.

        @param url URL.
        @param status Error status.
        """
        Exception.__init__(self, message)
        self.url = url
        self.status = status

    def __str__(self):
        return "url: %s, status: %s, message: %s" % (
            self.url, self.status, super(NavigateError, self).__str__())

class AccessDeniedError(NavigateError):
    """
    Access denied, security related error.
    """

class HttpError(NavigateError):
    """
    Http status error.
    """

class InvalidHyperlinkError(NavigateError):
    """
    Invalid hyperline format.
    """

class ConnectionError(NavigateError):
    """
    Unknown host, timeout, fail to connect to server...
    """

class TimeOutError(NavigateError):
    """
    Time out.
    """

class UnknownError(NavigateError):
    """
    Unknown error. (Not classified yet).
    """     

    