# -*- coding: utf-8 -*-

# Copyright (c) 2007 - 2015 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the Ruby debugger interface for the debug server.
"""

from __future__ import unicode_literals

import os

from PyQt5.QtCore import QObject, QTextCodec, QProcess, QProcessEnvironment, \
    QTimer

from E5Gui.E5Application import e5App
from E5Gui import E5MessageBox

from . import DebugProtocol
from . import DebugClientCapabilities

import Preferences
import Utilities

from eric6config import getConfig


ClientDefaultCapabilities = \
    DebugClientCapabilities.HasDebugger | \
    DebugClientCapabilities.HasShell | \
    DebugClientCapabilities.HasInterpreter | \
    DebugClientCapabilities.HasCompleter
    
ClientTypeAssociations = [".rb"]


def getRegistryData():
    """

    Module function to get characterising data for the debugger interface.
    
    @return list of the following data. Client type (string), client
        capabilities (integer), client type association (list of strings)
    """
    if Preferences.getDebugger("RubyInterpreter"):
        return ["Ruby", ClientDefaultCapabilities, ClientTypeAssociations]
    el
se:
        return ["", 0, []]


class DebuggerInterfaceRuby(QObject):
    """
    Class implementing the Ruby debugger interface for the debug server.
    """
    def __init__(self, debugServer, passive):
        """
        Constructor
        
        @param debugServer reference to the debug server (DebugServer)
        @param passive flag indicating passive connection mode (boolean)
        """
        super(DebuggerInterfaceRuby, self).__init__()
        
        self.__isNetworked = True
        self.__autoContinue = not passive
        
        self.debugServer = debugServer
        self.passive = passive
        self.process = None
        
        self.qsock = None
        self.queue = []
        
        # set default values for capabilities of clients
        self.clientCapabilities = ClientDefaultCapabilities
        
        # set translation function
        self.translate = self.__identityTranslation
        
        self.codec = QTextCodec.codecForName(
            str(Preferences.getSystem("StringEncoding")))
        
        if passive:
            # set translation function
            if Preferences.getDebugger("PathTranslation"):
                self.translateRemote = \
                    Preferences.getDebugger("PathTranslationRemote")
                self.translateLocal = \
                    Preferences.getDebugger("PathTranslationLocal")
                self.translate = self.__remoteTranslation
            else:
                self.translate = self.__identityTranslation

    def __identityTranslation(self, fn, remote2local=True):
        """
        Private method to perform the identity path translation.
        
        @param fn filename to be translated (string)
        @param remote2local flag indicating the direction of translation
            (False = local to remote, True = remote to local [default])
        @return translated filename (string)
        """
        return fn
        
    def __remoteTranslation(self, fn, remote2local=True):
        """
        Private method to perform the path translation.
        
        @param fn filename to be translated (string)
        @param remote2local flag indicating the direction of translation
            (False = local to remote, True = remote to local [default])
        @return translated filename (string)
        """
        if remote2local:
            return fn.replace(self.translateRemote, self.translateLocal)
        else:
            return fn.replace(self.translateLocal, self.translateRemote)
        
    def __startProcess(self, program, arguments, environment=None):
        """
        Private method to start the debugger client process.
        
        @param program name of the executable to start (string)
        @param arguments arguments to be passed to the program (list of string)
        @param environment dictionary of environment settings to pass
            (dict of string)
        @return the process object (QProcess) or None
        """
        proc = QProcess()
        if environment is not None:
            env = QProcessEnvironment()
            for key, value in list(environment.items()):
                env.insert(key, value)
            proc.setProcessEnvironment(env)
        args = []
        for arg in arguments:
            args.append(arg)
        proc.start(program, args)
        if not proc.waitForStarted(10000):
            proc = None
        
        return proc
        
    def startRemote(self, port, runInConsole):
        """
        Public method to start a remote Ruby interpreter.
        
        @param port portnumber the debug server is listening on (integer)
        @param runInConsole flag indicating to start the debugger in a
            console window (boolean)
        @return client process object (QProcess), a flag to indicate
            a network connection (boolean) and the name of the interpreter
            in case of a local execution (string)
        """
        interpreter = Preferences.getDebugger("RubyInterpreter")
        if interpreter == "":
            E5MessageBox.critical(
                None,
                self.tr("Start Debugger"),
                self.tr("""<p>No Ruby interpreter configured.</p>"""))
            return None, False, ""
        
        debugClient = os.path.join(
            getConfig('ericDir'), "DebugClients", "Ruby", "DebugClient.rb")
        
        redirect = str(Preferences.getDebugger("RubyRedirect"))
        
        if Preferences.getDebugger("RemoteDbgEnabled"):
            ipaddr = self.debugServer.getHostAddress(False)[0]
            rexec = Preferences.getDebugger("RemoteExecution")
            rhost = Preferences.getDebugger("RemoteHost")
            if rhost == "":
                rhost = "localhost"
            if rexec:
                args = Utilities.parseOptionString(rexec) + \
                    [rhost, interpreter, os.path.abspath(debugClient),
                        str(port), redirect, ipaddr]
                args[0] = Utilities.getExecutablePath(args[0])
                process = self.__startProcess(args[0], args[1:])
                if process is None:
                    E5MessageBox.critical(
                        None,
                        self.tr("Start Debugger"),
                        self.tr(
                            """<p>The debugger backend could not be"""
                            """ started.</p>"""))
                
                # set translation function
                if Preferences.getDebugger("PathTranslation"):
                    self.translateRemote = \
                        Preferences.getDebugger("PathTranslationRemote")
                    self.translateLocal = \
                        Preferences.getDebugger("PathTranslationLocal")
                    self.translate = self.__remoteTranslation
                else:
                    self.translate = self.__identityTranslation
                return process, self.__isNetworked, ""
        
        # set translation function
        self.translate = self.__identityTranslation
        
        # setup the environment for the debugger
        if Preferences.getDebugger("DebugEnvironmentReplace"):
            clientEnv = {}
        else:
            clientEnv = os.environ.copy()
        envlist = Utilities.parseEnvironmentString(
            Preferences.getDebugger("DebugEnvironment"))
        for el in envlist:
            try:
                key, value = el.split('=', 1)
                if value.startswith('"') or value.startswith("'"):
                    value = value[1:-1]
                clientEnv[str(key)] = str(value)
            except ValueError:
                pass
        
        ipaddr = self.debugServer.getHostAddress(True)
        if runInConsole or Preferences.getDebugger("ConsoleDbgEnabled"):
            ccmd = Preferences.getDebugger("ConsoleDbgCommand")
            if ccmd:
                args = Utilities.parseOptionString(ccmd) + \
                    [interpreter, o