#!/usr/bin/env python
# -*- coding: utf-8 -*-

#    Copyright (C) 2008-2010 Ivan Garcia capiscuas@gmail.com
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with this program; if not, write to the Free Software Foundation, Inc.,
#    see <http://www.gnu.org/licenses/>.

from xmlrpclib import Transport,ServerProxy
import base64, httplib, os
import StringIO, gzip, zlib
import logging
import threading, thread
log = logging.getLogger("babiloo.WebService")

from core.modules import APP_TITLE, APP_VERSION

DEFAULT_SDDB_SERVER = "http://sddb.subdownloader.net/xmlrpc/"
DEFAULT_PROXY = 'http://w2.hidemyass.com/'
USER_AGENT = "%s %s"% (APP_TITLE, APP_VERSION)

#"""This class is useful to let the server know who we are, good for statistics,
#    so we can separate the traffic from normal Web visitors"""
#class GtkTransport (Transport):
#        user_agent = "Babiloo " + APP_VERSION


def test_connection(url, timeout=15):
    import socket, urllib2
    defTimeOut=socket.getdefaulttimeout()
    socket.setdefaulttimeout(timeout)
    connectable=True
    try:
        urllib2.urlopen(url)
    except (urllib2.HTTPError, urllib2.URLError, socket.error, socket.sslerror):
        connectable=False
    socket.setdefaulttimeout(defTimeOut)
    return connectable

class TimeoutFunctionException(Exception):
    """Exception to raise on a timeout"""
    pass

class TimeoutFunction:

    def __init__(self, function, timeout=30):
        self.timeout = timeout
        self.function = function

    def handle_timeout(self):
        raise TimeoutFunctionException()

    def __call__(self, *args):
        #old = signal.alarm(signal.SIGALRM, self.handle_timeout)
        #signal.alarm(self.timeout)
        t = threading.Timer(self.timeout, self.handle_timeout)
        try:
            t.start()
            result = self.function(*args)
        finally:
            #signal.signal(signal.SIGALRM, old)
            t.cancel()
            pass
        #signal.alarm(0)
        del t
        return result

"""The XMLRPC can use a Proxy, this class is need for that."""
class ProxiedTransport(Transport):
    """ Used for proxied connections to the XMLRPC server
    """
    def __init__(self):
        #self.log = logging.getLogger("babiloo.OSDBServer.ProxiedTransport")
        self._use_datetime = True # annoying -> AttributeError: Main instance has no attribute '_use_datetime'
    def set_proxy(self, proxy):
        self.proxy = proxy
        #self.log.debug("Proxy set to: %s"% proxy)
    def make_connection(self, host):
        #self.log.debug("Connecting to %s through %s"% (host, self.proxy))
        self.realhost = host
        h = httplib.HTTP(self.proxy)
        return h
    def send_request(self, connection, handler, request_body):
        connection.putrequest("POST", 'http://%s%s' % (self.realhost, handler))
    def send_host(self, connection, host):
        connection.putheader('Host', self.realhost)


class OnlineService(object):
    """
    Contains the class that represents the OSDB and SDDB RPC Servers.
    Encapsules all the XMLRPC methods.

    Consult the OSDB API methods at http://trac.opensubtitles.org/projects/opensubtitles/wiki/XMLRPC
    Consult the SDDB API methods at https://sddb.subdownloader.net/xmlrpc/

    If it fails to connect directly to the XMLRPC server, it will try to do so through a default proxy.
    Default proxy uses a form to set which URL to open. We will try to change this in later stage.
    """
    def __init__(self, server = None, proxy = None ):
        self.log = logging.getLogger("babiloo.xmlrpc.service")
        self.log.debug("Creating Server with server = %s and proxy = %r" %(server, proxy))
        self.timeout = 30
        self.user_agent = USER_AGENT
        self.language = ''

        if server:
                self.server = server
        else:
                self.server = DEFAULT_SDDB_SERVER

        self.proxy = proxy


        self.logged_as = None
        self.xmlrpc_server = None
        self._token = None
        #Let's connect with the server XMLRPC
        #OSConnection.__init__(self)
        try:
            self.create_xmlrpcserver(self.server, self.proxy)
        except Exception, e:
            raise e

    def create_xmlrpcserver(self, server, proxy):
        self.log.debug("Creating XMLRPC server connection...")
        try:
            return self.connect(server, proxy)
        except Exception, e:
            raise e

    def connect(self, server, proxy):
        try:
            self.log.debug("Connecting with parameters (%r, %r)" %(server, proxy))
            connect = TimeoutFunction(self._connect)
            return connect(server, proxy)
        except TimeoutFunctionException, e:
            self.log.error("Connection timed out. Maybe you need a proxy.")
            raise e
        #except Exception, e:
            #import sys
            #self.log.error("Unexpected error: %s", sys.exc_info())

    def _connect(self, server, proxy):
        try:
            if proxy:
                self.log.debug("Trying proxied connection... (%r)"% proxy)
                self.proxied_transport = ProxiedTransport()
                self.proxied_transport.set_proxy(proxy)
                self.xmlrpc_server = ServerProxy(server, transport=self.proxied_transport, allow_none=1)
                #self.ServerInfo()
                self.log.debug("...connected")
                return True

            elif test_connection(server):
                    self.log.debug("Trying direct connection...")
                    self.xmlrpc_server = ServerProxy(server)
                    #self.ServerInfo()
                    self.log.debug("...connected")
                    return True
            else:
                self.log.debug("...failed")
                self.log.error("Unable to connect. Try setting a proxy.")
                return False
        except Exception,e:
                    self.log.debug("Connection to the server failed")
                    raise e

    def is_connected(self):
        """
        This method checks to see whether we are connected to the server.
        It does not return any information about the validity of the
        connection.
        """
        return self._token != None

    def ServerInfo(self):
        ServerInfo = TimeoutFunction(self._ServerInfo)
        try:
            a = ServerInfo()
            return a
        except TimeoutFunctionException:
            self.log.error("ServerInfo timed out")

        except Exception, e:
            #print type(e)     # the exception instance
            #print e.args      # arguments stored in .args
            #print e           # __str__ allows args to printed directly
            self.log.error("ServerInfo error connection.")
            raise e

    """This simple function returns basic server info,
    it could be used for ping or telling server info to client"""   
    def _ServerInfo(self):
        try:
            return self.xmlrpc_server.ServerInfo()
        except TimeoutFunctionException:
            raise

    def login(self, username="", password=""):
        login = TimeoutFunction(self._login)
        try:
            return login(username, password)
        except TimeoutFunctionException:
            self.log.error("login timed out")

    def _login(self, username="", password=""):
        """Login to the Server using username/password,
        empty parameters means an anonymously login
        Returns True if login sucessful, and False if not.
        """
        self.log.debug("----------------")
        self.log.debug("Logging in (username: %r)..."% username)
        info = self.xmlrpc_server.LogIn(username, password, self.language, self.user_agent)
        self.log.debug("Login ended in %s with status: %s"% (info['seconds'], info['status']))
        if info['status'] == "200 OK":
            self.log.debug("Session ID: %s"% info['token'])
            self.log.debug("----------------")
            self._token = info['token']
            return True
        else:
            # force token reset
            self.log.debug("----------------")
            self._token = None
            return False

    def logout(self):
        logout = TimeoutFunction(self._logout)
        try:
            return logout()
        except TimeoutFunctionException:
            self.log.error("logout timed out")

    def _logout(self):
        """Logout from current session(token)
        This functions doesn't return any boolean value, since it can 'fail' for anonymous logins
        """
        self.log.debug("Logging out from session ID: %s"% self._token)
        info = self.xmlrpc_server.LogOut(self._token)
        self.log.debug("Logout ended in %s with status: %s"% (info['seconds'], info['status']))
        # force token reset
        self._token = None

    def CheckSoftwareUpdates(self, app=None):
        CheckSoftwareUpdates = TimeoutFunction(self._CheckSoftwareUpdates)
        try:
            return CheckSoftwareUpdates(app)
        except TimeoutFunctionException:
            self.log.error("CheckSoftwareUpdates timed out")

    def _CheckSoftwareUpdates(self, app=None):
        """Returns latest info on the given application if available
        """
        self.log.debug("----------------")
        self.log.debug("CheckSoftwareUpdates RPC method starting...")
        if not app: app = APP_TITLE.lower()
        info = self.xmlrpc_server.CheckSoftwareUpdates(app)

        # we have something to show
        self.log.debug("Latest Babiloo Version Found: %s"% info['latest_version'])
        return info

    def GetDictionaries(self, version=None):
        GetDictionaries = TimeoutFunction(self._GetDictionaries)
        try:
            return GetDictionaries(version)
        except TimeoutFunctionException:
            self.log.error("GetDictionaries timed out")

    def _GetDictionaries(self, version=None):
        """Returns latest info on the given application if available
        """
        self.log.debug("----------------")
        self.log.debug("GetDictionaries RPC method starting...")
        if not version: version = APP_VERSION
        dictionaries = self.xmlrpc_server.GetDictionaries(version)

        return dictionaries

    def NoOperation(self):
        NoOperation = TimeoutFunction(self._NoOperation)
        try:
            return NoOperation()
        except TimeoutFunctionException:
            self.log.error("NoOperation timed out")

    def _NoOperation(self):
        """This method should be called every 15 minutes after last request to xmlrpc.
        It's used to keep current session alive.
        Returns True if current session token is valid and False if not.
        """
        self.log.debug("----------------")
        self.log.debug("NoOperation RPC method starting...")
        info = self.xmlrpc_server.NoOperation(self._token)
        self.log.debug("NoOperation finished in %s with status %s."% (info['seconds'], info['status']))
        if info['status'] != "200 OK":
            return False
        return True


    def BaseToFile(self, base_data, path):
        """This will decode the base64 data and save it as a file with the given path
        """
        compressedstream = base64.decodestring(base_data)
        gzipper = gzip.GzipFile(fileobj=StringIO.StringIO(compressedstream))
        s=gzipper.read()
        gzipper.close()
        subtitle_file = file(path,'wb')
        subtitle_file.write(s)
        subtitle_file.close()
