#
# Client module for the gDesklets daemon. Use this module to talk to the
# daemon from external programs.
#

from main import DISPLAY, HOME, SOCKET_PATH
from main.remotecommands import *
import utils.xdr as xdr

import os
import sys
import socket
import time

from utils import i18n
import __builtin__
__builtin__._ = i18n.Translator("gdesklets")


class RemoteConnection:


    def __init__(self, path):

        if (HAVE_WIN32):
            self.__sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.__sock.connect(("localhost", 1979))
        else:
            self.__sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
            self.__sock.connect(path)



    def __send(self, command, *args):

        """
        Sends the given command to the given socket and returns the answer.
        """

        xdr.send(self.__sock, command, *args)

        return xdr.recv(self.__sock)



    def open_display(self, path):

        """
        Opens a display file.

        @param path: The (VFS-)path of the display file to open.
        @type  path: str

        @return: The ID of the new display.
        @rtype : str
        """

        return self.__send(COMMAND_OPEN_DISPLAY, path)[0]



    def open_display_with_id(self, path, ident):

        """
        Opens a display file with the specified ID.

        @param path: The (VFS-)path of the display file to open.
        @type  path: str
        @param ident: The ID of the display.
        @type  ident: str
        """

        return self.__send(COMMAND_OPEN_DISPLAY_WITH_ID, path, ident)



    def open_plug(self, path):

        """
        Opens a display file as an embeddable plug.

        @param path: The (VFS-)path of the display file to open.
        @type  path: str

        @return: (id, xid) The ID of the new display and the XID of the plug
                 which is cimpliant with the XEmbed protocol.
        @rtype : str
        """

        ident, xid = self.__send(COMMAND_OPEN_PLUG, path)

        return (ident, long(xid))



    def close_display(self, ident):

        """
        Closes a display with the specified ID.

        @param ident: The ID of the display.
        @type  ident: str
        """

        self.__send(COMMAND_CLOSE_DISPLAY, ident)


    def configure(self):

        """
        Opens the configuration dialog.
        """

        self.__send(COMMAND_CONFIGURE)


    def version(self):

        """
        Shows the gDesklets version.

        @return: Tuple with the name and version of the gDesklets daemon
        @rtype : tuple(str, str)
        """

        return self.__send(COMMAND_VERSION)



    def about(self):

        """
        Shows some additional information about gDesklets.

        @return: gDesklets information
        @rtype : tuple(str, str, str)
        """

        return self.__send(COMMAND_ABOUT)



    def shutdown(self):

        """
        Shuts down the daemon.

        """

        self.__send(COMMAND_SHUTDOWN)



    def displays(self):

        """
        Shows the IDs of running displays.

        @return: The list of the IDs of the currently open displays.
        @rtype : tuple
        """

        return self.__send(COMMAND_DISPLAYS)



    def display_list(self):

        """
        Shows a list of running displays.

        @return: The list of currently open displays.
        @rtype: list
        """

        return self.__send(COMMAND_DISPLAY_LIST)



    def geometry(self, ident):

        """
        Shows the coordinates and the width, height of the display specified by
        the given ID.

        @param ident: The ID of the display.
        @type  ident: str

        @return: Tuple of the x, y, width, height.
        @rtype : tuple(int, int, int, int)
        """

        x, y, w, h = self.__send(COMMAND_GEOMETRY, ident)

        return (int(x), int(y), int(w), int(h))



    def set_remove_command(self, cmd):

        """
        Sets the remove command which will be run whenever the user removes
        a display. This can only be set once in a daemon's lifetime for
        security reasons.

        @param cmd: The command.
        @type  cmd: str
        """

        self.__send(COMMAND_SET_REMOVE_COMMAND, cmd)



def daemon_is_running(display = None):

    """
    Tells if the daemon is running.

    @param display: display on which should be queried for the daemon
    @type  display: str

    @return: True if the daemon is running, False if not.
    @rtype : bool
    """

    if (not display):
        display = DISPLAY

    path = os.path.join(SOCKET_PATH, display)

    if (HAVE_WIN32 or os.path.exists(path)):
        try:
            connection = RemoteConnection(path)
            return (connection != None)
        except:
            pass

    return False



def get_daemon(display = None):

    """ Returns the daemon. If there is no daemon running, start a new one.

    @param display: display on which should be queried for the daemon
    @type  display: str

    @return:
    @rtype :
    """

    if (not display):
        display = DISPLAY

    daemon = None
    path = os.path.join(SOCKET_PATH, display)
    boottime = time.time()

    if (daemon_is_running(display)):
        try:
            connection = RemoteConnection(path)
            daemon = connection
        except:
            os.unlink(path)

    else:
        daemon_path = os.path.join(HOME, "gdesklets-daemon")

        args = " ".join([ a for a in sys.argv if a.startswith("--") ])

        if (HAVE_WIN32):
            os.system("start python \"%s\" %s" % (daemon_path, args))
        else:
            os.system("%s %s &" % (daemon_path, args))

        pos = 0
        delta = 1
        for i in range(500):
            left = pos
            right = 10 - pos
            mybar = " " * left + "###" + " " * right
            sys.stdout.write("\r" + _("Connecting to daemon [%s]") % mybar)
            sys.stdout.flush()
            pos += delta
            if (pos == 0 or pos == 10):
                delta = -delta
            try:
                connection = RemoteConnection(path)
                daemon = connection
                if (daemon):
                    break
            except socket.error:
                pass
            time.sleep(0.1)

        print "\r" + " " * 40 + "\r",

        if (not daemon):
            sys.exit(_("Cannot establish connection to daemon: timeout!\n"
                        "The log file might help you solving the problem."))

    elapsed = int(1000000*(time.time() - boottime))

    if (elapsed < 1000):
        print _("Connected to daemon in %d microseconds.") % (elapsed,)
    else:
        elapsed /= 1000
        print _("Connected to daemon in %d milliseconds.") % (elapsed,)

    return daemon

